diff --git a/Mage.Client/src/main/java/mage/client/deck/generator/DeckGenerator.java b/Mage.Client/src/main/java/mage/client/deck/generator/DeckGenerator.java index 1375095d5d1..c5a4aa15756 100644 --- a/Mage.Client/src/main/java/mage/client/deck/generator/DeckGenerator.java +++ b/Mage.Client/src/main/java/mage/client/deck/generator/DeckGenerator.java @@ -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> basicLands = generateBasicLands(setsToUse); diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/MageBook.java b/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/MageBook.java index ec431e46028..a63f38cbf59 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/MageBook.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/MageBook.java @@ -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 haveNumbers = set + .getSetCardInfo() + .stream() + .map(ExpansionSet.SetCardInfo::getCardNumberAsInt) + .collect(Collectors.toList()); - List cards = set.getSetCardInfo(); - - // first run for numbers list - LinkedList 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 { diff --git a/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java b/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java index ccf83213025..327285a3593 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java @@ -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 extraAbilities) { + private PermanentView createPermanentCard(Game game, UUID controllerId, String code, String cardNumber, int power, int toughness, int damage, boolean tapped, boolean transform, List 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 //*/ diff --git a/Mage.Client/src/main/java/mage/client/plugins/adapters/MageActionCallback.java b/Mage.Client/src/main/java/mage/client/plugins/adapters/MageActionCallback.java index edabb05841f..1268264ef16 100644 --- a/Mage.Client/src/main/java/mage/client/plugins/adapters/MageActionCallback.java +++ b/Mage.Client/src/main/java/mage/client/plugins/adapters/MageActionCallback.java @@ -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(); } diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java index 3ecda635ac3..501ae1fcf2d 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java @@ -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); } diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderModeImage.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderModeImage.java index 631f453327f..63f61898446 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderModeImage.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderModeImage.java @@ -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 { diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderModeMTGO.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderModeMTGO.java index 54426314c42..5c9359a9cf2 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderModeMTGO.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderModeMTGO.java @@ -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 { diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java index 043f501d69f..16a0549acff 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java @@ -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; diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java index f39cc96b467..da544debd8f 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java @@ -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); diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSets.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSets.java index b3bda426310..b7ffabb8c0d 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSets.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSets.java @@ -68,7 +68,7 @@ public class GathererSets implements Iterable { // "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 { 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 { // checks for wrong card settings and support (easy to control what all good) private static final HashMap setsToDownload = new HashMap<>(); - private static final HashMap 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 { 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 { } } - // 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()) { diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSymbols.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSymbols.java index 963c0ac1d2c..6e4dc613b92 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSymbols.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSymbols.java @@ -28,6 +28,7 @@ public class GathererSymbols implements Iterable { 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"}; diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSource.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSource.java index 31688a61c3f..519fa483609 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSource.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSource.java @@ -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 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); diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java index 04de9873ae1..84590b0212e 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java @@ -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 xmageSetsToScryfall = ImmutableMap.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 directDownloadLinks = new HashMap() { { - // 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 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 getDirectDownloadLinks() { return directDownloadLinks; } diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java index a90b82d63d9..313543dbafa 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java @@ -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()) { diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallSymbolsSource.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallSymbolsSource.java index b4728feabfa..5e22e80d35e 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallSymbolsSource.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallSymbolsSource.java @@ -42,6 +42,7 @@ public class ScryfallSymbolsSource implements Iterable { // 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"}; diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/images/DownloadPicturesService.java b/Mage.Client/src/main/java/org/mage/plugins/card/images/DownloadPicturesService.java index 8e3e97ae7d0..18f6c63d02b 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/images/DownloadPicturesService.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/images/DownloadPicturesService.java @@ -314,6 +314,7 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements private void reloadCardsToDownload(String selectedItem) { // find selected sets selectedSets.clear(); + boolean onlyTokens = false; List formatSets; List 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."); diff --git a/Mage.Client/src/main/resources/card-pictures-tok.txt b/Mage.Client/src/main/resources/card-pictures-tok.txt index 4ee3e0b4d6f..ba20a0a9b3c 100644 --- a/Mage.Client/src/main/resources/card-pictures-tok.txt +++ b/Mage.Client/src/main/resources/card-pictures-tok.txt @@ -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| \ No newline at end of file +|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| \ No newline at end of file diff --git a/Mage.Client/src/main/resources/ormliteLocalLog.properties b/Mage.Client/src/main/resources/ormliteLocalLog.properties deleted file mode 100644 index a5b91026191..00000000000 --- a/Mage.Client/src/main/resources/ormliteLocalLog.properties +++ /dev/null @@ -1,2 +0,0 @@ -#workaround to remove un-wanted ormlite logs, see https://github.com/magefree/mage/issues/8373 -LogBackendType.*=ERROR \ No newline at end of file diff --git a/Mage.Common/src/main/java/mage/view/CardView.java b/Mage.Common/src/main/java/mage/view/CardView.java index 8a68422ea67..7e4ea447175 100644 --- a/Mage.Common/src/main/java/mage/view/CardView.java +++ b/Mage.Common/src/main/java/mage/view/CardView.java @@ -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("Chosen mode: " + mode.getEffects().getText(mode) + ""); } } + + // 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("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 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; } diff --git a/Mage.Common/src/main/java/mage/view/PermanentView.java b/Mage.Common/src/main/java/mage/view/PermanentView.java index 28bd992fb5d..b6f92f95c9f 100644 --- a/Mage.Common/src/main/java/mage/view/PermanentView.java +++ b/Mage.Common/src/main/java/mage/view/PermanentView.java @@ -26,7 +26,7 @@ public class PermanentView extends CardView { private final boolean summoningSickness; private final int damage; private List 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(); } } } diff --git a/Mage.Common/src/main/java/mage/view/StackAbilityView.java b/Mage.Common/src/main/java/mage/view/StackAbilityView.java index cbf08403122..b24dbc24913 100644 --- a/Mage.Common/src/main/java/mage/view/StackAbilityView.java +++ b/Mage.Common/src/main/java/mage/view/StackAbilityView.java @@ -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("Targeted ability related to this card: " + game.getCard(stackObjectTarget.getSourceId()).getIdName()); + } + } + } + + } } public CardView getSourceCard() { diff --git a/Mage.Server.Console/src/main/resources/ormliteLocalLog.properties b/Mage.Server.Console/src/main/resources/ormliteLocalLog.properties deleted file mode 100644 index a5b91026191..00000000000 --- a/Mage.Server.Console/src/main/resources/ormliteLocalLog.properties +++ /dev/null @@ -1,2 +0,0 @@ -#workaround to remove un-wanted ormlite logs, see https://github.com/magefree/mage/issues/8373 -LogBackendType.*=ERROR \ No newline at end of file diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/AusHighlander.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/AusHighlander.java index 99355992206..ad029d22fe6 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/AusHighlander.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/AusHighlander.java @@ -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); diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/CanadianHighlander.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/CanadianHighlander.java index d5feec40142..77cbffa2a49 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/CanadianHighlander.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/CanadianHighlander.java @@ -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); diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Commander.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Commander.java index 9dccf0fde5f..cdf129c452f 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Commander.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Commander.java @@ -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()); } diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/DuelCommander.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/DuelCommander.java index 629758796b8..1461e02db88 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/DuelCommander.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/DuelCommander.java @@ -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"); diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/EuropeanHighlander.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/EuropeanHighlander.java index fe0fd97ec49..85e04bb2c50 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/EuropeanHighlander.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/EuropeanHighlander.java @@ -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"); diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Historic.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Historic.java index 19524ddeea5..26a3595c5c6 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Historic.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Historic.java @@ -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"); diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Legacy.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Legacy.java index 259d8797155..195cfe17288 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Legacy.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Legacy.java @@ -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"); diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Pauper.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Pauper.java index af42951c0b6..0b44ef683e8 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Pauper.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Pauper.java @@ -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"); } diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Standard.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Standard.java index 9ab606410b4..af4507d2a52 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Standard.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Standard.java @@ -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"); } diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Vintage.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Vintage.java index a01a05ced37..3a7f2662caf 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Vintage.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Vintage.java @@ -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"); diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java index d7deb819b41..172485b99ff 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java @@ -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 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 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 diff --git a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java index 352147acc40..9b041f95b19 100644 --- a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java @@ -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 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); } diff --git a/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/MonoBlueCube.java b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/MonoBlueCube.java index b96d00e061e..21e2eca373c 100644 --- a/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/MonoBlueCube.java +++ b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/MonoBlueCube.java @@ -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", "")); diff --git a/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/VintageCubeFebruary2022.java b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/VintageCubeFebruary2022.java new file mode 100644 index 00000000000..5c7c6ddf1d4 --- /dev/null +++ b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/VintageCubeFebruary2022.java @@ -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", "")); + } +} + diff --git a/Mage.Server/config/config.xml b/Mage.Server/config/config.xml index 98e89221bc2..27a39a6b6bf 100644 --- a/Mage.Server/config/config.xml +++ b/Mage.Server/config/config.xml @@ -153,6 +153,7 @@ + diff --git a/Mage.Server/pom.xml b/Mage.Server/pom.xml index f951213ed28..fc3a0e63f1f 100644 --- a/Mage.Server/pom.xml +++ b/Mage.Server/pom.xml @@ -231,12 +231,12 @@ com.google.oauth-client google-oauth-client-java6 - 1.31.0 + 1.33.0 com.google.oauth-client google-oauth-client-jetty - 1.31.2 + 1.33.0 javax.mail diff --git a/Mage.Server/src/main/resources/ormliteLocalLog.properties b/Mage.Server/src/main/resources/ormliteLocalLog.properties deleted file mode 100644 index a5b91026191..00000000000 --- a/Mage.Server/src/main/resources/ormliteLocalLog.properties +++ /dev/null @@ -1,2 +0,0 @@ -#workaround to remove un-wanted ormlite logs, see https://github.com/magefree/mage/issues/8373 -LogBackendType.*=ERROR \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/a/AbandonReason.java b/Mage.Sets/src/mage/cards/a/AbandonReason.java index a34140c11b6..63e6f1bd479 100644 --- a/Mage.Sets/src/mage/cards/a/AbandonReason.java +++ b/Mage.Sets/src/mage/cards/a/AbandonReason.java @@ -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"); diff --git a/Mage.Sets/src/mage/cards/a/AbbotOfKeralKeep.java b/Mage.Sets/src/mage/cards/a/AbbotOfKeralKeep.java index addaf77b85f..356d63c2383 100644 --- a/Mage.Sets/src/mage/cards/a/AbbotOfKeralKeep.java +++ b/Mage.Sets/src/mage/cards/a/AbbotOfKeralKeep.java @@ -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() + " "; - 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)); - } -} diff --git a/Mage.Sets/src/mage/cards/a/AberrantResearcher.java b/Mage.Sets/src/mage/cards/a/AberrantResearcher.java index 04fb9353392..154fbf79013 100644 --- a/Mage.Sets/src/mage/cards/a/AberrantResearcher.java +++ b/Mage.Sets/src/mage/cards/a/AberrantResearcher.java @@ -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; } diff --git a/Mage.Sets/src/mage/cards/a/AbolisherOfBloodlines.java b/Mage.Sets/src/mage/cards/a/AbolisherOfBloodlines.java index 2300301607c..aa7c4ab7b14 100644 --- a/Mage.Sets/src/mage/cards/a/AbolisherOfBloodlines.java +++ b/Mage.Sets/src/mage/cards/a/AbolisherOfBloodlines.java @@ -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 + '.'; - } -} diff --git a/Mage.Sets/src/mage/cards/a/AbzanAdvantage.java b/Mage.Sets/src/mage/cards/a/AbzanAdvantage.java index 24760aff1c0..9310d361a81 100644 --- a/Mage.Sets/src/mage/cards/a/AbzanAdvantage.java +++ b/Mage.Sets/src/mage/cards/a/AbzanAdvantage.java @@ -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()); } diff --git a/Mage.Sets/src/mage/cards/a/AbzanBattlePriest.java b/Mage.Sets/src/mage/cards/a/AbzanBattlePriest.java index 0506abacc75..a8825077a0f 100644 --- a/Mage.Sets/src/mage/cards/a/AbzanBattlePriest.java +++ b/Mage.Sets/src/mage/cards/a/AbzanBattlePriest.java @@ -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" ) )); } diff --git a/Mage.Sets/src/mage/cards/a/AbzanFalconer.java b/Mage.Sets/src/mage/cards/a/AbzanFalconer.java index f8fe886748c..10b01542ecc 100644 --- a/Mage.Sets/src/mage/cards/a/AbzanFalconer.java +++ b/Mage.Sets/src/mage/cards/a/AbzanFalconer.java @@ -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) { diff --git a/Mage.Sets/src/mage/cards/a/AccessDenied.java b/Mage.Sets/src/mage/cards/a/AccessDenied.java new file mode 100644 index 00000000000..0776f2cd386 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AccessDenied.java @@ -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; + } +} diff --git a/Mage.Sets/src/mage/cards/a/AccursedWitch.java b/Mage.Sets/src/mage/cards/a/AccursedWitch.java index d157b752a25..bf88d81b5e9 100644 --- a/Mage.Sets/src/mage/cards/a/AccursedWitch.java +++ b/Mage.Sets/src/mage/cards/a/AccursedWitch.java @@ -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. diff --git a/Mage.Sets/src/mage/cards/a/AcidicSliver.java b/Mage.Sets/src/mage/cards/a/AcidicSliver.java index 12826a9efd1..b808edb913f 100644 --- a/Mage.Sets/src/mage/cards/a/AcidicSliver.java +++ b/Mage.Sets/src/mage/cards/a/AcidicSliver.java @@ -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.\""))); } diff --git a/Mage.Sets/src/mage/cards/a/AcklayOfTheArena.java b/Mage.Sets/src/mage/cards/a/AcklayOfTheArena.java index 9ed0ae644af..1cab13f48a5 100644 --- a/Mage.Sets/src/mage/cards/a/AcklayOfTheArena.java +++ b/Mage.Sets/src/mage/cards/a/AcklayOfTheArena.java @@ -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); diff --git a/Mage.Sets/src/mage/cards/a/AcquisitionOctopus.java b/Mage.Sets/src/mage/cards/a/AcquisitionOctopus.java new file mode 100644 index 00000000000..1d55d604dd8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AcquisitionOctopus.java @@ -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."; + } +} diff --git a/Mage.Sets/src/mage/cards/a/AcquisitionsExpert.java b/Mage.Sets/src/mage/cards/a/AcquisitionsExpert.java index bc1faa331d9..85f7fe6757b 100644 --- a/Mage.Sets/src/mage/cards/a/AcquisitionsExpert.java +++ b/Mage.Sets/src/mage/cards/a/AcquisitionsExpert.java @@ -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()) diff --git a/Mage.Sets/src/mage/cards/a/AcrobaticManeuver.java b/Mage.Sets/src/mage/cards/a/AcrobaticManeuver.java index 15313ce5fd1..c505455aa14 100644 --- a/Mage.Sets/src/mage/cards/a/AcrobaticManeuver.java +++ b/Mage.Sets/src/mage/cards/a/AcrobaticManeuver.java @@ -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("
")); } private AcrobaticManeuver(final AcrobaticManeuver card) { diff --git a/Mage.Sets/src/mage/cards/a/ActOfAggression.java b/Mage.Sets/src/mage/cards/a/ActOfAggression.java index e8b4a3be97a..468e53e5f16 100644 --- a/Mage.Sets/src/mage/cards/a/ActOfAggression.java +++ b/Mage.Sets/src/mage/cards/a/ActOfAggression.java @@ -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.")); diff --git a/Mage.Sets/src/mage/cards/a/AdarkarValkyrie.java b/Mage.Sets/src/mage/cards/a/AdarkarValkyrie.java index a8ac7a5a5c5..beded6f827f 100644 --- a/Mage.Sets/src/mage/cards/a/AdarkarValkyrie.java +++ b/Mage.Sets/src/mage/cards/a/AdarkarValkyrie.java @@ -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; diff --git a/Mage.Sets/src/mage/cards/a/AegisAngel.java b/Mage.Sets/src/mage/cards/a/AegisAngel.java index 7a7d812249f..7c5e4a6721b 100644 --- a/Mage.Sets/src/mage/cards/a/AegisAngel.java +++ b/Mage.Sets/src/mage/cards/a/AegisAngel.java @@ -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); } - } diff --git a/Mage.Sets/src/mage/cards/a/AerialCaravan.java b/Mage.Sets/src/mage/cards/a/AerialCaravan.java index 8867a11131f..9a31d61ccee 100644 --- a/Mage.Sets/src/mage/cards/a/AerialCaravan.java +++ b/Mage.Sets/src/mage/cards/a/AerialCaravan.java @@ -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. (Reveal the card as you exile it.)"), + 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() + " "; - 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; - } -} diff --git a/Mage.Sets/src/mage/cards/a/AerialModification.java b/Mage.Sets/src/mage/cards/a/AerialModification.java index f31af209783..83aacbb8bf2 100644 --- a/Mage.Sets/src/mage/cards/a/AerialModification.java +++ b/Mage.Sets/src/mage/cards/a/AerialModification.java @@ -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(), diff --git a/Mage.Sets/src/mage/cards/a/AerialSurveyor.java b/Mage.Sets/src/mage/cards/a/AerialSurveyor.java new file mode 100644 index 00000000000..9d733d7323e --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AerialSurveyor.java @@ -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("
")); + } + + @Override + public AerialSurveyorHint copy() { + return this; + } +} diff --git a/Mage.Sets/src/mage/cards/a/AerialVolley.java b/Mage.Sets/src/mage/cards/a/AerialVolley.java index 857b90350ab..2f44c92fbef 100644 --- a/Mage.Sets/src/mage/cards/a/AerialVolley.java +++ b/Mage.Sets/src/mage/cards/a/AerialVolley.java @@ -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) { diff --git a/Mage.Sets/src/mage/cards/a/AetherChaser.java b/Mage.Sets/src/mage/cards/a/AetherChaser.java index b6fa2065f94..7e13cf0556a 100644 --- a/Mage.Sets/src/mage/cards/a/AetherChaser.java +++ b/Mage.Sets/src/mage/cards/a/AetherChaser.java @@ -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) { diff --git a/Mage.Sets/src/mage/cards/a/AetherHerder.java b/Mage.Sets/src/mage/cards/a/AetherHerder.java index dfb7b51e861..63653549124 100644 --- a/Mage.Sets/src/mage/cards/a/AetherHerder.java +++ b/Mage.Sets/src/mage/cards/a/AetherHerder.java @@ -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) { diff --git a/Mage.Sets/src/mage/cards/a/AetherInspector.java b/Mage.Sets/src/mage/cards/a/AetherInspector.java index 6ba31ee9731..6ea57c07011 100644 --- a/Mage.Sets/src/mage/cards/a/AetherInspector.java +++ b/Mage.Sets/src/mage/cards/a/AetherInspector.java @@ -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) { diff --git a/Mage.Sets/src/mage/cards/a/AetherRift.java b/Mage.Sets/src/mage/cards/a/AetherRift.java index a0a433cf071..3f829658d69 100644 --- a/Mage.Sets/src/mage/cards/a/AetherRift.java +++ b/Mage.Sets/src/mage/cards/a/AetherRift.java @@ -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); diff --git a/Mage.Sets/src/mage/cards/a/AetherSwooper.java b/Mage.Sets/src/mage/cards/a/AetherSwooper.java index ad7cf1aab47..5aa40c707dd 100644 --- a/Mage.Sets/src/mage/cards/a/AetherSwooper.java +++ b/Mage.Sets/src/mage/cards/a/AetherSwooper.java @@ -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) { diff --git a/Mage.Sets/src/mage/cards/a/AetherfluxReservoir.java b/Mage.Sets/src/mage/cards/a/AetherfluxReservoir.java index 4dc5ac6b8fd..99f02213fd2 100644 --- a/Mage.Sets/src/mage/cards/a/AetherfluxReservoir.java +++ b/Mage.Sets/src/mage/cards/a/AetherfluxReservoir.java @@ -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"; } - } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/a/AetherstreamLeopard.java b/Mage.Sets/src/mage/cards/a/AetherstreamLeopard.java index e10a8848419..7c63853017f 100644 --- a/Mage.Sets/src/mage/cards/a/AetherstreamLeopard.java +++ b/Mage.Sets/src/mage/cards/a/AetherstreamLeopard.java @@ -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) { diff --git a/Mage.Sets/src/mage/cards/a/Aethertow.java b/Mage.Sets/src/mage/cards/a/Aethertow.java index f126ce78317..f9af151c47c 100644 --- a/Mage.Sets/src/mage/cards/a/Aethertow.java +++ b/Mage.Sets/src/mage/cards/a/Aethertow.java @@ -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) { diff --git a/Mage.Sets/src/mage/cards/a/AetherwindBasker.java b/Mage.Sets/src/mage/cards/a/AetherwindBasker.java index 3ca50e87661..1004f2de4cc 100644 --- a/Mage.Sets/src/mage/cards/a/AetherwindBasker.java +++ b/Mage.Sets/src/mage/cards/a/AetherwindBasker.java @@ -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))); diff --git a/Mage.Sets/src/mage/cards/a/AffectionateIndrik.java b/Mage.Sets/src/mage/cards/a/AffectionateIndrik.java index ef552693592..3e469e6ebb1 100644 --- a/Mage.Sets/src/mage/cards/a/AffectionateIndrik.java +++ b/Mage.Sets/src/mage/cards/a/AffectionateIndrik.java @@ -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. " + + "(Each deals damage equal to its power to the other.)"), true ); ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); diff --git a/Mage.Sets/src/mage/cards/a/AfflictedDeserter.java b/Mage.Sets/src/mage/cards/a/AfflictedDeserter.java index d340c1ace39..b5dc48b063c 100644 --- a/Mage.Sets/src/mage/cards/a/AfflictedDeserter.java +++ b/Mage.Sets/src/mage/cards/a/AfflictedDeserter.java @@ -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); diff --git a/Mage.Sets/src/mage/cards/a/AgitatorAnt.java b/Mage.Sets/src/mage/cards/a/AgitatorAnt.java index cda362b3783..71d914889e0 100644 --- a/Mage.Sets/src/mage/cards/a/AgitatorAnt.java +++ b/Mage.Sets/src/mage/cards/a/AgitatorAnt.java @@ -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; } diff --git a/Mage.Sets/src/mage/cards/a/AgonizingDemise.java b/Mage.Sets/src/mage/cards/a/AgonizingDemise.java index 105db4d2f76..925027eafde 100644 --- a/Mage.Sets/src/mage/cards/a/AgonizingDemise.java +++ b/Mage.Sets/src/mage/cards/a/AgonizingDemise.java @@ -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( diff --git a/Mage.Sets/src/mage/cards/a/Agoraphobia.java b/Mage.Sets/src/mage/cards/a/Agoraphobia.java index a06d2725efc..fee2c2e1fb5 100644 --- a/Mage.Sets/src/mage/cards/a/Agoraphobia.java +++ b/Mage.Sets/src/mage/cards/a/Agoraphobia.java @@ -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); diff --git a/Mage.Sets/src/mage/cards/a/AgrusKosWojekVeteran.java b/Mage.Sets/src/mage/cards/a/AgrusKosWojekVeteran.java index e3d081a6a35..ee1a142158e 100644 --- a/Mage.Sets/src/mage/cards/a/AgrusKosWojekVeteran.java +++ b/Mage.Sets/src/mage/cards/a/AgrusKosWojekVeteran.java @@ -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) { diff --git a/Mage.Sets/src/mage/cards/a/AhnCropChampion.java b/Mage.Sets/src/mage/cards/a/AhnCropChampion.java index c5572210f04..db5d5c4e7a8 100644 --- a/Mage.Sets/src/mage/cards/a/AhnCropChampion.java +++ b/Mage.Sets/src/mage/cards/a/AhnCropChampion.java @@ -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) { diff --git a/Mage.Sets/src/mage/cards/a/AidFromTheCowl.java b/Mage.Sets/src/mage/cards/a/AidFromTheCowl.java index adfcd640ca9..5794ef89141 100644 --- a/Mage.Sets/src/mage/cards/a/AidFromTheCowl.java +++ b/Mage.Sets/src/mage/cards/a/AidFromTheCowl.java @@ -25,7 +25,7 @@ import mage.watchers.common.RevoltWatcher; public final class AidFromTheCowl extends CardImpl { private static final String ruleText = "Revolt — 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}"); diff --git a/Mage.Sets/src/mage/cards/a/AimForTheHead.java b/Mage.Sets/src/mage/cards/a/AimForTheHead.java new file mode 100644 index 00000000000..572fa2f7513 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AimForTheHead.java @@ -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); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AinokBondKin.java b/Mage.Sets/src/mage/cards/a/AinokBondKin.java index bcfd5152e09..d90bd479787 100644 --- a/Mage.Sets/src/mage/cards/a/AinokBondKin.java +++ b/Mage.Sets/src/mage/cards/a/AinokBondKin.java @@ -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) { diff --git a/Mage.Sets/src/mage/cards/a/AirdropAeronauts.java b/Mage.Sets/src/mage/cards/a/AirdropAeronauts.java index a60bbe3aef9..d62249d4086 100644 --- a/Mage.Sets/src/mage/cards/a/AirdropAeronauts.java +++ b/Mage.Sets/src/mage/cards/a/AirdropAeronauts.java @@ -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()); // Revolt — 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, - "Revolt — 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()); diff --git a/Mage.Sets/src/mage/cards/a/AjaniAdversaryOfTyrants.java b/Mage.Sets/src/mage/cards/a/AjaniAdversaryOfTyrants.java index c6efc37d169..fcdc8d17509 100644 --- a/Mage.Sets/src/mage/cards/a/AjaniAdversaryOfTyrants.java +++ b/Mage.Sets/src/mage/cards/a/AjaniAdversaryOfTyrants.java @@ -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. diff --git a/Mage.Sets/src/mage/cards/a/AjaniCallerOfThePride.java b/Mage.Sets/src/mage/cards/a/AjaniCallerOfThePride.java index 22cf714c4bf..67d51825f00 100644 --- a/Mage.Sets/src/mage/cards/a/AjaniCallerOfThePride.java +++ b/Mage.Sets/src/mage/cards/a/AjaniCallerOfThePride.java @@ -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"); diff --git a/Mage.Sets/src/mage/cards/a/AjaniGoldmane.java b/Mage.Sets/src/mage/cards/a/AjaniGoldmane.java index 6efe3fc8d75..c0b364b259e 100644 --- a/Mage.Sets/src/mage/cards/a/AjaniGoldmane.java +++ b/Mage.Sets/src/mage/cards/a/AjaniGoldmane.java @@ -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)); diff --git a/Mage.Sets/src/mage/cards/a/AjaniInspiringLeader.java b/Mage.Sets/src/mage/cards/a/AjaniInspiringLeader.java index 87c510e8f3a..e7eeb863bb9 100644 --- a/Mage.Sets/src/mage/cards/a/AjaniInspiringLeader.java +++ b/Mage.Sets/src/mage/cards/a/AjaniInspiringLeader.java @@ -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); diff --git a/Mage.Sets/src/mage/cards/a/AjaniMentorOfHeroes.java b/Mage.Sets/src/mage/cards/a/AjaniMentorOfHeroes.java index f4cb427e1e9..d79182900b1 100644 --- a/Mage.Sets/src/mage/cards/a/AjaniMentorOfHeroes.java +++ b/Mage.Sets/src/mage/cards/a/AjaniMentorOfHeroes.java @@ -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); diff --git a/Mage.Sets/src/mage/cards/a/AjaniSteadfast.java b/Mage.Sets/src/mage/cards/a/AjaniSteadfast.java index c951d1b4f02..4d843009533 100644 --- a/Mage.Sets/src/mage/cards/a/AjaniSteadfast.java +++ b/Mage.Sets/src/mage/cards/a/AjaniSteadfast.java @@ -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); diff --git a/Mage.Sets/src/mage/cards/a/AjaniStrengthOfThePride.java b/Mage.Sets/src/mage/cards/a/AjaniStrengthOfThePride.java index d0f076bbe36..b8154b1c060 100644 --- a/Mage.Sets/src/mage/cards/a/AjaniStrengthOfThePride.java +++ b/Mage.Sets/src/mage/cards/a/AjaniStrengthOfThePride.java @@ -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( diff --git a/Mage.Sets/src/mage/cards/a/AjaniTheGreathearted.java b/Mage.Sets/src/mage/cards/a/AjaniTheGreathearted.java index 837660006db..e30d19de341 100644 --- a/Mage.Sets/src/mage/cards/a/AjaniTheGreathearted.java +++ b/Mage.Sets/src/mage/cards/a/AjaniTheGreathearted.java @@ -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 diff --git a/Mage.Sets/src/mage/cards/a/AjaniUnyielding.java b/Mage.Sets/src/mage/cards/a/AjaniUnyielding.java index 50a5472824e..786318a9df8 100644 --- a/Mage.Sets/src/mage/cards/a/AjaniUnyielding.java +++ b/Mage.Sets/src/mage/cards/a/AjaniUnyielding.java @@ -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); } diff --git a/Mage.Sets/src/mage/cards/a/AjaniValiantProtector.java b/Mage.Sets/src/mage/cards/a/AjaniValiantProtector.java index 5e8025bcb75..1bff23aab26 100644 --- a/Mage.Sets/src/mage/cards/a/AjaniValiantProtector.java +++ b/Mage.Sets/src/mage/cards/a/AjaniValiantProtector.java @@ -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); diff --git a/Mage.Sets/src/mage/cards/a/AjaniVengeant.java b/Mage.Sets/src/mage/cards/a/AjaniVengeant.java index 57eee39eda4..28325717978 100644 --- a/Mage.Sets/src/mage/cards/a/AjaniVengeant.java +++ b/Mage.Sets/src/mage/cards/a/AjaniVengeant.java @@ -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); diff --git a/Mage.Sets/src/mage/cards/a/AjaniWiseCounselor.java b/Mage.Sets/src/mage/cards/a/AjaniWiseCounselor.java index e7fd5f8837b..33feec0fdd1 100644 --- a/Mage.Sets/src/mage/cards/a/AjaniWiseCounselor.java +++ b/Mage.Sets/src/mage/cards/a/AjaniWiseCounselor.java @@ -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) diff --git a/Mage.Sets/src/mage/cards/a/AjanisInfluence.java b/Mage.Sets/src/mage/cards/a/AjanisInfluence.java index 94700f16dc7..d5c036b26f2 100644 --- a/Mage.Sets/src/mage/cards/a/AjanisInfluence.java +++ b/Mage.Sets/src/mage/cards/a/AjanisInfluence.java @@ -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("
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.") ); diff --git a/Mage.Sets/src/mage/cards/a/AkkiBattleSquad.java b/Mage.Sets/src/mage/cards/a/AkkiBattleSquad.java new file mode 100644 index 00000000000..846e9e22b71 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AkkiBattleSquad.java @@ -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); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AkkiCoalflinger.java b/Mage.Sets/src/mage/cards/a/AkkiCoalflinger.java index a4c5c61c1dd..6a746b5a52e 100644 --- a/Mage.Sets/src/mage/cards/a/AkkiCoalflinger.java +++ b/Mage.Sets/src/mage/cards/a/AkkiCoalflinger.java @@ -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); } diff --git a/Mage.Sets/src/mage/cards/a/AkkiEmberKeeper.java b/Mage.Sets/src/mage/cards/a/AkkiEmberKeeper.java new file mode 100644 index 00000000000..a8e9c05f3fe --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AkkiEmberKeeper.java @@ -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); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AkkiRonin.java b/Mage.Sets/src/mage/cards/a/AkkiRonin.java new file mode 100644 index 00000000000..ebf96b42d8c --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AkkiRonin.java @@ -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); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AkkiWarPaint.java b/Mage.Sets/src/mage/cards/a/AkkiWarPaint.java new file mode 100644 index 00000000000..38ad19c6a00 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AkkiWarPaint.java @@ -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); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AkroanHoplite.java b/Mage.Sets/src/mage/cards/a/AkroanHoplite.java index d6b34e5df9f..4b0db99753a 100644 --- a/Mage.Sets/src/mage/cards/a/AkroanHoplite.java +++ b/Mage.Sets/src/mage/cards/a/AkroanHoplite.java @@ -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) { diff --git a/Mage.Sets/src/mage/cards/a/AkroanHorse.java b/Mage.Sets/src/mage/cards/a/AkroanHorse.java index f9ed7447c89..ea9789aebed 100644 --- a/Mage.Sets/src/mage/cards/a/AkroanHorse.java +++ b/Mage.Sets/src/mage/cards/a/AkroanHorse.java @@ -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; } diff --git a/Mage.Sets/src/mage/cards/a/AkromaVisionOfIxidor.java b/Mage.Sets/src/mage/cards/a/AkromaVisionOfIxidor.java index c859b98ff03..b3f27fa3cdc 100644 --- a/Mage.Sets/src/mage/cards/a/AkromaVisionOfIxidor.java +++ b/Mage.Sets/src/mage/cards/a/AkromaVisionOfIxidor.java @@ -114,7 +114,7 @@ class AkromaVisionOfIxidorEffect extends OneShotEffect { .sum(); if (count > 0) { ContinuousEffect effect = new BoostTargetEffect(count, count, Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(permanent.getId())); + effect.setTargetPointer(new FixedTarget(permanent.getId(), game)); game.addEffect(effect, source); } } diff --git a/Mage.Sets/src/mage/cards/a/AkromasBlessing.java b/Mage.Sets/src/mage/cards/a/AkromasBlessing.java index 39c76efabb7..06b0bbb62e7 100644 --- a/Mage.Sets/src/mage/cards/a/AkromasBlessing.java +++ b/Mage.Sets/src/mage/cards/a/AkromasBlessing.java @@ -9,7 +9,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; /** @@ -23,7 +23,7 @@ public final class AkromasBlessing extends CardImpl { // Choose a color. Creatures you control gain protection from the chosen color until end of turn. - this.getSpellAbility().addEffect(new GainProtectionFromColorAllEffect(Duration.EndOfTurn, new FilterControlledCreaturePermanent("Creatures you control"))); + this.getSpellAbility().addEffect(new GainProtectionFromColorAllEffect(Duration.EndOfTurn, StaticFilters.FILTER_CONTROLLED_CREATURES)); // Cycling {W} this.addAbility(new CyclingAbility(new ManaCostsImpl("{W}"))); } diff --git a/Mage.Sets/src/mage/cards/a/Aladdin.java b/Mage.Sets/src/mage/cards/a/Aladdin.java index 4afa37d8da6..15c85f9ce87 100644 --- a/Mage.Sets/src/mage/cards/a/Aladdin.java +++ b/Mage.Sets/src/mage/cards/a/Aladdin.java @@ -1,25 +1,21 @@ package mage.cards.a; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.condition.common.SourceOnBattlefieldControlUnchangedCondition; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.common.continuous.GainControlTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.Zone; import mage.target.common.TargetArtifactPermanent; -import mage.watchers.common.LostControlWatcher; + +import java.util.UUID; /** - * * @author nigelzor */ public final class Aladdin extends CardImpl { @@ -32,14 +28,11 @@ public final class Aladdin extends CardImpl { this.toughness = new MageInt(1); // {1}{R}{R}, {tap}: Gain control of target artifact for as long as you control Aladdin. - ConditionalContinuousEffect effect = new ConditionalContinuousEffect( - new GainControlTargetEffect(Duration.Custom), - new SourceOnBattlefieldControlUnchangedCondition(), - "Gain control of target artifact for as long as you control Aladdin"); - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl("{1}{R}{R}")); + Ability ability = new SimpleActivatedAbility( + new GainControlTargetEffect(Duration.WhileControlled), new ManaCostsImpl<>("{1}{R}{R}") + ); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetArtifactPermanent()); - ability.addWatcher(new LostControlWatcher()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/a/AlchemistsGambit.java b/Mage.Sets/src/mage/cards/a/AlchemistsGambit.java new file mode 100644 index 00000000000..a9a49bf1ca5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AlchemistsGambit.java @@ -0,0 +1,98 @@ +package mage.cards.a; + +import mage.abilities.Ability; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.ExileSpellEffect; +import mage.abilities.effects.common.turn.AddExtraTurnControllerEffect; +import mage.abilities.keyword.CleaveAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.events.GameEvent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AlchemistsGambit extends CardImpl { + + public AlchemistsGambit(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{R}{R}"); + + // Cleave {4}{U}{U}{R} + Ability ability = new CleaveAbility( + this, + new AddExtraTurnControllerEffect( + false, AlchemistsGambitApplier.instance + ), "{4}{U}{U}{R}" + ); + ability.addEffect(new ExileSpellEffect()); + this.addAbility(ability); + + // Take an extra turn after this one. During that turn, damage can't be prevented. [At the beginning of that turn's end step, you lose the game.] + this.getSpellAbility().addEffect(new AddExtraTurnControllerEffect( + true, AlchemistsGambitApplier.instance + ).setText("take an extra turn after this one. During that turn, damage can't be prevented. " + + "[At the beginning of that turn's end step, you lose the game.]")); + + // Exile Alchemist's Gambit. + this.getSpellAbility().addEffect(new ExileSpellEffect().concatBy("
")); + } + + private AlchemistsGambit(final AlchemistsGambit card) { + super(card); + } + + @Override + public AlchemistsGambit copy() { + return new AlchemistsGambit(this); + } +} + +enum AlchemistsGambitApplier implements AddExtraTurnControllerEffect.TurnModApplier { + instance; + + @Override + public void apply(UUID turnId, Ability source, Game game) { + game.addEffect(new AlchemistsGambitEffect(turnId), source); + } +} + +class AlchemistsGambitEffect extends ReplacementEffectImpl { + + private final UUID turnId; + + public AlchemistsGambitEffect(UUID turnId) { + super(Duration.Custom, Outcome.Benefit); + this.turnId = turnId; + } + + public AlchemistsGambitEffect(final AlchemistsGambitEffect effect) { + super(effect); + this.turnId = effect.turnId; + } + + @Override + public AlchemistsGambitEffect copy() { + return new AlchemistsGambitEffect(this); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + return true; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.PREVENT_DAMAGE; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return game.getState().getTurnId().equals(turnId); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AlchemistsRetrieval.java b/Mage.Sets/src/mage/cards/a/AlchemistsRetrieval.java new file mode 100644 index 00000000000..fd0d4d57d45 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AlchemistsRetrieval.java @@ -0,0 +1,49 @@ +package mage.cards.a; + +import mage.abilities.Ability; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.abilities.keyword.CleaveAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterNonlandPermanent; +import mage.target.TargetPermanent; +import mage.target.common.TargetNonlandPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AlchemistsRetrieval extends CardImpl { + + private static final FilterPermanent filter = new FilterNonlandPermanent("nonland permanent [you control]"); + + static { + filter.add(TargetController.YOU.getControllerPredicate()); + } + + public AlchemistsRetrieval(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{U}"); + + // Cleave {1}{U} + Ability ability = new CleaveAbility(this, new ReturnToHandTargetEffect(), "{1}{U}"); + ability.addTarget(new TargetNonlandPermanent()); + this.addAbility(ability); + + // Return target nonland permanent [you control] to its owner's hand. + this.getSpellAbility().addEffect(new ReturnToHandTargetEffect()); + this.getSpellAbility().addTarget(new TargetPermanent(filter)); + } + + private AlchemistsRetrieval(final AlchemistsRetrieval card) { + super(card); + } + + @Override + public AlchemistsRetrieval copy() { + return new AlchemistsRetrieval(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AlertHeedbonder.java b/Mage.Sets/src/mage/cards/a/AlertHeedbonder.java index 8e77c371bdd..02eb1bdb7cf 100644 --- a/Mage.Sets/src/mage/cards/a/AlertHeedbonder.java +++ b/Mage.Sets/src/mage/cards/a/AlertHeedbonder.java @@ -44,7 +44,9 @@ public final class AlertHeedbonder extends CardImpl { // At the beginning of your end step, you gain 1 life for each creature you control with vigilance. this.addAbility(new BeginningOfEndStepTriggeredAbility( - new GainLifeEffect(xValue), TargetController.YOU, false + new GainLifeEffect(xValue) + .setText("you gain 1 life for each creature you control with vigilance"), + TargetController.YOU, false )); } diff --git a/Mage.Sets/src/mage/cards/a/AllIsDust.java b/Mage.Sets/src/mage/cards/a/AllIsDust.java index fab13e4be5e..606f71cdeda 100644 --- a/Mage.Sets/src/mage/cards/a/AllIsDust.java +++ b/Mage.Sets/src/mage/cards/a/AllIsDust.java @@ -38,7 +38,7 @@ class AllIsDustEffect extends OneShotEffect { AllIsDustEffect() { super(Outcome.DestroyPermanent); - staticText = "Each player sacrifices all colored permanents they control"; + staticText = "Each player sacrifices all permanents they control that are one or more colors"; } AllIsDustEffect(final AllIsDustEffect effect) { diff --git a/Mage.Sets/src/mage/cards/a/AlleyGrifters.java b/Mage.Sets/src/mage/cards/a/AlleyGrifters.java index c33f3be5f1d..4e91fc294e9 100644 --- a/Mage.Sets/src/mage/cards/a/AlleyGrifters.java +++ b/Mage.Sets/src/mage/cards/a/AlleyGrifters.java @@ -1,19 +1,13 @@ - package mage.cards.a; import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.discard.DiscardTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; /** * @@ -29,7 +23,9 @@ public final class AlleyGrifters extends CardImpl { this.toughness = new MageInt(2); // Whenever Alley Grifters becomes blocked, defending player discards a card. - this.addAbility(new BecomesBlockedSourceTriggeredAbility(new AlleyGriftersDiscardEffect(), false)); + this.addAbility(new BecomesBlockedSourceTriggeredAbility( + new DiscardTargetEffect(1).setText("defending player discards a card"), + false, true)); } private AlleyGrifters(final AlleyGrifters card) { @@ -41,33 +37,3 @@ public final class AlleyGrifters extends CardImpl { return new AlleyGrifters(this); } } - -class AlleyGriftersDiscardEffect extends OneShotEffect { - - public AlleyGriftersDiscardEffect() { - super(Outcome.Discard); - this.staticText = "defending player discards a card"; - } - - public AlleyGriftersDiscardEffect(final AlleyGriftersDiscardEffect effect) { - super(effect); - } - - @Override - public AlleyGriftersDiscardEffect copy() { - return new AlleyGriftersDiscardEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent blockingCreature = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (blockingCreature != null) { - Player opponent = game.getPlayer(blockingCreature.getControllerId()); - if (opponent != null) { - opponent.discard(1, false, false, source, game); - return true; - } - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/a/AlleyStrangler.java b/Mage.Sets/src/mage/cards/a/AlleyStrangler.java index f56725ad4bb..e9635081794 100644 --- a/Mage.Sets/src/mage/cards/a/AlleyStrangler.java +++ b/Mage.Sets/src/mage/cards/a/AlleyStrangler.java @@ -24,7 +24,7 @@ public final class AlleyStrangler extends CardImpl { this.toughness = new MageInt(3); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); } private AlleyStrangler(final AlleyStrangler card) { diff --git a/Mage.Sets/src/mage/cards/a/AlluringSiren.java b/Mage.Sets/src/mage/cards/a/AlluringSiren.java index 3a286660236..c8b658087eb 100644 --- a/Mage.Sets/src/mage/cards/a/AlluringSiren.java +++ b/Mage.Sets/src/mage/cards/a/AlluringSiren.java @@ -11,7 +11,7 @@ import mage.abilities.effects.common.combat.AttacksIfAbleTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; /** @@ -20,12 +20,6 @@ import mage.target.common.TargetCreaturePermanent; */ public final class AlluringSiren extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public AlluringSiren(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}"); @@ -33,7 +27,7 @@ public final class AlluringSiren extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AttacksIfAbleTargetEffect(Duration.EndOfTurn), new TapSourceCost()); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/a/AlluringSuitor.java b/Mage.Sets/src/mage/cards/a/AlluringSuitor.java new file mode 100644 index 00000000000..b9ea71bbb06 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AlluringSuitor.java @@ -0,0 +1,81 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.keyword.TransformAbility; +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.GameEvent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AlluringSuitor extends CardImpl { + + public AlluringSuitor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); + + this.subtype.add(SubType.VAMPIRE); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + this.secondSideCardClazz = mage.cards.d.DeadlyDancer.class; + + // When you attack with exactly two creatures, transform Alluring Suitor. + this.addAbility(new TransformAbility()); + this.addAbility(new AlluringSuitorTriggeredAbility()); + } + + private AlluringSuitor(final AlluringSuitor card) { + super(card); + } + + @Override + public AlluringSuitor copy() { + return new AlluringSuitor(this); + } +} + +class AlluringSuitorTriggeredAbility extends TriggeredAbilityImpl { + + AlluringSuitorTriggeredAbility() { + super(Zone.BATTLEFIELD, new TransformSourceEffect()); + } + + private AlluringSuitorTriggeredAbility(final AlluringSuitorTriggeredAbility ability) { + super(ability); + } + + @Override + public AlluringSuitorTriggeredAbility copy() { + return new AlluringSuitorTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DECLARED_ATTACKERS; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return isControlledBy(game.getCombat().getAttackingPlayerId()) + && game + .getCombat() + .getAttackers() + .stream() + .map(game::getPermanent) + .filter(permanent -> permanent.isCreature(game)) + .count() == 2; + } + + @Override + public String getRule() { + return "When you attack with exactly two creatures, transform {this}."; + } +} diff --git a/Mage.Sets/src/mage/cards/a/AlphaBrawl.java b/Mage.Sets/src/mage/cards/a/AlphaBrawl.java index bdc752290f1..ef8cb04aa61 100644 --- a/Mage.Sets/src/mage/cards/a/AlphaBrawl.java +++ b/Mage.Sets/src/mage/cards/a/AlphaBrawl.java @@ -1,4 +1,3 @@ - package mage.cards.a; import java.util.UUID; @@ -8,8 +7,10 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.TargetController; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.PermanentIdPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -21,19 +22,12 @@ import mage.target.common.TargetCreaturePermanent; */ public final class AlphaBrawl extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public AlphaBrawl(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{6}{R}{R}"); - // Target creature an opponent controls deals damage equal to its power to each other creature that player controls, then each of those creatures deals damage equal to its power to that creature. this.getSpellAbility().addEffect(new AlphaBrawlEffect()); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); } @@ -49,8 +43,6 @@ public final class AlphaBrawl extends CardImpl { class AlphaBrawlEffect extends OneShotEffect { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); - public AlphaBrawlEffect() { super(Outcome.Damage); staticText = "Target creature an opponent controls deals damage equal to its power to each other creature that player controls, then each of those creatures deals damage equal to its power to that creature"; @@ -62,7 +54,10 @@ class AlphaBrawlEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent creature = game.getPermanent(source.getFirstTarget()); + UUID targetId = source.getFirstTarget(); + FilterCreaturePermanent filter = new FilterCreaturePermanent("each other creature that player controls"); + filter.add(Predicates.not(new PermanentIdPredicate(targetId))); + Permanent creature = game.getPermanent(targetId); if (creature != null) { Player player = game.getPlayer(creature.getControllerId()); if (player != null) { @@ -84,4 +79,4 @@ class AlphaBrawlEffect extends OneShotEffect { return new AlphaBrawlEffect(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/a/AlpineHoundmaster.java b/Mage.Sets/src/mage/cards/a/AlpineHoundmaster.java index 33100f83b55..fa9269902e5 100644 --- a/Mage.Sets/src/mage/cards/a/AlpineHoundmaster.java +++ b/Mage.Sets/src/mage/cards/a/AlpineHoundmaster.java @@ -13,7 +13,6 @@ import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.cards.*; import mage.constants.*; import mage.filter.FilterCard; -import mage.filter.FilterPermanent; import mage.filter.common.FilterAttackingCreature; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.NamePredicate; @@ -29,7 +28,7 @@ import java.util.UUID; */ public final class AlpineHoundmaster extends CardImpl { - private static final FilterPermanent filter = new FilterAttackingCreature("the number of other attacking creatures"); + private static final FilterAttackingCreature filter = new FilterAttackingCreature("other attacking creatures"); static { filter.add(AnotherPredicate.instance); @@ -49,9 +48,7 @@ public final class AlpineHoundmaster extends CardImpl { this.addAbility(new EntersBattlefieldTriggeredAbility(new AlpineHoundmasterEffect(), true)); // Whenever Alpine Houndmaster attacks, it gets +X/+0 until end of turn, where X is the number of other attacking creatures. - this.addAbility(new AttacksTriggeredAbility(new BoostSourceEffect( - xValue, StaticValue.get(0), Duration.EndOfTurn, true - ).setText("it gets +X/+0 until end of turn, where X is the number of other attacking creatures"), false)); + this.addAbility(new AttacksTriggeredAbility(new BoostSourceEffect(xValue, StaticValue.get(0), Duration.EndOfTurn, true, "it"), false)); } private AlpineHoundmaster(final AlpineHoundmaster card) { diff --git a/Mage.Sets/src/mage/cards/a/AlrundGodOfTheCosmos.java b/Mage.Sets/src/mage/cards/a/AlrundGodOfTheCosmos.java index 35b4748f591..73f77296623 100644 --- a/Mage.Sets/src/mage/cards/a/AlrundGodOfTheCosmos.java +++ b/Mage.Sets/src/mage/cards/a/AlrundGodOfTheCosmos.java @@ -50,7 +50,7 @@ public final class AlrundGodOfTheCosmos extends ModalDoubleFacesCard { // Alrund gets +1/+1 for each card in your hand and each foretold card you own in exile. Effect effect = new BoostSourceEffect(AlrundGodOfTheCosmosValue.instance, AlrundGodOfTheCosmosValue.instance, Duration.EndOfGame); - effect.setText("Alrund gets +1/+1 for each card in your hand and each foretold card you own in exile."); + effect.setText("{this} gets +1/+1 for each card in your hand and each foretold card you own in exile."); Ability ability = new SimpleStaticAbility(effect); this.getLeftHalfCard().addAbility(ability); @@ -89,7 +89,7 @@ class AlrundGodOfTheCosmosEffect extends OneShotEffect { public AlrundGodOfTheCosmosEffect() { super(Outcome.Neutral); - staticText = ", then reveal the top two cards of your library. Put all cards revealed this way of the chosen type into your hand and the rest on the bottom of your library in any order"; + staticText = ", then reveal the top two cards of your library. Put all cards of the chosen type revealed this way into your hand and the rest on the bottom of your library in any order"; } public AlrundGodOfTheCosmosEffect(final AlrundGodOfTheCosmosEffect effect) { diff --git a/Mage.Sets/src/mage/cards/a/AltacBloodseeker.java b/Mage.Sets/src/mage/cards/a/AltacBloodseeker.java index 655e9ca1bb7..dfb56e9a31e 100644 --- a/Mage.Sets/src/mage/cards/a/AltacBloodseeker.java +++ b/Mage.Sets/src/mage/cards/a/AltacBloodseeker.java @@ -15,20 +15,13 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; /** * * @author Quercitron */ public final class AltacBloodseeker extends CardImpl { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } public AltacBloodseeker(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{R}"); @@ -41,7 +34,7 @@ public final class AltacBloodseeker extends CardImpl { // Whenever a creature an opponent controls dies, Altac Bloodseeker gets +2/+0 and gains first strike and haste until end of turn. Effect effect = new BoostSourceEffect(2, 0, Duration.EndOfTurn); effect.setText("{this} gets +2/+0"); - Ability ability = new DiesCreatureTriggeredAbility(effect, false, filter); + Ability ability = new DiesCreatureTriggeredAbility(effect, false, StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE); effect = new GainAbilitySourceEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn); effect.setText("and gains first strike"); diff --git a/Mage.Sets/src/mage/cards/a/AltarOfTheGoyf.java b/Mage.Sets/src/mage/cards/a/AltarOfTheGoyf.java index 6e44a81f7ac..a590ba3de37 100644 --- a/Mage.Sets/src/mage/cards/a/AltarOfTheGoyf.java +++ b/Mage.Sets/src/mage/cards/a/AltarOfTheGoyf.java @@ -1,6 +1,6 @@ package mage.cards.a; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.AttacksAloneControlledTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.dynamicvalue.common.CardTypesInGraveyardCount; import mage.abilities.effects.common.continuous.BoostTargetEffect; @@ -12,12 +12,8 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.target.targetpointer.FixedTarget; import java.util.UUID; @@ -35,7 +31,12 @@ public final class AltarOfTheGoyf extends CardImpl { this.subtype.add(SubType.LHURGOYF); // Whenever a creature you control attacks alone, it gets +X/+X until end of turn, where X is the number of card types among cards in all graveyard. - this.addAbility(new AltarOfTheGoyfAbility()); + this.addAbility(new AttacksAloneControlledTriggeredAbility(new BoostTargetEffect( + CardTypesInGraveyardCount.ALL, + CardTypesInGraveyardCount.ALL, + Duration.EndOfTurn, true + ).setText("it gets +X/+X until end of turn, where X is " + + "the number of card types among cards in all graveyards.")).addHint(CardTypesInGraveyardHint.ALL)); // Lhurgoyf creatures you control have trample. this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( @@ -52,43 +53,3 @@ public final class AltarOfTheGoyf extends CardImpl { return new AltarOfTheGoyf(this); } } - -class AltarOfTheGoyfAbility extends TriggeredAbilityImpl { - - public AltarOfTheGoyfAbility() { - super(Zone.BATTLEFIELD, new BoostTargetEffect( - CardTypesInGraveyardCount.ALL, CardTypesInGraveyardCount.ALL, Duration.EndOfTurn, true - ), false); - this.addHint(CardTypesInGraveyardHint.ALL); - } - - public AltarOfTheGoyfAbility(final AltarOfTheGoyfAbility ability) { - super(ability); - } - - @Override - public AltarOfTheGoyfAbility copy() { - return new AltarOfTheGoyfAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DECLARED_ATTACKERS; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (game.isActivePlayer(this.controllerId) && game.getCombat().attacksAlone()) { - this.getEffects().setTargetPointer(new FixedTarget(game.getCombat().getAttackers().get(0), game)); - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever a creature you control attacks alone, " + - "it gets +X/+X until end of turn, " + - "where X is the number of card types among cards in all graveyards."; - } -} diff --git a/Mage.Sets/src/mage/cards/a/AmbitiousAssault.java b/Mage.Sets/src/mage/cards/a/AmbitiousAssault.java new file mode 100644 index 00000000000..1992c9fd79e --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AmbitiousAssault.java @@ -0,0 +1,51 @@ +package mage.cards.a; + +import java.util.UUID; + +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.ModifiedPredicate; + +/** + * + * @author weirddan455 + */ +public final class AmbitiousAssault extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("you control a modified creature"); + + static { + filter.add(ModifiedPredicate.instance); + } + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter); + private static final Hint hint = new ConditionHint(condition); + + public AmbitiousAssault(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{R}"); + + // Creatures you control get +2/+0 until end of turn. If you control a modified creature, draw a card. + this.getSpellAbility().addEffect(new BoostControlledEffect(2, 0, Duration.EndOfTurn)); + this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new DrawCardSourceControllerEffect(1), condition)); + this.getSpellAbility().addHint(hint); + } + + private AmbitiousAssault(final AmbitiousAssault card) { + super(card); + } + + @Override + public AmbitiousAssault copy() { + return new AmbitiousAssault(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AmbitiousFarmhand.java b/Mage.Sets/src/mage/cards/a/AmbitiousFarmhand.java index b292ac13b41..63256a34839 100644 --- a/Mage.Sets/src/mage/cards/a/AmbitiousFarmhand.java +++ b/Mage.Sets/src/mage/cards/a/AmbitiousFarmhand.java @@ -36,7 +36,6 @@ public final class AmbitiousFarmhand extends CardImpl { this.subtype.add(SubType.PEASANT); this.power = new MageInt(1); this.toughness = new MageInt(1); - this.transformable = true; this.secondSideCardClazz = mage.cards.s.SeasonedCathar.class; // When Ambitious Farmhand enters the battlefield, you may search your library for a basic Plains card, reveal it, put it into your hand, then shuffle. @@ -47,7 +46,7 @@ public final class AmbitiousFarmhand extends CardImpl { // Coven—{1}{W}{W}: Transform Ambitious Farmhand. Activate only if you control three or more creatures with different powers. this.addAbility(new TransformAbility()); this.addAbility(new ActivateIfConditionActivatedAbility( - Zone.BATTLEFIELD, new TransformSourceEffect(true), + Zone.BATTLEFIELD, new TransformSourceEffect(), new ManaCostsImpl<>("{1}{W}{W}"), CovenCondition.instance ).setAbilityWord(AbilityWord.COVEN).addHint(CovenHint.instance)); } diff --git a/Mage.Sets/src/mage/cards/a/Ambuscade.java b/Mage.Sets/src/mage/cards/a/Ambuscade.java index 28ab3c6e17b..c7d4ed15304 100644 --- a/Mage.Sets/src/mage/cards/a/Ambuscade.java +++ b/Mage.Sets/src/mage/cards/a/Ambuscade.java @@ -7,9 +7,8 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.StaticFilters; import mage.target.common.TargetControlledCreaturePermanent; -import mage.target.common.TargetCreaturePermanent; +import mage.target.common.TargetOpponentsCreaturePermanent; import java.util.UUID; @@ -28,7 +27,7 @@ public final class Ambuscade extends CardImpl { // It deals damage equal to its power to target creature you don't control. this.getSpellAbility().addEffect(new DamageWithPowerFromOneToAnotherTargetEffect("It")); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); // second target for effect + this.getSpellAbility().addTarget(new TargetOpponentsCreaturePermanent()); // second target for effect } private Ambuscade(final Ambuscade card) { diff --git a/Mage.Sets/src/mage/cards/a/Ambush.java b/Mage.Sets/src/mage/cards/a/Ambush.java index 01ef724ff84..ffb5529e360 100644 --- a/Mage.Sets/src/mage/cards/a/Ambush.java +++ b/Mage.Sets/src/mage/cards/a/Ambush.java @@ -8,21 +8,19 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.common.FilterBlockingCreature; +import mage.filter.StaticFilters; /** * * @author fireshoes */ public final class Ambush extends CardImpl { - - private static final FilterBlockingCreature filter = new FilterBlockingCreature("Blocking creatures"); public Ambush(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{3}{R}"); // Blocking creatures gain first strike until end of turn. - this.getSpellAbility().addEffect(new GainAbilityAllEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn, filter, false)); + this.getSpellAbility().addEffect(new GainAbilityAllEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_BLOCKING_CREATURES, false)); } private Ambush(final Ambush card) { diff --git a/Mage.Sets/src/mage/cards/a/AminatouTheFateshifter.java b/Mage.Sets/src/mage/cards/a/AminatouTheFateshifter.java index 9125e8fc0f8..6ef5a39ee3c 100644 --- a/Mage.Sets/src/mage/cards/a/AminatouTheFateshifter.java +++ b/Mage.Sets/src/mage/cards/a/AminatouTheFateshifter.java @@ -3,7 +3,6 @@ package mage.cards.a; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.common.CanBeYourCommanderAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ExileTargetForSourceEffect; @@ -46,7 +45,7 @@ public class AminatouTheFateshifter extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.AMINATOU); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +1: Draw a card, then put a card from your hand on top of your library. Ability ability = new LoyaltyAbility(new AminatouPlusEffect(), +1); diff --git a/Mage.Sets/src/mage/cards/a/AmuletOfVigor.java b/Mage.Sets/src/mage/cards/a/AmuletOfVigor.java index 3dab8ec2d57..383366fa4f1 100644 --- a/Mage.Sets/src/mage/cards/a/AmuletOfVigor.java +++ b/Mage.Sets/src/mage/cards/a/AmuletOfVigor.java @@ -13,6 +13,7 @@ import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; import mage.util.GameLog; +import java.util.Optional; import java.util.UUID; /** @@ -63,7 +64,7 @@ class AmuletOfVigorTriggeredAbility extends TriggeredAbilityImpl { if (permanent != null && permanent.isTapped() && permanent.isControlledBy(this.getControllerId())) { for (Effect effect : this.getEffects()) { effect.setTargetPointer( - new FixedTarget(event.getTargetId()) + new FixedTarget(event.getTargetId(), game) .withData("triggeredName", GameLog.getColoredObjectIdNameForTooltip(permanent)) ); } @@ -75,10 +76,12 @@ class AmuletOfVigorTriggeredAbility extends TriggeredAbilityImpl { @Override public String getRule() { // that triggers depends on stack order, so make each trigger unique with extra info - String triggeredInfo = ""; - if (this.getEffects().get(0).getTargetPointer() != null) { - triggeredInfo = " Triggered permanent: " + this.getEffects().get(0).getTargetPointer().getData("triggeredName") + "."; - } - return "Whenever a permanent enters the battlefield tapped and under your control, untap it." + triggeredInfo; + return "Whenever a permanent enters the battlefield tapped and under your control, untap it." + + Optional + .of(this.getEffects().get(0).getTargetPointer()) + .map(targetPointer -> targetPointer.getData("triggeredName")) + .filter(s -> s != null && !s.isEmpty()) + .map(s -> " Triggered permanent: " + s) + .orElse(""); } } diff --git a/Mage.Sets/src/mage/cards/a/AnaSanctuary.java b/Mage.Sets/src/mage/cards/a/AnaSanctuary.java index 41ad5a2adf3..7acb2a3d590 100644 --- a/Mage.Sets/src/mage/cards/a/AnaSanctuary.java +++ b/Mage.Sets/src/mage/cards/a/AnaSanctuary.java @@ -68,7 +68,7 @@ class BoostEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { ContinuousEffect effect = new BoostTargetEffect(amount, amount, Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(source.getFirstTarget())); + effect.setTargetPointer(new FixedTarget(source.getFirstTarget(), game)); game.addEffect(effect, source); return true; } diff --git a/Mage.Sets/src/mage/cards/a/AnakinSkywalker.java b/Mage.Sets/src/mage/cards/a/AnakinSkywalker.java index e8a3e9c205f..ad03a0f3c92 100644 --- a/Mage.Sets/src/mage/cards/a/AnakinSkywalker.java +++ b/Mage.Sets/src/mage/cards/a/AnakinSkywalker.java @@ -38,7 +38,6 @@ public final class AnakinSkywalker extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(4); - this.transformable = true; this.secondSideCardClazz = mage.cards.d.DarthVader.class; // Whenever another creature dies, put a +1/+1 counter on Anakin Skywalker. @@ -82,7 +81,7 @@ class AnakinSkywalkerEffect extends ReplacementEffectImpl { Permanent permanent = game.getPermanent(event.getTargetId()); if (permanent != null) { permanent.regenerate(source, game); - return new TransformSourceEffect(true).apply(game, source); + return new TransformSourceEffect().apply(game, source); } return false; } diff --git a/Mage.Sets/src/mage/cards/a/AncestorsEmbrace.java b/Mage.Sets/src/mage/cards/a/AncestorsEmbrace.java new file mode 100644 index 00000000000..69ae1e5e60f --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AncestorsEmbrace.java @@ -0,0 +1,55 @@ +package mage.cards.a; + +import mage.abilities.Ability; +import mage.abilities.common.PutIntoGraveFromAnywhereSourceAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.ExileSourceEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AncestorsEmbrace extends CardImpl { + + public AncestorsEmbrace(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, ""); + + this.subtype.add(SubType.AURA); + this.color.setWhite(true); + this.nightCard = true; + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // Enchanted creature has lifelink. + this.addAbility(new SimpleStaticAbility(new GainAbilityAttachedEffect( + LifelinkAbility.getInstance(), AttachmentType.AURA, Duration.WhileOnBattlefield + ))); + + // If Ancestor's Embrace would be put into a graveyard from anywhere, exile it instead. + this.addAbility(new PutIntoGraveFromAnywhereSourceAbility(new ExileSourceEffect().setText("exile it instead"))); + } + + private AncestorsEmbrace(final AncestorsEmbrace card) { + super(card); + } + + @Override + public AncestorsEmbrace copy() { + return new AncestorsEmbrace(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AncestralAnger.java b/Mage.Sets/src/mage/cards/a/AncestralAnger.java new file mode 100644 index 00000000000..335bf4ca705 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AncestralAnger.java @@ -0,0 +1,67 @@ +package mage.cards.a; + +import mage.abilities.dynamicvalue.AdditiveDynamicValue; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.NamePredicate; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AncestralAnger extends CardImpl { + + private static final FilterCard filter = new FilterCard(); + + static { + filter.add(new NamePredicate("Ancestral Anger")); + } + + private static final DynamicValue xValue = new AdditiveDynamicValue( + new CardsInControllerGraveyardCount(filter), StaticValue.get(1) + ); + private static final Hint hint = new ValueHint( + "Cards named Ancestral Anger in your graveyard", new CardsInControllerGraveyardCount(filter) + ); + + public AncestralAnger(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{R}"); + + // Target creature gains trample and gets +X/+0 until end of turn, where X is 1 plus the number of cards named Ancestral Anger in your graveyard. + this.getSpellAbility().addEffect(new GainAbilityTargetEffect( + TrampleAbility.getInstance(), Duration.EndOfTurn + ).setText("target creature gains trample")); + this.getSpellAbility().addEffect(new BoostTargetEffect( + xValue, StaticValue.get(0), Duration.EndOfTurn + ).setText("and gets +X/+0 until end of turn, where X is 1 plus " + + "the number of cards named Ancestral Anger in your graveyard")); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + this.getSpellAbility().addHint(hint); + + // Draw a card. + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("
")); + } + + private AncestralAnger(final AncestralAnger card) { + super(card); + } + + @Override + public AncestralAnger copy() { + return new AncestralAnger(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AncestralKatana.java b/Mage.Sets/src/mage/cards/a/AncestralKatana.java new file mode 100644 index 00000000000..bb53a2cbe60 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AncestralKatana.java @@ -0,0 +1,53 @@ +package mage.cards.a; + +import mage.abilities.common.AttacksAloneControlledTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.DoWhenCostPaid; +import mage.abilities.effects.common.continuous.BoostEquippedEffect; +import mage.abilities.keyword.EquipAbility; +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 java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AncestralKatana extends CardImpl { + + public AncestralKatana(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}{W}"); + + this.subtype.add(SubType.EQUIPMENT); + + // Whenever a Samurai or Warrior you control attacks alone, you may pay {1}. When you do, attach Ancestral Katana to it. + this.addAbility(new AttacksAloneControlledTriggeredAbility(new DoWhenCostPaid( + new ReflexiveTriggeredAbility( + new AttachEffect(Outcome.BoostCreature), + false, "attach {this} to it" + ), new GenericManaCost(1), "Pay {1}?" + ), StaticFilters.FILTER_CONTROLLED_SAMURAI_OR_WARRIOR, true, false)); + + // Equipped creature gets +2/+1. + this.addAbility(new SimpleStaticAbility(new BoostEquippedEffect(2, 1))); + + // Equip {3} + this.addAbility(new EquipAbility(3)); + } + + private AncestralKatana(final AncestralKatana card) { + super(card); + } + + @Override + public AncestralKatana copy() { + return new AncestralKatana(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AnchorToReality.java b/Mage.Sets/src/mage/cards/a/AnchorToReality.java new file mode 100644 index 00000000000..2a6954237b4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AnchorToReality.java @@ -0,0 +1,110 @@ +package mage.cards.a; + +import java.util.List; +import java.util.UUID; + +import mage.abilities.Ability; +import mage.abilities.costs.Cost; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCardInLibrary; + +/** + * + * @author weirddan455 + */ +public final class AnchorToReality extends CardImpl { + + private static final FilterControlledPermanent filter = new FilterControlledPermanent("an artifact or creature"); + + static { + filter.add(Predicates.or(CardType.ARTIFACT.getPredicate(), CardType.CREATURE.getPredicate())); + } + + public AnchorToReality(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{U}{U}"); + + // As an additional cost to cast this spell, sacrifice an artifact or creature. + this.getSpellAbility().addCost(new SacrificeTargetCost(filter)); + + // Search your library for an Equipment or Vehicle card, put that card onto the battlefield, then shuffle. If it has mana value less than the sacrificed permanent's mana value, scry 2. + this.getSpellAbility().addEffect(new AnchorToRealityEffect()); + } + + private AnchorToReality(final AnchorToReality card) { + super(card); + } + + @Override + public AnchorToReality copy() { + return new AnchorToReality(this); + } +} + +class AnchorToRealityEffect extends OneShotEffect { + + private static final FilterCard filter = new FilterCard("an Equipment or Vehicle card"); + + static { + filter.add(Predicates.or(SubType.EQUIPMENT.getPredicate(), SubType.VEHICLE.getPredicate())); + } + + public AnchorToRealityEffect() { + super(Outcome.PutCardInPlay); + this.staticText = "Search your library for an Equipment or Vehicle card, put that card onto the battlefield, then shuffle. If it has mana value less than the sacrificed permanent's mana value, scry 2"; + } + + private AnchorToRealityEffect(final AnchorToRealityEffect effect) { + super(effect); + } + + @Override + public AnchorToRealityEffect copy() { + return new AnchorToRealityEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + TargetCardInLibrary target = new TargetCardInLibrary(filter); + controller.searchLibrary(target, source, game); + Card card = game.getCard(target.getFirstTarget()); + if (card == null) { + controller.shuffleLibrary(source, game); + return true; + } + int searchedManaValue = card.getManaValue(); + controller.moveCards(card, Zone.BATTLEFIELD, source, game); + controller.shuffleLibrary(source, game); + int sacrificedManaValue = 0; + for (Cost cost : source.getCosts()) { + if (cost instanceof SacrificeTargetCost) { + List sacrificedPermanents = ((SacrificeTargetCost) cost).getPermanents(); + if (sacrificedPermanents.size() > 0) { + sacrificedManaValue = sacrificedPermanents.get(0).getManaValue(); + break; + } + } + } + if (searchedManaValue < sacrificedManaValue) { + controller.scry(2, source, game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/a/AncientAnimus.java b/Mage.Sets/src/mage/cards/a/AncientAnimus.java index e2b0e9387d9..ebcc1ab75ba 100644 --- a/Mage.Sets/src/mage/cards/a/AncientAnimus.java +++ b/Mage.Sets/src/mage/cards/a/AncientAnimus.java @@ -33,7 +33,8 @@ public final class AncientAnimus extends CardImpl { effect.setText("Put a +1/+1 counter on target creature you control if it's legendary"); this.getSpellAbility().addEffect(effect); effect = new FightTargetsEffect(); - effect.setText("Then it fights target creature an opponent controls"); + effect.setText("Then it fights target creature an opponent controls. " + + "(Each deals damage equal to its power to the other.)"); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); Target target = new TargetOpponentsCreaturePermanent(); diff --git a/Mage.Sets/src/mage/cards/a/AncientLumberknot.java b/Mage.Sets/src/mage/cards/a/AncientLumberknot.java new file mode 100644 index 00000000000..cd944672aac --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AncientLumberknot.java @@ -0,0 +1,57 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.ruleModifying.CombatDamageByToughnessEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicate; +import mage.game.Game; +import mage.game.permanent.Permanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AncientLumberknot extends CardImpl { + + private static final FilterCreaturePermanent filter + = new FilterCreaturePermanent("creature you control with toughness greater than its power"); + + static { + filter.add(AncientLumberknotPredicate.instance); + } + + public AncientLumberknot(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{G}"); + + this.subtype.add(SubType.TREEFOLK); + this.power = new MageInt(1); + this.toughness = new MageInt(4); + + // Each creature you control with toughness greater than its power assigns combat damage equal to its toughness rather than its power. + this.addAbility(new SimpleStaticAbility(new CombatDamageByToughnessEffect(filter, true))); + } + + private AncientLumberknot(final AncientLumberknot card) { + super(card); + } + + @Override + public AncientLumberknot copy() { + return new AncientLumberknot(this); + } +} + +enum AncientLumberknotPredicate implements Predicate { + instance; + + @Override + public boolean apply(Permanent input, Game game) { + return input.getToughness().getValue() > input.getPower().getValue(); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AngelOfDeliverance.java b/Mage.Sets/src/mage/cards/a/AngelOfDeliverance.java index b8d4e7123a2..9fc7e16d914 100644 --- a/Mage.Sets/src/mage/cards/a/AngelOfDeliverance.java +++ b/Mage.Sets/src/mage/cards/a/AngelOfDeliverance.java @@ -15,9 +15,8 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent; import mage.target.common.TargetCreaturePermanent; @@ -27,12 +26,6 @@ import mage.target.common.TargetCreaturePermanent; */ public final class AngelOfDeliverance extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public AngelOfDeliverance(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{6}{W}{W}"); this.subtype.add(SubType.ANGEL); @@ -49,7 +42,7 @@ public final class AngelOfDeliverance extends CardImpl { DeliriumCondition.instance, "Delirium — Whenever {this} deals damage, if there are four or more card types among cards in your graveyard, exile target creature an opponent controls" ); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); ability.addHint(CardTypesInGraveyardHint.YOU); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/a/AngelOfFlightAlabaster.java b/Mage.Sets/src/mage/cards/a/AngelOfFlightAlabaster.java index 82147b452b7..9234c9ebc47 100644 --- a/Mage.Sets/src/mage/cards/a/AngelOfFlightAlabaster.java +++ b/Mage.Sets/src/mage/cards/a/AngelOfFlightAlabaster.java @@ -1,10 +1,9 @@ package mage.cards.a; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -15,7 +14,10 @@ import mage.filter.FilterCard; import mage.target.Target; import mage.target.common.TargetCardInYourGraveyard; +import java.util.UUID; + public final class AngelOfFlightAlabaster extends CardImpl { + private static final FilterCard filter = new FilterCard("Spirit card from your graveyard"); static { @@ -23,19 +25,18 @@ public final class AngelOfFlightAlabaster extends CardImpl { } public AngelOfFlightAlabaster(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{W}"); this.subtype.add(SubType.ANGEL); this.power = new MageInt(4); this.toughness = new MageInt(4); this.addAbility(FlyingAbility.getInstance()); - Ability ability = new BeginningOfUpkeepTriggeredAbility(new ReturnToHandTargetEffect(), TargetController.YOU, false); - Target target = new TargetCardInYourGraveyard(filter); - ability.addTarget(target); + Ability ability = new BeginningOfUpkeepTriggeredAbility( + new ReturnFromGraveyardToHandTargetEffect(), TargetController.YOU, false + ); + ability.addTarget(new TargetCardInYourGraveyard(filter)); this.addAbility(ability); - - } private AngelOfFlightAlabaster(final AngelOfFlightAlabaster card) { diff --git a/Mage.Sets/src/mage/cards/a/AngelOfJubilation.java b/Mage.Sets/src/mage/cards/a/AngelOfJubilation.java index aec07cb3d91..6dd7137114c 100644 --- a/Mage.Sets/src/mage/cards/a/AngelOfJubilation.java +++ b/Mage.Sets/src/mage/cards/a/AngelOfJubilation.java @@ -1,7 +1,6 @@ package mage.cards.a; import mage.MageInt; -import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.SpellAbility; import mage.abilities.common.SimpleStaticAbility; @@ -15,9 +14,9 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.Filter; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ColorPredicate; import mage.game.Game; import mage.players.Player; @@ -28,12 +27,6 @@ import java.util.UUID; */ public final class AngelOfJubilation extends CardImpl { - private static final FilterCreaturePermanent filterNonBlack = new FilterCreaturePermanent("nonblack creatures"); - - static { - filterNonBlack.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public AngelOfJubilation(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{W}{W}"); this.subtype.add(SubType.ANGEL); @@ -44,7 +37,7 @@ public final class AngelOfJubilation extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Other nonblack creatures you control get +1/+1. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield, filterNonBlack, true))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURES_NON_BLACK, true))); // Players can't pay life or sacrifice creatures to cast spells or activate abilities. Ability ability = new SimpleStaticAbility(new AngelOfJubilationEffect()); diff --git a/Mage.Sets/src/mage/cards/a/AngelOfTheDireHour.java b/Mage.Sets/src/mage/cards/a/AngelOfTheDireHour.java index 1d44db8cb4e..9f8899d0ea8 100644 --- a/Mage.Sets/src/mage/cards/a/AngelOfTheDireHour.java +++ b/Mage.Sets/src/mage/cards/a/AngelOfTheDireHour.java @@ -13,7 +13,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.filter.common.FilterAttackingCreature; +import mage.filter.StaticFilters; import mage.watchers.common.CastFromHandWatcher; /** @@ -35,7 +35,7 @@ public final class AngelOfTheDireHour extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Angel of the Dire Hour enters the battlefield, if you cast it from your hand, exile all attacking creatures. this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility(new ExileAllEffect(new FilterAttackingCreature("attacking creatures")), false), + new EntersBattlefieldTriggeredAbility(new ExileAllEffect(StaticFilters.FILTER_ATTACKING_CREATURES), false), CastFromHandSourcePermanentCondition.instance, "When {this} enters the battlefield, if you cast it from your hand, exile all attacking creatures."), new CastFromHandWatcher()); diff --git a/Mage.Sets/src/mage/cards/a/AngelicBenediction.java b/Mage.Sets/src/mage/cards/a/AngelicBenediction.java index bc5ebe12f62..3d395c758fc 100644 --- a/Mage.Sets/src/mage/cards/a/AngelicBenediction.java +++ b/Mage.Sets/src/mage/cards/a/AngelicBenediction.java @@ -1,32 +1,33 @@ - package mage.cards.a; -import java.util.UUID; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.Ability; +import mage.abilities.common.AttacksAloneControlledTriggeredAbility; import mage.abilities.effects.common.TapTargetEffect; import mage.abilities.keyword.ExaltedAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author North */ public final class AngelicBenediction extends CardImpl { public AngelicBenediction(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{3}{W}"); - + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{W}"); + // Exalted this.addAbility(new ExaltedAbility()); + // Whenever a creature you control attacks alone, you may tap target creature. - this.addAbility(new AngelicBenedictionTriggeredAbility()); + Ability ability = new AttacksAloneControlledTriggeredAbility( + new TapTargetEffect(), false, true + ); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); } private AngelicBenediction(final AngelicBenediction card) { @@ -38,40 +39,3 @@ public final class AngelicBenediction extends CardImpl { return new AngelicBenediction(this); } } - -class AngelicBenedictionTriggeredAbility extends TriggeredAbilityImpl { - - public AngelicBenedictionTriggeredAbility() { - super(Zone.BATTLEFIELD, new TapTargetEffect(), true); - this.addTarget(new TargetCreaturePermanent()); - } - - public AngelicBenedictionTriggeredAbility(final AngelicBenedictionTriggeredAbility ability) { - super(ability); - } - - @Override - public AngelicBenedictionTriggeredAbility copy() { - return new AngelicBenedictionTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DECLARED_ATTACKERS; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (game.isActivePlayer(this.controllerId)) { - if (game.getCombat().attacksAlone()) { - return true; - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever a creature you control attacks alone, you may tap target creature"; - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/a/AngelicCaptain.java b/Mage.Sets/src/mage/cards/a/AngelicCaptain.java index d9f8f3f4e39..d0bb534df8d 100644 --- a/Mage.Sets/src/mage/cards/a/AngelicCaptain.java +++ b/Mage.Sets/src/mage/cards/a/AngelicCaptain.java @@ -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.effects.common.continuous.BoostSourceEffect; import mage.abilities.keyword.FlyingAbility; @@ -28,6 +28,8 @@ public final class AngelicCaptain extends CardImpl { filter.add(AnotherPredicate.instance); } + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter); + public AngelicCaptain(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{R}{W}"); this.subtype.add(SubType.ANGEL); @@ -39,8 +41,7 @@ public final class AngelicCaptain extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever Angelic Captain attacks, it gets +1/+1 until end of turn for each other attacking Ally. - PermanentsOnBattlefieldCount value = new PermanentsOnBattlefieldCount(filter); - this.addAbility(new AttacksTriggeredAbility(new BoostSourceEffect(value, value, Duration.EndOfTurn, true), false)); + this.addAbility(new AttacksTriggeredAbility(new BoostSourceEffect(xValue, xValue, Duration.EndOfTurn, true, "it"), false)); } private AngelicCaptain(final AngelicCaptain card) { diff --git a/Mage.Sets/src/mage/cards/a/AngelicEnforcer.java b/Mage.Sets/src/mage/cards/a/AngelicEnforcer.java new file mode 100644 index 00000000000..a1b545ee4c6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AngelicEnforcer.java @@ -0,0 +1,60 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.common.ControllerLifeCount; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.continuous.GainAbilityControllerEffect; +import mage.abilities.effects.common.continuous.SetPowerToughnessSourceEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.HexproofAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.Zone; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AngelicEnforcer extends CardImpl { + + public AngelicEnforcer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); + + this.subtype.add(SubType.ANGEL); + this.power = new MageInt(0); + this.toughness = new MageInt(0); + this.color.setWhite(true); + this.nightCard = true; + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // You have hexproof. + this.addAbility(new SimpleStaticAbility(new GainAbilityControllerEffect(HexproofAbility.getInstance()))); + + // Angelic Enforcer's power and toughness are each equal to your life total. + this.addAbility(new SimpleStaticAbility(Zone.ALL, new SetPowerToughnessSourceEffect( + ControllerLifeCount.instance, Duration.EndOfGame + ).setText("{this}'s power and toughness are each equal to your life total"))); + + // Whenever Angelic Enforcer attacks, double your life total. + this.addAbility(new AttacksTriggeredAbility(new GainLifeEffect( + ControllerLifeCount.instance + ).setText("double your life total"))); + } + + private AngelicEnforcer(final AngelicEnforcer card) { + super(card); + } + + @Override + public AngelicEnforcer copy() { + return new AngelicEnforcer(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AngelicExaltation.java b/Mage.Sets/src/mage/cards/a/AngelicExaltation.java index df3b3f66f8b..ef1494214ff 100644 --- a/Mage.Sets/src/mage/cards/a/AngelicExaltation.java +++ b/Mage.Sets/src/mage/cards/a/AngelicExaltation.java @@ -1,18 +1,13 @@ package mage.cards.a; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.AttacksAloneControlledTriggeredAbility; import mage.abilities.dynamicvalue.common.CreaturesYouControlCount; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.abilities.hint.common.CreaturesYouControlHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.target.targetpointer.FixedTarget; import java.util.UUID; @@ -25,7 +20,11 @@ public final class AngelicExaltation extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{W}"); // Whenever a creature you control attacks alone, it gets +X/+X until end of turn, where X is the number of creatures you control. - this.addAbility(new AngelicExaltationAbility().addHint(CreaturesYouControlHint.instance)); + this.addAbility(new AttacksAloneControlledTriggeredAbility(new BoostTargetEffect( + CreaturesYouControlCount.instance, + CreaturesYouControlCount.instance, + Duration.EndOfTurn, true + ).setText("it gets +X/+X until end of turn, where X is the number of creatures you control")).addHint(CreaturesYouControlHint.instance)); } private AngelicExaltation(final AngelicExaltation card) { @@ -37,45 +36,3 @@ public final class AngelicExaltation extends CardImpl { return new AngelicExaltation(this); } } - -class AngelicExaltationAbility extends TriggeredAbilityImpl { - - public AngelicExaltationAbility() { - super(Zone.BATTLEFIELD, new BoostTargetEffect(CreaturesYouControlCount.instance, CreaturesYouControlCount.instance, Duration.EndOfTurn, true), false); - } - - public AngelicExaltationAbility(final AngelicExaltationAbility ability) { - super(ability); - } - - @Override - public AngelicExaltationAbility copy() { - return new AngelicExaltationAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DECLARED_ATTACKERS; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (game.isActivePlayer(this.controllerId)) { - if (game.getCombat().attacksAlone()) { - for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(game.getCombat().getAttackers().get(0))); - } - return true; - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever a creature you control attacks alone, " + - "it gets +X/+X until end of turn, " + - "where X is the number of creatures you control."; - } - -} diff --git a/Mage.Sets/src/mage/cards/a/AngelicQuartermaster.java b/Mage.Sets/src/mage/cards/a/AngelicQuartermaster.java new file mode 100644 index 00000000000..15524708c6b --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AngelicQuartermaster.java @@ -0,0 +1,49 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AngelicQuartermaster extends CardImpl { + + public AngelicQuartermaster(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}{W}"); + + this.subtype.add(SubType.ANGEL); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When Angelic Quartermaster enters the battlefield, put a +1/+1 counter on each of up to two other target creatures. + Ability ability = new EntersBattlefieldTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance()) + .setText("put a +1/+1 counter on each of up to two other target creatures")); + ability.addTarget(new TargetPermanent(0, 2, StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE)); + this.addAbility(ability); + } + + private AngelicQuartermaster(final AngelicQuartermaster card) { + super(card); + } + + @Override + public AngelicQuartermaster copy() { + return new AngelicQuartermaster(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AngelicRenewal.java b/Mage.Sets/src/mage/cards/a/AngelicRenewal.java index 89cbd6c1c6f..4697d6e5158 100644 --- a/Mage.Sets/src/mage/cards/a/AngelicRenewal.java +++ b/Mage.Sets/src/mage/cards/a/AngelicRenewal.java @@ -7,7 +7,7 @@ import mage.abilities.effects.common.ReturnToBattlefieldUnderOwnerControlTargetE import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import java.util.UUID; @@ -22,7 +22,7 @@ public final class AngelicRenewal extends CardImpl { // Whenever a creature is put into your graveyard from the battlefield, you may sacrifice Angelic Renewal. If you do, return that card to the battlefield. this.addAbility(new PutIntoGraveFromBattlefieldAllTriggeredAbility(new DoIfCostPaid( new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false), new SacrificeSourceCost()), false, - new FilterCreaturePermanent("a creature"), true, true)); + StaticFilters.FILTER_PERMANENT_A_CREATURE, true, true)); } private AngelicRenewal(final AngelicRenewal card) { diff --git a/Mage.Sets/src/mage/cards/a/AngelsTomb.java b/Mage.Sets/src/mage/cards/a/AngelsTomb.java index 9e73e17b2f2..61720dc68cc 100644 --- a/Mage.Sets/src/mage/cards/a/AngelsTomb.java +++ b/Mage.Sets/src/mage/cards/a/AngelsTomb.java @@ -34,7 +34,7 @@ public final class AngelsTomb extends CardImpl { this.addAbility(new EntersBattlefieldControlledTriggeredAbility( Zone.BATTLEFIELD, effect, - StaticFilters.FILTER_PERMANENT_CREATURE_A, + StaticFilters.FILTER_PERMANENT_A_CREATURE, true) ); } diff --git a/Mage.Sets/src/mage/cards/a/AngrathCaptainOfChaos.java b/Mage.Sets/src/mage/cards/a/AngrathCaptainOfChaos.java index 9827fa66844..57c302b73f5 100644 --- a/Mage.Sets/src/mage/cards/a/AngrathCaptainOfChaos.java +++ b/Mage.Sets/src/mage/cards/a/AngrathCaptainOfChaos.java @@ -1,7 +1,6 @@ package mage.cards.a; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.abilities.effects.keyword.AmassEffect; @@ -26,7 +25,7 @@ public final class AngrathCaptainOfChaos extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.ANGRATH); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // Creatures you control have menace. this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( diff --git a/Mage.Sets/src/mage/cards/a/AngrathMinotaurPirate.java b/Mage.Sets/src/mage/cards/a/AngrathMinotaurPirate.java index 69a0669e360..33a1590efc4 100644 --- a/Mage.Sets/src/mage/cards/a/AngrathMinotaurPirate.java +++ b/Mage.Sets/src/mage/cards/a/AngrathMinotaurPirate.java @@ -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.Effects; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageAllControlledTargetEffect; @@ -37,7 +36,7 @@ public final class AngrathMinotaurPirate extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.ANGRATH); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +2: Angrath, Minotaur Pirate deals 1 damage to target opponent and each creature that player controls. Effects effects1 = new Effects(); diff --git a/Mage.Sets/src/mage/cards/a/AngrathTheFlameChained.java b/Mage.Sets/src/mage/cards/a/AngrathTheFlameChained.java index 294fdd7e7aa..b9de629fde2 100644 --- a/Mage.Sets/src/mage/cards/a/AngrathTheFlameChained.java +++ b/Mage.Sets/src/mage/cards/a/AngrathTheFlameChained.java @@ -5,7 +5,6 @@ import java.util.UUID; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.LoseLifeOpponentsEffect; @@ -41,7 +40,7 @@ public final class AngrathTheFlameChained extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.ANGRATH); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Each opponent discards a card and loses 2 life. LoyaltyAbility ability = new LoyaltyAbility(new DiscardEachPlayerEffect(TargetController.OPPONENT), 1); diff --git a/Mage.Sets/src/mage/cards/a/AngrathsRampage.java b/Mage.Sets/src/mage/cards/a/AngrathsRampage.java index b97ee95945c..7730c060ae5 100644 --- a/Mage.Sets/src/mage/cards/a/AngrathsRampage.java +++ b/Mage.Sets/src/mage/cards/a/AngrathsRampage.java @@ -21,7 +21,7 @@ public final class AngrathsRampage extends CardImpl { // Choose one: // • Target player sacrifices an artifact. this.getSpellAbility().addEffect(new SacrificeEffect( - StaticFilters.FILTER_PERMANENT_ARTIFACT, + StaticFilters.FILTER_PERMANENT_ARTIFACT_AN, 1, "Target player" )); this.getSpellAbility().addTarget(new TargetPlayer()); diff --git a/Mage.Sets/src/mage/cards/a/AnimateDead.java b/Mage.Sets/src/mage/cards/a/AnimateDead.java index 8094e7da870..7398061be83 100644 --- a/Mage.Sets/src/mage/cards/a/AnimateDead.java +++ b/Mage.Sets/src/mage/cards/a/AnimateDead.java @@ -94,7 +94,7 @@ class AnimateDeadReAttachEffect extends OneShotEffect { if (controller != null && animateDead != null) { Card cardInGraveyard = game.getCard(animateDead.getAttachedTo()); - if (cardInGraveyard == null) { + if (cardInGraveyard == null || game.getState().getZone(cardInGraveyard.getId()) != Zone.GRAVEYARD) { return true; } // put card into play from Graveyard @@ -105,6 +105,7 @@ class AnimateDeadReAttachEffect extends OneShotEffect { FilterCreaturePermanent filter = new FilterCreaturePermanent("enchant creature put onto the battlefield with Animate Dead"); filter.add(new PermanentIdPredicate(cardInGraveyard.getId())); Target target = new TargetCreaturePermanent(filter); + target.setNotTarget(true); // Bug #7772 target.addTarget(enchantedCreature.getId(), source, game); animateDead.getSpellAbility().getTargets().clear(); animateDead.getSpellAbility().getTargets().add(target); @@ -225,6 +226,7 @@ class AnimateDeadChangeAbilityEffect extends ContinuousEffectImpl implements Sou for (Ability ability : permanent.getAbilities()) { if (ability instanceof EnchantAbility) { abilityToRemove = ability; + ability.getTargets().clear(); } } permanent.removeAbility(abilityToRemove, source.getSourceId(), game); @@ -258,6 +260,7 @@ class AnimateDeadAttachToPermanentEffect extends ContinuousEffectImpl { FilterCreaturePermanent filter = new FilterCreaturePermanent("enchant creature put onto the battlefield with Animate Dead"); filter.add(new PermanentIdPredicate(getTargetPointer().getFirst(game, source))); Target target = new TargetCreaturePermanent(filter); + target.setNotTarget(true); // Bug #7772 target.addTarget(((FixedTarget) getTargetPointer()).getTarget(), source, game); animateDead.getSpellAbility().getTargets().clear(); animateDead.getSpellAbility().getTargets().add(target); diff --git a/Mage.Sets/src/mage/cards/a/AnimusOfNightsReach.java b/Mage.Sets/src/mage/cards/a/AnimusOfNightsReach.java new file mode 100644 index 00000000000..fae6c4995c3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AnimusOfNightsReach.java @@ -0,0 +1,92 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.CardsInAllGraveyardsCount; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.hint.Hint; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.card.DefendingPlayerOwnsCardPredicate; +import mage.game.Game; + +import java.util.Objects; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class AnimusOfNightsReach extends CardImpl { + + private static final FilterCard filter + = new FilterCreatureCard("creature cards in defending player's graveyard"); + + static { + filter.add(DefendingPlayerOwnsCardPredicate.instance); + } + + private static final DynamicValue xValue = new CardsInAllGraveyardsCount(filter); + + public AnimusOfNightsReach(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, ""); + + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(0); + this.toughness = new MageInt(4); + this.color.setBlack(true); + this.nightCard = true; + + // Menace + this.addAbility(new MenaceAbility()); + + // Whenever Animus of Night's Reach attacks, it gets +X/+0 until end of turn, where X is the number of creature cards in defending player's graveyard. + this.addAbility(new AttacksTriggeredAbility(new BoostSourceEffect( + xValue, StaticValue.get(0), Duration.EndOfTurn, true + ).setText("it gets +X/+0 until end of turn, where X is the number of creature cards in defending player's graveyard")).addHint(AnimusOfNightsReachHint.instance)); + } + + private AnimusOfNightsReach(final AnimusOfNightsReach card) { + super(card); + } + + @Override + public AnimusOfNightsReach copy() { + return new AnimusOfNightsReach(this); + } +} + +enum AnimusOfNightsReachHint implements Hint { + instance; + + @Override + public String getText(Game game, Ability ability) { + return "Cards in each opponent's graveyard:
" + + game + .getOpponents(ability.getControllerId()) + .stream() + .map(game::getPlayer) + .filter(Objects::nonNull) + .map(player -> player + .getName() + + ": " + player + .getGraveyard() + .count(StaticFilters.FILTER_CARD_CREATURE, game)) + .collect(Collectors.joining("
")); + } + + @Override + public AnimusOfNightsReachHint copy() { + return instance; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/a/AnjeMaidOfDishonor.java b/Mage.Sets/src/mage/cards/a/AnjeMaidOfDishonor.java new file mode 100644 index 00000000000..60992503a43 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AnjeMaidOfDishonor.java @@ -0,0 +1,120 @@ +package mage.cards.a; + +import java.util.UUID; +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.LoseLifeOpponentsEffect; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.permanent.PermanentToken; +import mage.game.permanent.token.BloodToken; + +/** + * + * @author weirddan455 + */ +public final class AnjeMaidOfDishonor extends CardImpl { + + private static final FilterControlledPermanent filter = new FilterControlledPermanent("another creature or a Blood token"); + + static { + filter.add(AnjeMaidOfDishonorPredicate.instance); + } + + public AnjeMaidOfDishonor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{R}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.VAMPIRE); + this.power = new MageInt(4); + this.toughness = new MageInt(5); + + // Whenever Anje, Maid of Dishonor and/or one or more other Vampires enter the battlefield under your control, create a Blood token. + // This ability triggers only once each turn. + this.addAbility(new AnjeMaidOfDishonorTriggeredAbility()); + + // {2}, Sacrifice another creature or a Blood token: Each opponent loses 2 life and you gain 2 life. + Ability ability = new SimpleActivatedAbility(new LoseLifeOpponentsEffect(2), new GenericManaCost(2)); + ability.addCost(new SacrificeTargetCost(filter)); + ability.addEffect(new GainLifeEffect(2).concatBy("and")); + this.addAbility(ability); + } + + private AnjeMaidOfDishonor(final AnjeMaidOfDishonor card) { + super(card); + } + + @Override + public AnjeMaidOfDishonor copy() { + return new AnjeMaidOfDishonor(this); + } +} + +class AnjeMaidOfDishonorTriggeredAbility extends TriggeredAbilityImpl { + + public AnjeMaidOfDishonorTriggeredAbility() { + super(Zone.BATTLEFIELD, new CreateTokenEffect(new BloodToken())); + this.setTriggersOnce(true); + } + + private AnjeMaidOfDishonorTriggeredAbility(final AnjeMaidOfDishonorTriggeredAbility ability) { + super(ability); + } + + @Override + public AnjeMaidOfDishonorTriggeredAbility copy() { + return new AnjeMaidOfDishonorTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent != null && permanent.isControlledBy(controllerId)) { + return permanent.getId().equals(sourceId) || permanent.hasSubtype(SubType.VAMPIRE, game); + } + return false; + } + + @Override + public String getTriggerPhrase() { + return "Whenever {this} and/or one or more other Vampires enter the battlefield under your control, "; + } +} + +enum AnjeMaidOfDishonorPredicate implements ObjectSourcePlayerPredicate { + instance; + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + MageObject object = input.getObject(); + if (object instanceof PermanentToken && object.hasSubtype(SubType.BLOOD, game)) { + return true; + } + if (!object.getId().equals(input.getSourceId())) { + return object.isCreature(game); + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/a/Annihilate.java b/Mage.Sets/src/mage/cards/a/Annihilate.java index fdebe6b6442..98130153029 100644 --- a/Mage.Sets/src/mage/cards/a/Annihilate.java +++ b/Mage.Sets/src/mage/cards/a/Annihilate.java @@ -1,16 +1,12 @@ - package mage.cards.a; import java.util.UUID; -import mage.ObjectColor; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; 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; /** @@ -19,18 +15,12 @@ import mage.target.common.TargetCreaturePermanent; */ public final class Annihilate extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public Annihilate(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{3}{B}{B}"); - // Destroy target nonblack creature. It can't be regenerated. this.getSpellAbility().addEffect(new DestroyTargetEffect(true)); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); // Draw a card. this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); } diff --git a/Mage.Sets/src/mage/cards/a/AnsweredPrayers.java b/Mage.Sets/src/mage/cards/a/AnsweredPrayers.java index da33f97d183..2ecac4c2238 100644 --- a/Mage.Sets/src/mage/cards/a/AnsweredPrayers.java +++ b/Mage.Sets/src/mage/cards/a/AnsweredPrayers.java @@ -30,7 +30,7 @@ public final class AnsweredPrayers extends CardImpl { // Whenever a creature enters the battlefield under your control, you gain 1 life. If Answered Prayers isn't a creature, it becomes a 3/3 Angel creature with flying in addition to its other types until end of turn. this.addAbility(new EntersBattlefieldControlledTriggeredAbility( - new AnsweredPrayersEffect(), StaticFilters.FILTER_PERMANENT_CREATURE_A + new AnsweredPrayersEffect(), StaticFilters.FILTER_PERMANENT_A_CREATURE )); } diff --git a/Mage.Sets/src/mage/cards/a/AoTheDawnSky.java b/Mage.Sets/src/mage/cards/a/AoTheDawnSky.java new file mode 100644 index 00000000000..67953334731 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AoTheDawnSky.java @@ -0,0 +1,148 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.counter.AddCountersAllEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.*; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.FilterCard; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.common.FilterPermanentCard; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInLibrary; + +import java.util.Objects; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AoTheDawnSky extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledPermanent("permanent you control that's a creature or Vehicle"); + + static { + filter.add(Predicates.or( + CardType.CREATURE.getPredicate(), + SubType.VEHICLE.getPredicate() + )); + } + + public AoTheDawnSky(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.DRAGON); + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(5); + this.toughness = new MageInt(4); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // When Ao, the Dawn Sky dies, choose one — + // • Look at the top seven cards of your library. Put any number of nonland permanent cards with total mana value 4 or less from among them onto the battlefield. Put the rest on the bottom of your library in a random order. + Ability ability = new DiesSourceTriggeredAbility(new AoTheDawnSkyEffect()); + + // • Put two +1/+1 counters on each permanent you control that's a creature or Vehicle. + ability.addMode(new Mode(new AddCountersAllEffect(CounterType.P1P1.createInstance(2), filter))); + this.addAbility(ability); + } + + private AoTheDawnSky(final AoTheDawnSky card) { + super(card); + } + + @Override + public AoTheDawnSky copy() { + return new AoTheDawnSky(this); + } +} + +class AoTheDawnSkyEffect extends OneShotEffect { + + AoTheDawnSkyEffect() { + super(Outcome.Benefit); + staticText = "look at the top seven cards of your library. Put any number of nonland permanent cards " + + "with total mana value 4 or less from among them onto the battlefield. " + + "Put the rest on the bottom of your library in a random order"; + } + + private AoTheDawnSkyEffect(final AoTheDawnSkyEffect effect) { + super(effect); + } + + @Override + public AoTheDawnSkyEffect copy() { + return new AoTheDawnSkyEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Cards cards = new CardsImpl(player.getLibrary().getTopCards(game, 7)); + TargetCard target = new AoTheDawnSkyTarget(); + player.choose(outcome, cards, target, game); + player.moveCards(new CardsImpl(target.getTargets()), Zone.BATTLEFIELD, source, game); + cards.retainZone(Zone.LIBRARY, game); + player.putCardsOnBottomOfLibrary(cards, game, source, false); + return true; + } +} + +class AoTheDawnSkyTarget extends TargetCardInLibrary { + + private static final FilterCard filter = new FilterPermanentCard("nonland permanent card"); + + static { + filter.add(Predicates.not(CardType.LAND.getPredicate())); + } + + AoTheDawnSkyTarget() { + super(0, Integer.MAX_VALUE, filter); + } + + private AoTheDawnSkyTarget(final AoTheDawnSkyTarget target) { + super(target); + } + + @Override + public AoTheDawnSkyTarget copy() { + return new AoTheDawnSkyTarget(this); + } + + @Override + public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) { + if (!super.canTarget(playerId, id, source, game)) { + return false; + } + Card card = game.getCard(id); + return card != null + && card.getManaValue() + + this + .getTargets() + .stream() + .map(game::getCard) + .filter(Objects::nonNull) + .mapToInt(MageObject::getManaValue) + .sum() <= 4; + } +} diff --git a/Mage.Sets/src/mage/cards/a/ApprenticeSharpshooter.java b/Mage.Sets/src/mage/cards/a/ApprenticeSharpshooter.java new file mode 100644 index 00000000000..ba400721286 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/ApprenticeSharpshooter.java @@ -0,0 +1,41 @@ +package mage.cards.a; + +import java.util.UUID; +import mage.MageInt; +import mage.constants.SubType; +import mage.abilities.keyword.ReachAbility; +import mage.abilities.keyword.TrainingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; + +/** + * + * @author TheElk801 + */ +public final class ApprenticeSharpshooter extends CardImpl { + + public ApprenticeSharpshooter(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ARCHER); + this.power = new MageInt(1); + this.toughness = new MageInt(4); + + // Reach + this.addAbility(ReachAbility.getInstance()); + + // Training + this.addAbility(new TrainingAbility()); + } + + private ApprenticeSharpshooter(final ApprenticeSharpshooter card) { + super(card); + } + + @Override + public ApprenticeSharpshooter copy() { + return new ApprenticeSharpshooter(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AquastrandSpider.java b/Mage.Sets/src/mage/cards/a/AquastrandSpider.java index ea6bf9be47a..e97e5d76767 100644 --- a/Mage.Sets/src/mage/cards/a/AquastrandSpider.java +++ b/Mage.Sets/src/mage/cards/a/AquastrandSpider.java @@ -15,8 +15,7 @@ import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.Zone; -import mage.counters.CounterType; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; /** @@ -24,14 +23,7 @@ import mage.target.common.TargetCreaturePermanent; * @author JotaPeRL */ public final class AquastrandSpider extends CardImpl { - - private static final FilterCreaturePermanent filter - = new FilterCreaturePermanent("creature with a +1/+1 counter on it"); - static { - filter.add(CounterType.P1P1.getPredicate()); - } - public AquastrandSpider(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); this.subtype.add(SubType.SPIDER); @@ -46,7 +38,7 @@ public final class AquastrandSpider extends CardImpl { Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainAbilityTargetEffect(ReachAbility.getInstance(), Duration.EndOfTurn), new ManaCostsImpl("{G}")); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_P1P1)); this.addAbility(ability.addCustomOutcome(Outcome.Benefit)); } diff --git a/Mage.Sets/src/mage/cards/a/AquitectsWill.java b/Mage.Sets/src/mage/cards/a/AquitectsWill.java index 2076f905aba..c1f9df1e485 100644 --- a/Mage.Sets/src/mage/cards/a/AquitectsWill.java +++ b/Mage.Sets/src/mage/cards/a/AquitectsWill.java @@ -4,12 +4,9 @@ import mage.abilities.Ability; import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.decorator.ConditionalOneShotEffect; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.ContinuousEffectImpl; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.continuous.BecomesBasicLandTargetEffect; import mage.abilities.effects.common.counter.AddCountersTargetEffect; -import mage.abilities.mana.BlueManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -19,10 +16,7 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetLandPermanent; -import java.util.List; -import java.util.Set; import java.util.UUID; -import java.util.stream.Collectors; /** * @author ilcartographer @@ -60,10 +54,10 @@ public final class AquitectsWill extends CardImpl { } } -class AquitectsWillEffect extends ContinuousEffectImpl { +class AquitectsWillEffect extends BecomesBasicLandTargetEffect { AquitectsWillEffect() { - super(Duration.EndOfGame, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Benefit); + super(Duration.Custom, false, false, SubType.ISLAND); staticText = "That land is an Island in addition to its other types for as long as it has a flood counter on it"; } @@ -79,25 +73,10 @@ class AquitectsWillEffect extends ContinuousEffectImpl { @Override public boolean apply(Game game, Ability source) { Permanent land = game.getPermanent(this.targetPointer.getFirst(game, source)); - if (land == null - || land.getCounters(game).getCount(CounterType.FLOOD) < 1) { + if (land == null || land.getCounters(game).getCount(CounterType.FLOOD) < 1) { discard(); return false; } - // The land is an island intrinsically so the ability is added at layer 4, not layer 6 - land.addSubType(game, SubType.ISLAND); - if (!land.getAbilities(game).containsClass(BlueManaAbility.class)) { - land.addAbility(new BlueManaAbility(), source.getSourceId(), game); - } - return true; - } - - @Override - public Set isDependentTo(List allEffectsInLayer) { - return allEffectsInLayer - .stream() - .filter(effect -> effect.getDependencyTypes().contains(DependencyType.BecomeIsland)) - .map(Effect::getId) - .collect(Collectors.toSet()); + return super.apply(game, source); } } diff --git a/Mage.Sets/src/mage/cards/a/AradaraExpress.java b/Mage.Sets/src/mage/cards/a/AradaraExpress.java index 4e72beb0644..1ac94290c32 100644 --- a/Mage.Sets/src/mage/cards/a/AradaraExpress.java +++ b/Mage.Sets/src/mage/cards/a/AradaraExpress.java @@ -23,7 +23,7 @@ public final class AradaraExpress extends CardImpl { this.toughness = new MageInt(6); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // Crew 4 this.addAbility(new CrewAbility(4)); } diff --git a/Mage.Sets/src/mage/cards/a/ArahboRoarOfTheWorld.java b/Mage.Sets/src/mage/cards/a/ArahboRoarOfTheWorld.java index 75b677d4e3b..a7feb5b51c9 100644 --- a/Mage.Sets/src/mage/cards/a/ArahboRoarOfTheWorld.java +++ b/Mage.Sets/src/mage/cards/a/ArahboRoarOfTheWorld.java @@ -57,7 +57,7 @@ public final class ArahboRoarOfTheWorld extends CardImpl { Ability ability = new ConditionalInterveningIfTriggeredAbility( new BeginningOfCombatTriggeredAbility(Zone.ALL, new BoostTargetEffect(3, 3, Duration.EndOfTurn), TargetController.YOU, false, false), SourceOnBattlefieldOrCommandZoneCondition.instance, - "Eminence — At the beginning of combat on your turn, if Arahbo, Roar of the World is in the command zone or on the battlefield, another target Cat you control gets +3/+3 until end of turn."); + "At the beginning of combat on your turn, if {this} is in the command zone or on the battlefield, another target Cat you control gets +3/+3 until end of turn."); ability.addTarget(new TargetCreaturePermanent(filter)); ability.setAbilityWord(AbilityWord.EMINENCE); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/a/ArcLightning.java b/Mage.Sets/src/mage/cards/a/ArcLightning.java index 213abfc53e7..d27a09fd71d 100644 --- a/Mage.Sets/src/mage/cards/a/ArcLightning.java +++ b/Mage.Sets/src/mage/cards/a/ArcLightning.java @@ -1,5 +1,3 @@ - - package mage.cards.a; import java.util.UUID; @@ -18,7 +16,7 @@ public final class ArcLightning extends CardImpl { public ArcLightning(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{R}"); - // Arc Lightning deals 3 damage divided as you choose among one, two, or three target creatures and/or players. + // Arc Lightning deals 3 damage divided as you choose among one, two, or three targets. this.getSpellAbility().addEffect(new DamageMultiEffect(3)); this.getSpellAbility().addTarget(new TargetAnyTargetAmount(3)); } @@ -31,4 +29,4 @@ public final class ArcLightning extends CardImpl { public ArcLightning copy() { return new ArcLightning(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/a/ArcMage.java b/Mage.Sets/src/mage/cards/a/ArcMage.java index 437e80e40b5..40125002e9b 100644 --- a/Mage.Sets/src/mage/cards/a/ArcMage.java +++ b/Mage.Sets/src/mage/cards/a/ArcMage.java @@ -1,4 +1,3 @@ - package mage.cards.a; import java.util.UUID; @@ -8,7 +7,6 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.DiscardCardCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.DamageMultiEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -30,10 +28,8 @@ public final class ArcMage extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); - // {2}{R}, {tap}, Discard a card: Arc Mage deals 2 damage divided as you choose among one or two target creatures and/or players. - Effect effect = new DamageMultiEffect(2); - effect.setText("{this} deals 2 damage divided as you choose among one or two target creatures and/or players"); - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl("{2}{R}")); + // {2}{R}, {tap}, Discard a card: Arc Mage deals 2 damage divided as you choose among one or two targets. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageMultiEffect(2), new ManaCostsImpl("{2}{R}")); ability.addCost(new TapSourceCost()); ability.addCost(new DiscardCardCost()); ability.addTarget(new TargetAnyTargetAmount(2)); diff --git a/Mage.Sets/src/mage/cards/a/ArcaneArtisan.java b/Mage.Sets/src/mage/cards/a/ArcaneArtisan.java index 60ad4f593b5..1a3b700eb15 100644 --- a/Mage.Sets/src/mage/cards/a/ArcaneArtisan.java +++ b/Mage.Sets/src/mage/cards/a/ArcaneArtisan.java @@ -112,7 +112,7 @@ class ArcaneArtisanCreateTokenEffect extends OneShotEffect { } else { tokensCreated = new HashSet<>(); } - for (Permanent perm : effect.getAddedPermanent()) { + for (Permanent perm : effect.getAddedPermanents()) { if (perm != null) { tokensCreated.add(perm.getId()); } diff --git a/Mage.Sets/src/mage/cards/a/ArcaneDenial.java b/Mage.Sets/src/mage/cards/a/ArcaneDenial.java index 5e99149b233..57e617ef0e9 100644 --- a/Mage.Sets/src/mage/cards/a/ArcaneDenial.java +++ b/Mage.Sets/src/mage/cards/a/ArcaneDenial.java @@ -1,6 +1,5 @@ package mage.cards.a; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.delayed.AtTheBeginOfNextUpkeepDelayedTriggeredAbility; @@ -19,8 +18,9 @@ import mage.players.Player; import mage.target.TargetSpell; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class ArcaneDenial extends CardImpl { @@ -35,7 +35,10 @@ public final class ArcaneDenial extends CardImpl { // You draw a card at the beginning of the next turn's upkeep. this.getSpellAbility().addEffect(new CreateDelayedTriggeredAbilityEffect( new AtTheBeginOfNextUpkeepDelayedTriggeredAbility( - new DrawCardSourceControllerEffect(1)), false)); + new DrawCardSourceControllerEffect(1) + .setText("you draw a card") + ), false).concatBy("
") + ); } private ArcaneDenial(final ArcaneDenial card) { diff --git a/Mage.Sets/src/mage/cards/a/ArcboundTracker.java b/Mage.Sets/src/mage/cards/a/ArcboundTracker.java index c407d7e401c..a5310f08398 100644 --- a/Mage.Sets/src/mage/cards/a/ArcboundTracker.java +++ b/Mage.Sets/src/mage/cards/a/ArcboundTracker.java @@ -30,7 +30,7 @@ public final class ArcboundTracker extends CardImpl { this.toughness = new MageInt(0); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // Modular 2 this.addAbility(new ModularAbility(this, 2)); diff --git a/Mage.Sets/src/mage/cards/a/ArchangelAvacyn.java b/Mage.Sets/src/mage/cards/a/ArchangelAvacyn.java index 3156f5621e0..605363d4870 100644 --- a/Mage.Sets/src/mage/cards/a/ArchangelAvacyn.java +++ b/Mage.Sets/src/mage/cards/a/ArchangelAvacyn.java @@ -1,30 +1,23 @@ - package mage.cards.a; -import java.util.UUID; import mage.MageInt; -import mage.MageObject; -import mage.abilities.Ability; import mage.abilities.common.DiesCreatureTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.delayed.AtTheBeginOfNextUpkeepDelayedTriggeredAbility; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.TransformTargetEffect; -import mage.abilities.effects.common.continuous.GainAbilityAllEffect; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.abilities.keyword.*; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; /** - * * @author fireshoes */ public final class ArchangelAvacyn extends CardImpl { @@ -37,14 +30,13 @@ public final class ArchangelAvacyn extends CardImpl { } public ArchangelAvacyn(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{W}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}{W}"); addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.ANGEL); this.power = new MageInt(4); this.toughness = new MageInt(4); - this.transformable = true; this.secondSideCardClazz = AvacynThePurifier.class; // Flash @@ -57,15 +49,18 @@ public final class ArchangelAvacyn extends CardImpl { this.addAbility(VigilanceAbility.getInstance()); // When Archangel Avacyn enters the battlefield, creatures you control gain indestructible until end of turn. - Ability ability = new EntersBattlefieldTriggeredAbility( - new GainAbilityAllEffect(IndestructibleAbility.getInstance(), Duration.EndOfTurn, - new FilterControlledCreaturePermanent("creatures you control")), false); - this.addAbility(ability); + this.addAbility(new EntersBattlefieldTriggeredAbility(new GainAbilityControlledEffect( + IndestructibleAbility.getInstance(), Duration.EndOfTurn, + StaticFilters.FILTER_PERMANENT_CREATURES + ), false)); // When a non-Angel creature you control dies, transform Archangel Avacyn at the beginning of the next upkeep. this.addAbility(new TransformAbility()); - this.addAbility(new DiesCreatureTriggeredAbility(new ArchangelAvacynEffect(), false, filter)); - + this.addAbility(new DiesCreatureTriggeredAbility( + new CreateDelayedTriggeredAbilityEffect( + new AtTheBeginOfNextUpkeepDelayedTriggeredAbility(new TransformSourceEffect()) + ).setText("transform {this} at the beginning of the next upkeep"), false, filter + ).setTriggerPhrase("When a non-Angel creature you control dies, ")); } private ArchangelAvacyn(final ArchangelAvacyn card) { @@ -77,36 +72,3 @@ public final class ArchangelAvacyn extends CardImpl { return new ArchangelAvacyn(this); } } - -class ArchangelAvacynEffect extends OneShotEffect { - - private static final String effectText = "transform {this} at the beginning of the next upkeep"; - - ArchangelAvacynEffect() { - super(Outcome.Benefit); - staticText = effectText; - } - - ArchangelAvacynEffect(ArchangelAvacynEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - MageObject sourceObject = source.getSourceObjectIfItStillExists(game); - if (sourceObject instanceof Permanent) { - //create delayed triggered ability - Effect effect = new TransformTargetEffect(false); - effect.setTargetPointer(new FixedTarget((Permanent) sourceObject, game)); - AtTheBeginOfNextUpkeepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextUpkeepDelayedTriggeredAbility(effect); - game.addDelayedTriggeredAbility(delayedAbility, source); - } - return true; - - } - - @Override - public ArchangelAvacynEffect copy() { - return new ArchangelAvacynEffect(this); - } -} diff --git a/Mage.Sets/src/mage/cards/a/ArchdemonOfGreed.java b/Mage.Sets/src/mage/cards/a/ArchdemonOfGreed.java index 24536e649aa..b8127b27631 100644 --- a/Mage.Sets/src/mage/cards/a/ArchdemonOfGreed.java +++ b/Mage.Sets/src/mage/cards/a/ArchdemonOfGreed.java @@ -39,7 +39,6 @@ public final class ArchdemonOfGreed extends CardImpl { this.color.setBlack(true); this.nightCard = true; - this.transformable = true; this.power = new MageInt(9); this.toughness = new MageInt(9); diff --git a/Mage.Sets/src/mage/cards/a/ArchghoulOfThraben.java b/Mage.Sets/src/mage/cards/a/ArchghoulOfThraben.java new file mode 100644 index 00000000000..916d798d7f5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/ArchghoulOfThraben.java @@ -0,0 +1,93 @@ +package mage.cards.a; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DiesThisOrAnotherCreatureTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.cards.CardsImpl; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.filter.common.FilterControlledPermanent; +import mage.game.Game; +import mage.players.Player; + +/** + * + * @author weirddan455 + */ +public final class ArchghoulOfThraben extends CardImpl { + + private static final FilterControlledPermanent filter = new FilterControlledPermanent(SubType.ZOMBIE); + + public ArchghoulOfThraben(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.subtype.add(SubType.ZOMBIE); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // Whenever Archghoul of Thraben or another Zombie you control dies, look at the top card of your library. + // If it's a Zombie card, you may reveal it and put it into your hand. + // If you don't put the card into your hand, you may put it into your graveyard. + this.addAbility(new DiesThisOrAnotherCreatureTriggeredAbility(new ArchghoulOfThrabenEffect(), false, filter)); + } + + private ArchghoulOfThraben(final ArchghoulOfThraben card) { + super(card); + } + + @Override + public ArchghoulOfThraben copy() { + return new ArchghoulOfThraben(this); + } +} + +class ArchghoulOfThrabenEffect extends OneShotEffect { + + public ArchghoulOfThrabenEffect() { + super(Outcome.Benefit); + staticText = "look at the top card of your library. " + + "If it's a Zombie card, you may reveal it and put it into your hand. " + + "If you don't put the card into your hand, you may put it into your graveyard"; + } + + private ArchghoulOfThrabenEffect(final ArchghoulOfThrabenEffect effect) { + super(effect); + } + + @Override + public ArchghoulOfThrabenEffect copy() { + return new ArchghoulOfThrabenEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + Card topCard = controller.getLibrary().getFromTop(game); + if (topCard == null) { + return false; + } + controller.lookAtCards("Top card of library", topCard, game); + if (topCard.hasSubtype(SubType.ZOMBIE, game)) { + if (controller.chooseUse(Outcome.DrawCard, "Reveal " + topCard.getName() + " and put it into your hand?", source, game)) { + controller.revealCards(source, new CardsImpl(topCard), game); + controller.moveCards(topCard, Zone.HAND, source, game); + return true; + } + } + if (controller.chooseUse(Outcome.Neutral, "Put " + topCard.getName() + " into your graveyard?", source, game)) { + controller.moveCards(topCard, Zone.GRAVEYARD, source, game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/a/Archipelagore.java b/Mage.Sets/src/mage/cards/a/Archipelagore.java index 55a15bdb0db..276ce4a252b 100644 --- a/Mage.Sets/src/mage/cards/a/Archipelagore.java +++ b/Mage.Sets/src/mage/cards/a/Archipelagore.java @@ -34,7 +34,7 @@ public final class Archipelagore extends CardImpl { // Whenever this creature mutates, tap up to X target creatures, where X is the number of times this creature has mutated. Those creatures don't untap during their controller's next untap step. Ability ability = new MutatesSourceTriggeredAbility(new TapTargetEffect( - "up to X target creatures, where X is the number of times this creature has mutated." + "tap up to X target creatures, where X is the number of times this creature has mutated." )); ability.addEffect(new DontUntapInControllersNextUntapStepTargetEffect("Those creatures")); ability.setTargetAdjuster(ArchipelagoreAdjuster.instance); diff --git a/Mage.Sets/src/mage/cards/a/ArchitectOfRestoration.java b/Mage.Sets/src/mage/cards/a/ArchitectOfRestoration.java new file mode 100644 index 00000000000..7796e0f603a --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/ArchitectOfRestoration.java @@ -0,0 +1,45 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.common.AttacksOrBlocksTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.SpiritToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ArchitectOfRestoration extends CardImpl { + + public ArchitectOfRestoration(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, ""); + + this.subtype.add(SubType.FOX); + this.subtype.add(SubType.MONK); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + this.color.setWhite(true); + this.nightCard = true; + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // Whenever Architect of Restoration attacks or blocks, create a 1/1 colorless Spirit creature token. + this.addAbility(new AttacksOrBlocksTriggeredAbility(new CreateTokenEffect(new SpiritToken()), false)); + } + + private ArchitectOfRestoration(final ArchitectOfRestoration card) { + super(card); + } + + @Override + public ArchitectOfRestoration copy() { + return new ArchitectOfRestoration(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/ArchiveHaunt.java b/Mage.Sets/src/mage/cards/a/ArchiveHaunt.java index c026815658a..7516cde5dc8 100644 --- a/Mage.Sets/src/mage/cards/a/ArchiveHaunt.java +++ b/Mage.Sets/src/mage/cards/a/ArchiveHaunt.java @@ -26,7 +26,6 @@ public final class ArchiveHaunt extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(1); this.color.setBlue(true); - this.transformable = true; this.nightCard = true; // Flying diff --git a/Mage.Sets/src/mage/cards/a/ArchonOfEmeria.java b/Mage.Sets/src/mage/cards/a/ArchonOfEmeria.java index f5bdb2b10f3..14580c81d9b 100644 --- a/Mage.Sets/src/mage/cards/a/ArchonOfEmeria.java +++ b/Mage.Sets/src/mage/cards/a/ArchonOfEmeria.java @@ -1,18 +1,19 @@ package mage.cards.a; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.PermanentsEnterBattlefieldTappedEffect; import mage.abilities.effects.common.continuous.CantCastMoreThanOneSpellEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.game.Game; -import mage.game.events.EntersTheBattlefieldEvent; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterLandPermanent; +import mage.filter.predicate.Predicates; import java.util.UUID; @@ -21,6 +22,13 @@ import java.util.UUID; */ public final class ArchonOfEmeria extends CardImpl { + private static final FilterPermanent filter = new FilterLandPermanent("nonbasic lands your opponents control"); + + static { + filter.add(Predicates.not(SuperType.BASIC.getPredicate())); + filter.add(TargetController.OPPONENT.getControllerPredicate()); + } + public ArchonOfEmeria(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); @@ -35,7 +43,7 @@ public final class ArchonOfEmeria extends CardImpl { this.addAbility(new SimpleStaticAbility(new CantCastMoreThanOneSpellEffect(TargetController.ANY))); // Nonbasic lands your opponents control enter the battlefield tapped. - this.addAbility(new SimpleStaticAbility(new ArchonOfEmeriaEffect())); + this.addAbility(new SimpleStaticAbility(new PermanentsEnterBattlefieldTappedEffect(filter))); } private ArchonOfEmeria(final ArchonOfEmeria card) { @@ -47,45 +55,3 @@ public final class ArchonOfEmeria extends CardImpl { return new ArchonOfEmeria(this); } } - -class ArchonOfEmeriaEffect extends ReplacementEffectImpl { - - ArchonOfEmeriaEffect() { - super(Duration.WhileOnBattlefield, Outcome.Tap); - staticText = "nonbasic lands your opponents control enter the battlefield tapped"; - } - - private ArchonOfEmeriaEffect(final ArchonOfEmeriaEffect effect) { - super(effect); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Permanent target = ((EntersTheBattlefieldEvent) event).getTarget(); - if (target != null) { - target.setTapped(true); - } - return false; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (game.getOpponents(source.getControllerId()).contains(event.getPlayerId())) { - Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget(); - if (permanent != null && permanent.isLand(game) && !permanent.isBasic()) { - return true; - } - } - return false; - } - - @Override - public ArchonOfEmeriaEffect copy() { - return new ArchonOfEmeriaEffect(this); - } -} diff --git a/Mage.Sets/src/mage/cards/a/ArcumDagsson.java b/Mage.Sets/src/mage/cards/a/ArcumDagsson.java index 05850343d9f..532ae254123 100644 --- a/Mage.Sets/src/mage/cards/a/ArcumDagsson.java +++ b/Mage.Sets/src/mage/cards/a/ArcumDagsson.java @@ -87,6 +87,7 @@ class ArcumDagssonEffect extends OneShotEffect { Player player = game.getPlayer(artifactCreature.getControllerId()); if (player != null) { artifactCreature.sacrifice(source, game); + game.getState().processAction(game); // Workaround for https://github.com/magefree/mage/issues/8501 if (player.chooseUse(Outcome.PutCardInPlay, "Search your library for a noncreature artifact card?", source, game)) { TargetCardInLibrary target = new TargetCardInLibrary(filter); if (player.searchLibrary(target, source, game)) { diff --git a/Mage.Sets/src/mage/cards/a/Arena.java b/Mage.Sets/src/mage/cards/a/Arena.java index 6f800a7e011..f0e338e21c2 100644 --- a/Mage.Sets/src/mage/cards/a/Arena.java +++ b/Mage.Sets/src/mage/cards/a/Arena.java @@ -49,7 +49,8 @@ class ArenaEffect extends OneShotEffect { ArenaEffect() { super(Outcome.Benefit); - this.staticText = "Tap target creature you control and target creature of an opponent's choice they control. Those creatures fight each other."; + this.staticText = "Tap target creature you control and target creature of an opponent's choice they control. " + + "Those creatures fight each other. (Each deals damage equal to its power to the other.)"; } ArenaEffect(final ArenaEffect effect) { diff --git a/Mage.Sets/src/mage/cards/a/ArenaAthlete.java b/Mage.Sets/src/mage/cards/a/ArenaAthlete.java index c5a497e9b99..2e79284fd8b 100644 --- a/Mage.Sets/src/mage/cards/a/ArenaAthlete.java +++ b/Mage.Sets/src/mage/cards/a/ArenaAthlete.java @@ -11,8 +11,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; /** @@ -20,12 +19,6 @@ import mage.target.common.TargetCreaturePermanent; * @author Plopman */ public final class ArenaAthlete extends CardImpl { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } public ArenaAthlete(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{R}"); @@ -36,7 +29,7 @@ public final class ArenaAthlete extends CardImpl { // Heroic Whenever you cast a spell that targets Arena Athlete, target creature an opponent controls can't block this turn. Ability ability = new HeroicAbility(new CantBlockTargetEffect(Duration.EndOfTurn)); - TargetCreaturePermanent target = new TargetCreaturePermanent(filter); + TargetCreaturePermanent target = new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE); ability.addTarget(target); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/a/ArguelsBloodFast.java b/Mage.Sets/src/mage/cards/a/ArguelsBloodFast.java index a590432997f..7e6f55b301b 100644 --- a/Mage.Sets/src/mage/cards/a/ArguelsBloodFast.java +++ b/Mage.Sets/src/mage/cards/a/ArguelsBloodFast.java @@ -29,7 +29,6 @@ public final class ArguelsBloodFast extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}"); addSuperType(SuperType.LEGENDARY); - this.transformable = true; this.secondSideCardClazz = mage.cards.t.TempleOfAclazotz.class; // {1}{B}, Pay 2 life: Draw a card. @@ -40,7 +39,7 @@ public final class ArguelsBloodFast extends CardImpl { // At the beginning of your upkeep, if you have 5 or less life, you may transform Arguel's Blood Fast. this.addAbility(new TransformAbility()); this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(true), TargetController.YOU, true), + new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(), TargetController.YOU, true), FatefulHourCondition.instance, "At the beginning of your upkeep, if you have 5 or less life, you may transform {this}" )); diff --git a/Mage.Sets/src/mage/cards/a/ArlinnEmbracedByTheMoon.java b/Mage.Sets/src/mage/cards/a/ArlinnEmbracedByTheMoon.java index 1a2d5b1ae44..786926b4687 100644 --- a/Mage.Sets/src/mage/cards/a/ArlinnEmbracedByTheMoon.java +++ b/Mage.Sets/src/mage/cards/a/ArlinnEmbracedByTheMoon.java @@ -35,7 +35,6 @@ public final class ArlinnEmbracedByTheMoon extends CardImpl { this.color.setGreen(true); this.nightCard = true; - this.transformable = true; // +1: Creatures you control get +1/+1 and gain trample until end of turn. Effect effect = new BoostControlledEffect(1, 1, Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURE); @@ -50,7 +49,7 @@ public final class ArlinnEmbracedByTheMoon extends CardImpl { this.addAbility(new TransformAbility()); ability = new LoyaltyAbility(new DamageTargetEffect(3), -1); ability.addTarget(new TargetAnyTarget()); - ability.addEffect(new TransformSourceEffect(false)); + ability.addEffect(new TransformSourceEffect()); this.addAbility(ability); // -6: You get an emblem with "Creatures you control have haste and '{T}: This creature deals damage equal to its power to any target.'" diff --git a/Mage.Sets/src/mage/cards/a/ArlinnKord.java b/Mage.Sets/src/mage/cards/a/ArlinnKord.java index 80b3c240fb6..1c9db9b355a 100644 --- a/Mage.Sets/src/mage/cards/a/ArlinnKord.java +++ b/Mage.Sets/src/mage/cards/a/ArlinnKord.java @@ -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.CreateTokenEffect; import mage.abilities.effects.common.TransformSourceEffect; @@ -32,10 +31,9 @@ public final class ArlinnKord extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.ARLINN); - this.transformable = true; this.secondSideCardClazz = ArlinnEmbracedByTheMoon.class; - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +1: Until end of turn, up to one target creature gets +2/+2 and gains vigilance and haste. Effect effect = new BoostTargetEffect(2, 2, Duration.EndOfTurn); @@ -53,7 +51,7 @@ public final class ArlinnKord extends CardImpl { // 0: Create a 2/2 green Wolf creature token. Transform Arlinn Kord. this.addAbility(new TransformAbility()); ability = new LoyaltyAbility(new CreateTokenEffect(new WolfToken()), 0); - ability.addEffect(new TransformSourceEffect(true)); + ability.addEffect(new TransformSourceEffect()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/a/ArlinnTheMoonsFury.java b/Mage.Sets/src/mage/cards/a/ArlinnTheMoonsFury.java index 98e064a041b..3488bbb9a9c 100644 --- a/Mage.Sets/src/mage/cards/a/ArlinnTheMoonsFury.java +++ b/Mage.Sets/src/mage/cards/a/ArlinnTheMoonsFury.java @@ -3,7 +3,6 @@ package mage.cards.a; import mage.Mana; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.mana.BasicManaEffect; import mage.abilities.keyword.HasteAbility; @@ -28,10 +27,9 @@ public final class ArlinnTheMoonsFury extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.ARLINN); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); this.color.setRed(true); this.color.setGreen(true); - this.transformable = true; this.nightCard = true; // Nightbound diff --git a/Mage.Sets/src/mage/cards/a/ArlinnThePacksHope.java b/Mage.Sets/src/mage/cards/a/ArlinnThePacksHope.java index ee4f826916a..14d73c31ae3 100644 --- a/Mage.Sets/src/mage/cards/a/ArlinnThePacksHope.java +++ b/Mage.Sets/src/mage/cards/a/ArlinnThePacksHope.java @@ -2,12 +2,10 @@ package mage.cards.a; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.continuous.CastAsThoughItHadFlashAllEffect; import mage.abilities.keyword.DayboundAbility; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -34,12 +32,10 @@ public final class ArlinnThePacksHope extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.ARLINN); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); - this.transformable = true; + this.setStartingLoyalty(4); this.secondSideCardClazz = mage.cards.a.ArlinnTheMoonsFury.class; // Daybound - this.addAbility(new TransformAbility()); this.addAbility(new DayboundAbility()); // +1: Until your next turn, you may cast creature spells as though they had flash, and each creature you control enters the battlefield with an additional +1/+1 counter on it. diff --git a/Mage.Sets/src/mage/cards/a/ArlinnVoiceOfThePack.java b/Mage.Sets/src/mage/cards/a/ArlinnVoiceOfThePack.java index 0f671dc4c70..3b7defc7915 100644 --- a/Mage.Sets/src/mage/cards/a/ArlinnVoiceOfThePack.java +++ b/Mage.Sets/src/mage/cards/a/ArlinnVoiceOfThePack.java @@ -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.ReplacementEffectImpl; import mage.abilities.effects.common.CreateTokenEffect; @@ -28,7 +27,7 @@ public final class ArlinnVoiceOfThePack extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.ARLINN); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(7)); + this.setStartingLoyalty(7); // Each creature you control that's a Wolf or Werewolf enters the battlefield with an additional +1/+1 counter on it. this.addAbility(new SimpleStaticAbility(new ArlinnVoiceOfThePackReplacementEffect())); @@ -51,7 +50,7 @@ class ArlinnVoiceOfThePackReplacementEffect extends ReplacementEffectImpl { ArlinnVoiceOfThePackReplacementEffect() { super(Duration.WhileOnBattlefield, Outcome.BoostCreature); - staticText = "Each creature you control that's a Wolf or Werewolf " + + staticText = "Each creature you control that's a Wolf or a Werewolf " + "enters the battlefield with an additional +1/+1 counter on it"; } diff --git a/Mage.Sets/src/mage/cards/a/ArmTheCathars.java b/Mage.Sets/src/mage/cards/a/ArmTheCathars.java new file mode 100644 index 00000000000..f81384f8255 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/ArmTheCathars.java @@ -0,0 +1,70 @@ +package mage.cards.a; + +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.other.AnotherTargetPredicate; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetpointer.EachTargetPointer; +import mage.target.targetpointer.SecondTargetPointer; +import mage.target.targetpointer.ThirdTargetPointer; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ArmTheCathars extends CardImpl { + + private static final FilterCreaturePermanent filter1 = new FilterCreaturePermanent(); + private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent(); + private static final FilterCreaturePermanent filter3 = new FilterCreaturePermanent(); + + static { + filter2.add(new AnotherTargetPredicate(2)); + filter3.add(new AnotherTargetPredicate(3)); + } + + public ArmTheCathars(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{W}{W}"); + + // Until end of turn, target creature gets +3/+3, up to one other target creature gets +2/+2, and up to one other target creature gets +1/+1. Those creatures gain vigilance until end of turn. + this.getSpellAbility().addEffect(new BoostTargetEffect(3, 3) + .setText("until end of turn, target creature gets +3/+3")); + TargetCreaturePermanent target1 = new TargetCreaturePermanent(filter1); + target1.setTargetTag(1); + this.getSpellAbility().addTarget(target1.withChooseHint("+3/+3")); + + this.getSpellAbility().addEffect(new BoostTargetEffect(2, 2) + .setTargetPointer(new SecondTargetPointer()) + .setText(", up to one other target creature gets +2/+2")); + TargetCreaturePermanent target2 = new TargetCreaturePermanent(0, 1, filter2, false); + target2.setTargetTag(2); + this.getSpellAbility().addTarget(target2.withChooseHint("+2/+2")); + + this.getSpellAbility().addEffect(new BoostTargetEffect(1, 1) + .setTargetPointer(new ThirdTargetPointer()) + .setText(", and up to one other target creature gets +1/+1")); + TargetCreaturePermanent target3 = new TargetCreaturePermanent(0, 1, filter3, false); + target3.setTargetTag(3); + this.getSpellAbility().addTarget(target3.withChooseHint("+1/+1")); + + this.getSpellAbility().addEffect( + new GainAbilityTargetEffect(VigilanceAbility.getInstance()) + .setTargetPointer(new EachTargetPointer()) + .setText("Those creatures gain vigilance until end of turn")); + } + + private ArmTheCathars(final ArmTheCathars card) { + super(card); + } + + @Override + public ArmTheCathars copy() { + return new ArmTheCathars(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/ArmguardFamiliar.java b/Mage.Sets/src/mage/cards/a/ArmguardFamiliar.java new file mode 100644 index 00000000000..33a58c83abe --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/ArmguardFamiliar.java @@ -0,0 +1,54 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.continuous.BoostEquippedEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.ReconfigureAbility; +import mage.abilities.keyword.WardAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ArmguardFamiliar extends CardImpl { + + public ArmguardFamiliar(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{1}{U}"); + + this.subtype.add(SubType.EQUIPMENT); + this.subtype.add(SubType.BEAST); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Ward {2} + this.addAbility(new WardAbility(new GenericManaCost(2))); + + // Equipped creature gets +2/+1 and has ward {2}. + Ability ability = new SimpleStaticAbility(new BoostEquippedEffect(2, 1)); + ability.addEffect(new GainAbilityAttachedEffect( + new WardAbility(new GenericManaCost(2)), AttachmentType.EQUIPMENT + ).setText("and has ward {2}")); + this.addAbility(ability); + + // Reconfigure {4} + this.addAbility(new ReconfigureAbility("{4}")); + } + + private ArmguardFamiliar(final ArmguardFamiliar card) { + super(card); + } + + @Override + public ArmguardFamiliar copy() { + return new ArmguardFamiliar(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/ArmixFiligreeThrasher.java b/Mage.Sets/src/mage/cards/a/ArmixFiligreeThrasher.java index 705e4b4a35f..4e1745fe5de 100644 --- a/Mage.Sets/src/mage/cards/a/ArmixFiligreeThrasher.java +++ b/Mage.Sets/src/mage/cards/a/ArmixFiligreeThrasher.java @@ -1,12 +1,14 @@ package mage.cards.a; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.dynamicvalue.AdditiveDynamicValue; import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.effects.Effect; +import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.dynamicvalue.common.SignInversionDynamicValue; import mage.abilities.effects.common.DoWhenCostPaid; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.abilities.keyword.PartnerAbility; @@ -19,8 +21,6 @@ import mage.constants.SuperType; import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.permanent.DefendingPlayerControlsPredicate; -import mage.game.Game; -import mage.players.Player; import mage.target.TargetPermanent; import java.util.UUID; @@ -36,6 +36,11 @@ public final class ArmixFiligreeThrasher extends CardImpl { filter.add(DefendingPlayerControlsPredicate.instance); } + private static final DynamicValue xValue = new SignInversionDynamicValue(new AdditiveDynamicValue( + new PermanentsOnBattlefieldCount(StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACTS), + new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_ARTIFACT) + )); + public ArmixFiligreeThrasher(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{2}{B}"); @@ -46,11 +51,8 @@ public final class ArmixFiligreeThrasher extends CardImpl { // Whenever Armix, Filigree Thrasher attacks, you may discard a card. When you do, target creature defending player controls gets -X/-X until end of turn, where X is the number of artifacts you control plus the number of artifact cards in your graveyard. ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( - new BoostTargetEffect( - ArmixFiligreeThrasherValue.instance, - ArmixFiligreeThrasherValue.instance, - Duration.EndOfTurn, true - ), false, "target creature defending player controls gets -X/-X until end of turn, " + + new BoostTargetEffect(xValue, xValue, Duration.EndOfTurn, true), false, + "target creature defending player controls gets -X/-X until end of turn, " + "where X is the number of artifacts you control plus the number of artifact cards in your graveyard" ); ability.addTarget(new TargetPermanent(filter)); @@ -71,32 +73,3 @@ public final class ArmixFiligreeThrasher extends CardImpl { return new ArmixFiligreeThrasher(this); } } - -enum ArmixFiligreeThrasherValue implements DynamicValue { - instance; - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - Player player = game.getPlayer(sourceAbility.getControllerId()); - if (player == null) { - return 0; - } - return -(player.getGraveyard().count( - StaticFilters.FILTER_CARD_ARTIFACT, player.getId(), game - ) + game.getBattlefield().count( - StaticFilters.FILTER_PERMANENT_ARTIFACT, - sourceAbility.getSourceId(), - sourceAbility.getControllerId(), game - )); - } - - @Override - public ArmixFiligreeThrasherValue copy() { - return instance; - } - - @Override - public String getMessage() { - return ""; - } -} diff --git a/Mage.Sets/src/mage/cards/a/ArmorOfThorns.java b/Mage.Sets/src/mage/cards/a/ArmorOfThorns.java index fe7fe088a00..905942295f1 100644 --- a/Mage.Sets/src/mage/cards/a/ArmorOfThorns.java +++ b/Mage.Sets/src/mage/cards/a/ArmorOfThorns.java @@ -1,8 +1,6 @@ - package mage.cards.a; import java.util.UUID; -import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.SacrificeIfCastAtInstantTimeTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; @@ -13,9 +11,7 @@ import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ColorPredicate; +import mage.filter.StaticFilters; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; @@ -25,12 +21,6 @@ import mage.target.common.TargetCreaturePermanent; */ public final class ArmorOfThorns extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public ArmorOfThorns(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{G}"); this.subtype.add(SubType.AURA); @@ -39,7 +29,7 @@ public final class ArmorOfThorns extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.ALL, new CastAsThoughItHadFlashSourceEffect(Duration.EndOfGame))); this.addAbility(new SacrificeIfCastAtInstantTimeTriggeredAbility()); // Enchant nonblack creature - TargetPermanent auraTarget = new TargetCreaturePermanent(filter); + TargetPermanent auraTarget = new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK); this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); Ability ability = new EnchantAbility(auraTarget.getTargetName()); diff --git a/Mage.Sets/src/mage/cards/a/ArmorSliver.java b/Mage.Sets/src/mage/cards/a/ArmorSliver.java index 51b621bb65f..d3b81dcb084 100644 --- a/Mage.Sets/src/mage/cards/a/ArmorSliver.java +++ b/Mage.Sets/src/mage/cards/a/ArmorSliver.java @@ -30,7 +30,7 @@ public final class ArmorSliver extends CardImpl { this.toughness = new MageInt(2); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect( new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(0, 1, Duration.EndOfTurn).setText("this creature gets +0/+1 until end of turn"), - new GenericManaCost(2)), Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS, false))); + new GenericManaCost(2)), Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_ALL_SLIVERS, false))); } private ArmorSliver(final ArmorSliver card) { diff --git a/Mage.Sets/src/mage/cards/a/ArmorcraftJudge.java b/Mage.Sets/src/mage/cards/a/ArmorcraftJudge.java index 68b443289d0..1441c07f3a9 100644 --- a/Mage.Sets/src/mage/cards/a/ArmorcraftJudge.java +++ b/Mage.Sets/src/mage/cards/a/ArmorcraftJudge.java @@ -10,8 +10,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.counters.CounterType; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; /** * @@ -19,12 +18,6 @@ import mage.filter.common.FilterControlledCreaturePermanent; */ public final class ArmorcraftJudge extends CardImpl { - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("creature you control with a +1/+1 counter on it"); - - static { - filter.add(CounterType.P1P1.getPredicate()); - } - public ArmorcraftJudge(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{G}"); this.subtype.add(SubType.ELF); @@ -33,7 +26,9 @@ public final class ArmorcraftJudge extends CardImpl { this.toughness = new MageInt(3); // When Armorcraft Judge enters the battlefield, draw a card for each creature you control with a +1/+1 counter on it. - this.addAbility(new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(new PermanentsOnBattlefieldCount(filter)))); + this.addAbility(new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect( + new PermanentsOnBattlefieldCount(StaticFilters.FILTER_CONTROLLED_CREATURE_P1P1)) + )); } private ArmorcraftJudge(final ArmorcraftJudge card) { diff --git a/Mage.Sets/src/mage/cards/a/ArmoryVeteran.java b/Mage.Sets/src/mage/cards/a/ArmoryVeteran.java index e775d66896e..df367735e7e 100644 --- a/Mage.Sets/src/mage/cards/a/ArmoryVeteran.java +++ b/Mage.Sets/src/mage/cards/a/ArmoryVeteran.java @@ -30,7 +30,8 @@ public final class ArmoryVeteran extends CardImpl { this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( new GainAbilitySourceEffect(new MenaceAbility()), EquippedSourceCondition.instance, - "As long as {this} is equipped, it has menace" + "As long as {this} is equipped, it has menace. " + + "(It can't be blocked except by two or more creatures.)" ))); } diff --git a/Mage.Sets/src/mage/cards/a/ArmyOfAllah.java b/Mage.Sets/src/mage/cards/a/ArmyOfAllah.java index 97b319b9110..b4761425cc2 100644 --- a/Mage.Sets/src/mage/cards/a/ArmyOfAllah.java +++ b/Mage.Sets/src/mage/cards/a/ArmyOfAllah.java @@ -7,21 +7,19 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.common.FilterAttackingCreature; +import mage.filter.StaticFilters; /** * * @author fireshoes */ public final class ArmyOfAllah extends CardImpl { - - private static final FilterAttackingCreature filter = new FilterAttackingCreature("Attacking creatures"); public ArmyOfAllah(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{W}{W}"); // Attacking creatures get +2/+0 until end of turn. - this.getSpellAbility().addEffect(new BoostAllEffect(2, 0, Duration.EndOfTurn, filter, false)); + this.getSpellAbility().addEffect(new BoostAllEffect(2, 0, Duration.EndOfTurn, StaticFilters.FILTER_ATTACKING_CREATURES, false)); } private ArmyOfAllah(final ArmyOfAllah card) { diff --git a/Mage.Sets/src/mage/cards/a/ArniSlaysTheTroll.java b/Mage.Sets/src/mage/cards/a/ArniSlaysTheTroll.java index cac74adb05e..45b45565ce6 100644 --- a/Mage.Sets/src/mage/cards/a/ArniSlaysTheTroll.java +++ b/Mage.Sets/src/mage/cards/a/ArniSlaysTheTroll.java @@ -33,11 +33,11 @@ public final class ArniSlaysTheTroll extends CardImpl { this.subtype.add(SubType.SAGA); // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) - SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); + SagaAbility sagaAbility = new SagaAbility(this); // I — Target creature you control fights up to one target creature you don't control. sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_I, - new Effects(new FightTargetsEffect( + new Effects(new FightTargetsEffect().setText( "Target creature you control fights up to one target creature you don't control" )), new Targets( new TargetControlledCreaturePermanent(), diff --git a/Mage.Sets/src/mage/cards/a/ArrowVolleyTrap.java b/Mage.Sets/src/mage/cards/a/ArrowVolleyTrap.java index e1abdc21314..27a6ee9e854 100644 --- a/Mage.Sets/src/mage/cards/a/ArrowVolleyTrap.java +++ b/Mage.Sets/src/mage/cards/a/ArrowVolleyTrap.java @@ -1,4 +1,3 @@ - package mage.cards.a; import java.util.UUID; @@ -11,7 +10,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.filter.common.FilterAttackingCreature; +import mage.filter.StaticFilters; import mage.game.Game; import mage.target.common.TargetCreaturePermanentAmount; @@ -29,8 +28,7 @@ public final class ArrowVolleyTrap extends CardImpl { // Arrow Volley Trap deals 5 damage divided as you choose among any number of target attacking creatures. this.getSpellAbility().addEffect(new DamageMultiEffect(5)); - this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount(5, new FilterAttackingCreature("attacking creatures"))); - + this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount(5, StaticFilters.FILTER_ATTACKING_CREATURES)); } private ArrowVolleyTrap(final ArrowVolleyTrap card) { diff --git a/Mage.Sets/src/mage/cards/a/ArterialAlchemy.java b/Mage.Sets/src/mage/cards/a/ArterialAlchemy.java new file mode 100644 index 00000000000..9fc51df6f9d --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/ArterialAlchemy.java @@ -0,0 +1,103 @@ +package mage.cards.a; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.common.OpponentsCount; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.continuous.BoostEquippedEffect; +import mage.abilities.keyword.EquipAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.BloodToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ArterialAlchemy extends CardImpl { + + public ArterialAlchemy(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{R}"); + + // When Arterial Alchemy enters the battlefield, create a Blood token for each opponent you have. + this.addAbility(new EntersBattlefieldTriggeredAbility( + new CreateTokenEffect(new BloodToken(), OpponentsCount.instance) + .setText("create a Blood token for each opponent you have") + )); + + // Blood tokens you control are Equipment in addition to their other types and have "Equipped creature gets +2/+0" and equip {2}. + this.addAbility(new SimpleStaticAbility(new ArterialAlchemyEffect())); + } + + private ArterialAlchemy(final ArterialAlchemy card) { + super(card); + } + + @Override + public ArterialAlchemy copy() { + return new ArterialAlchemy(this); + } +} + +class ArterialAlchemyEffect extends ContinuousEffectImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.BLOOD); + + static { + filter.add(TokenPredicate.TRUE); + } + + ArterialAlchemyEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "Blood tokens you control are Equipment in addition " + + "to their other types and have \"Equipped creature gets +2/+0\" and equip {2}."; + } + + private ArterialAlchemyEffect(final ArterialAlchemyEffect effect) { + super(effect); + } + + @Override + public ArterialAlchemyEffect copy() { + return new ArterialAlchemyEffect(this); + } + + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + for (Permanent permanent : game.getBattlefield().getActivePermanents( + filter, source.getControllerId(), source.getSourceId(), game + )) { + switch (layer) { + case TypeChangingEffects_4: + permanent.addSubType(game, SubType.EQUIPMENT); + break; + case AbilityAddingRemovingEffects_6: + permanent.addAbility(new SimpleStaticAbility( + new BoostEquippedEffect(2, 0) + ), source.getSourceId(), game); + permanent.addAbility(new EquipAbility(2), source.getSourceId(), game); + break; + } + } + return true; + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean hasLayer(Layer layer) { + return layer == Layer.TypeChangingEffects_4 || layer == Layer.AbilityAddingRemovingEffects_6; + } +} diff --git a/Mage.Sets/src/mage/cards/a/ArtificersAssistant.java b/Mage.Sets/src/mage/cards/a/ArtificersAssistant.java index 4cd1e56eb86..8af18e85ac9 100644 --- a/Mage.Sets/src/mage/cards/a/ArtificersAssistant.java +++ b/Mage.Sets/src/mage/cards/a/ArtificersAssistant.java @@ -36,7 +36,7 @@ public final class ArtificersAssistant extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever you cast a Historic spell scry 1. - this.addAbility(new SpellCastControllerTriggeredAbility(new ScryEffect(1), filter, false)); + this.addAbility(new SpellCastControllerTriggeredAbility(new ScryEffect(1, false), filter, false)); } private ArtificersAssistant(final ArtificersAssistant card) { diff --git a/Mage.Sets/src/mage/cards/a/ArvinoxTheMindFlail.java b/Mage.Sets/src/mage/cards/a/ArvinoxTheMindFlail.java new file mode 100644 index 00000000000..8b84f951fa8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/ArvinoxTheMindFlail.java @@ -0,0 +1,243 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.AsThoughManaEffect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.LoseCreatureTypeSourceEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.cards.*; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.game.Game; +import mage.players.ManaPoolItem; +import mage.players.Player; +import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * @author TheElk801 plus everyone who worked on Gonti + */ +public final class ArvinoxTheMindFlail extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent(); + + static { + filter.add(TargetController.NOT_YOU.getOwnerPredicate()); + } + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter); + private static final Hint hint = new ValueHint("Permanents you control but don't own", xValue); + + public ArvinoxTheMindFlail(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{4}{B}{B}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HORROR); + this.power = new MageInt(9); + this.toughness = new MageInt(9); + + // Mind Flayer, the Shadow isn't a creature unless you control three or more permanents you don't own. + this.addAbility(new SimpleStaticAbility(new LoseCreatureTypeSourceEffect(xValue, 3) + .setText("{this} isn't a creature unless you control three or more permanents you don't own") + ).addHint(hint)); + + // At the beginning of your end step, exile the bottom card of each opponent's library face down. For as long as those cards remain exiled, you may look at them, you may cast permanent spells from among them, and you may spend mana as though it were mana of any color to cast those spells. + this.addAbility(new BeginningOfEndStepTriggeredAbility( + new ArvinoxTheMindFlailExileEffect(), TargetController.YOU, false + )); + } + + private ArvinoxTheMindFlail(final ArvinoxTheMindFlail card) { + super(card); + } + + @Override + public ArvinoxTheMindFlail copy() { + return new ArvinoxTheMindFlail(this); + } +} + +class ArvinoxTheMindFlailExileEffect extends OneShotEffect { + + ArvinoxTheMindFlailExileEffect() { + super(Outcome.Benefit); + staticText = "exile the bottom card of each opponent's library face down. " + + "For as long as those cards remain exiled, you may look at them, " + + "you may cast permanent spells from among them, " + + "and you may spend mana as though it were mana of any color to cast those spells"; + } + + private ArvinoxTheMindFlailExileEffect(final ArvinoxTheMindFlailExileEffect effect) { + super(effect); + } + + @Override + public ArvinoxTheMindFlailExileEffect copy() { + return new ArvinoxTheMindFlailExileEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + Cards cards = new CardsImpl(); + for (UUID playerId : game.getOpponents(source.getControllerId())) { + Player opponent = game.getPlayer(playerId); + if (opponent == null) { + continue; + } + cards.add(opponent.getLibrary().getFromBottom(game)); + } + controller.moveCardsToExile( + cards.getCards(game), source, game, false, + CardUtil.getExileZoneId(game, source), + CardUtil.getSourceName(game, source) + ); + cards.getCards(game).stream().forEach(card -> card.setFaceDown(true, game)); + for (Card card : cards.getCards(game)) { + card.setFaceDown(true, game); + game.addEffect(new ArvinoxTheMindFlailCastFromExileEffect().setTargetPointer(new FixedTarget(card, game)), source); + game.addEffect(new ArvinoxTheMindFlailSpendAnyManaEffect().setTargetPointer(new FixedTarget(card, game)), source); + game.addEffect(new ArvinoxTheMindFlailLookEffect(source.getControllerId()).setTargetPointer(new FixedTarget(card, game)), source); + } + return true; + } +} + +class ArvinoxTheMindFlailCastFromExileEffect extends AsThoughEffectImpl { + + public ArvinoxTheMindFlailCastFromExileEffect() { + super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.Custom, Outcome.Benefit); + } + + private ArvinoxTheMindFlailCastFromExileEffect(final ArvinoxTheMindFlailCastFromExileEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public ArvinoxTheMindFlailCastFromExileEffect copy() { + return new ArvinoxTheMindFlailCastFromExileEffect(this); + } + + @Override + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + UUID targetId = getTargetPointer().getFirst(game, source); + if (targetId == null) { + this.discard(); // card is no longer in the origin zone, effect can be discarded + return false; + } + Card theCard = game.getCard(objectId); + if (theCard == null + || theCard.isLand(game) + || !theCard.isPermanent(game)) { + return false; + } + objectId = theCard.getMainCard().getId(); // for split cards + + if (objectId.equals(targetId) + && affectedControllerId.equals(source.getControllerId())) { + Card card = game.getCard(objectId); + // TODO: Allow to cast Zoetic Cavern face down + return card != null; + } + return false; + } +} + +class ArvinoxTheMindFlailSpendAnyManaEffect extends AsThoughEffectImpl implements AsThoughManaEffect { + + public ArvinoxTheMindFlailSpendAnyManaEffect() { + super(AsThoughEffectType.SPEND_OTHER_MANA, Duration.Custom, Outcome.Benefit); + staticText = "you may spend mana as though it were mana of any color to cast it"; + } + + private ArvinoxTheMindFlailSpendAnyManaEffect(final ArvinoxTheMindFlailSpendAnyManaEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public ArvinoxTheMindFlailSpendAnyManaEffect copy() { + return new ArvinoxTheMindFlailSpendAnyManaEffect(this); + } + + @Override + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + Card theCard = game.getCard(objectId); + if (theCard == null) { + return false; + } + objectId = theCard.getMainCard().getId(); // for split cards + if (objectId.equals(((FixedTarget) getTargetPointer()).getTarget()) + && game.getState().getZoneChangeCounter(objectId) <= ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1) { + // if the card moved from exile to spell the zone change counter is increased by 1 (effect must applies before and on stack, use isCheckPlayableMode?) + return source.isControlledBy(affectedControllerId); + } else if (((FixedTarget) getTargetPointer()).getTarget().equals(objectId)) { + // object has moved zone so effect can be discarded + this.discard(); + } + return false; + } + + @Override + public ManaType getAsThoughManaType(ManaType manaType, ManaPoolItem mana, UUID affectedControllerId, Ability source, Game game) { + return mana.getFirstAvailable(); + } +} + +class ArvinoxTheMindFlailLookEffect extends AsThoughEffectImpl { + + private final UUID authorizedPlayerId; + + public ArvinoxTheMindFlailLookEffect(UUID authorizedPlayerId) { + super(AsThoughEffectType.LOOK_AT_FACE_DOWN, Duration.EndOfGame, Outcome.Benefit); + this.authorizedPlayerId = authorizedPlayerId; + staticText = "You may look at the cards exiled with {this}"; + } + + private ArvinoxTheMindFlailLookEffect(final ArvinoxTheMindFlailLookEffect effect) { + super(effect); + this.authorizedPlayerId = effect.authorizedPlayerId; + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public ArvinoxTheMindFlailLookEffect copy() { + return new ArvinoxTheMindFlailLookEffect(this); + } + + @Override + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + UUID cardId = getTargetPointer().getFirst(game, source); + if (cardId == null) { + this.discard(); // card is no longer in the origin zone, effect can be discarded + } + return affectedControllerId.equals(authorizedPlayerId) + && objectId.equals(cardId); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AsariCaptain.java b/Mage.Sets/src/mage/cards/a/AsariCaptain.java new file mode 100644 index 00000000000..90b8b0101af --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AsariCaptain.java @@ -0,0 +1,57 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.common.AttacksAloneControlledTriggeredAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AsariCaptain extends CardImpl { + + private static final DynamicValue xValue + = new PermanentsOnBattlefieldCount(StaticFilters.FILTER_CONTROLLED_SAMURAI_OR_WARRIOR); + private static final Hint hint = new ValueHint("Samurai and Warriors you control", xValue); + + public AsariCaptain(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SAMURAI); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // Whenever a Samurai or Warrior you control attacks alone, it gets +1/+0 until end of turn for each Samurai or Warrior you control. + this.addAbility(new AttacksAloneControlledTriggeredAbility( + new BoostTargetEffect(xValue, StaticValue.get(0), Duration.EndOfTurn, true) + .setText("it gets +1/+0 until end of turn for each Samurai or Warrior you control"), + StaticFilters.FILTER_CONTROLLED_SAMURAI_OR_WARRIOR, true, false + ).addHint(hint)); + } + + private AsariCaptain(final AsariCaptain card) { + super(card); + } + + @Override + public AsariCaptain copy() { + return new AsariCaptain(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AscendantAcolyte.java b/Mage.Sets/src/mage/cards/a/AscendantAcolyte.java new file mode 100644 index 00000000000..dfeafe2aa0c --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AscendantAcolyte.java @@ -0,0 +1,89 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DoubleCountersSourceEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.game.Game; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AscendantAcolyte extends CardImpl { + + public AscendantAcolyte(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.MONK); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Ascendant Acolyte enters the battlefield with a +1/+1 counter on it for each +1/+1 counter among other creatures you control. + this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect( + CounterType.P1P1.createInstance(), AscendantAcolyteValue.instance, true + ), "with a +1/+1 counter on it for each +1/+1 counter among other creatures you control").addHint(AscendantAcolyteValue.getHint())); + + // At the beginning of your upkeep, double the number of +1/+1 counters on Ascendant Acolyte. + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new DoubleCountersSourceEffect(CounterType.P1P1), TargetController.YOU, false + )); + } + + private AscendantAcolyte(final AscendantAcolyte card) { + super(card); + } + + @Override + public AscendantAcolyte copy() { + return new AscendantAcolyte(this); + } +} + +enum AscendantAcolyteValue implements DynamicValue { + instance; + private static final Hint hint = new ValueHint( + "Total +1/+1 counters on other creatures you control", instance + ); + + public static Hint getHint() { + return hint; + } + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + return game.getBattlefield() + .getActivePermanents( + StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE, + sourceAbility.getControllerId(), + sourceAbility.getSourceId(), game + ).stream() + .mapToInt(permanent -> permanent.getCounters(game).getCount(CounterType.P1P1)) + .sum(); + } + + @Override + public AscendantAcolyteValue copy() { + return this; + } + + @Override + public String getMessage() { + return ""; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/a/AscendantPackleader.java b/Mage.Sets/src/mage/cards/a/AscendantPackleader.java new file mode 100644 index 00000000000..21d87ecc1c0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AscendantPackleader.java @@ -0,0 +1,64 @@ +package mage.cards.a; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.constants.ComparisonType; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.FilterSpell; +import mage.filter.predicate.mageobject.ManaValuePredicate; + +/** + * + * @author weirddan455 + */ +public final class AscendantPackleader extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent("a permanent with mana value 4 or greater"); + private static final FilterSpell filter2 = new FilterSpell("a spell with mana value 4 or greater"); + + static { + filter.add(new ManaValuePredicate(ComparisonType.MORE_THAN, 3)); + filter2.add(new ManaValuePredicate(ComparisonType.MORE_THAN, 3)); + } + + public AscendantPackleader(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}"); + + this.subtype.add(SubType.WOLF); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Ascendant Packleader enters the battlefield with a +1/+1 counter on it if you control a permanent with mana value 4 or greater. + this.addAbility(new EntersBattlefieldAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), + new PermanentsOnTheBattlefieldCondition(filter), + null, + "with a +1/+1 counter on it if you control a permanent with mana value 4 or greater" + )); + + // Whenever you cast a spell with mana value 4 or greater, put a +1/+1 counter on Ascendant Packleader. + this.addAbility(new SpellCastControllerTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), + filter2, + false + )); + } + + private AscendantPackleader(final AscendantPackleader card) { + super(card); + } + + @Override + public AscendantPackleader copy() { + return new AscendantPackleader(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AscentOfTheWorthy.java b/Mage.Sets/src/mage/cards/a/AscentOfTheWorthy.java index c5035cdc54d..c8c9b689e43 100644 --- a/Mage.Sets/src/mage/cards/a/AscentOfTheWorthy.java +++ b/Mage.Sets/src/mage/cards/a/AscentOfTheWorthy.java @@ -35,7 +35,7 @@ public final class AscentOfTheWorthy extends CardImpl { this.subtype.add(SubType.SAGA); // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) - SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); + SagaAbility sagaAbility = new SagaAbility(this); // I, II — Choose a creature you control. Until your next turn, all damage that would be dealt to creatures you control is dealt to that creature instead. sagaAbility.addChapterEffect( diff --git a/Mage.Sets/src/mage/cards/a/AshayaSoulOfTheWild.java b/Mage.Sets/src/mage/cards/a/AshayaSoulOfTheWild.java index a0cb766eace..010985ee806 100644 --- a/Mage.Sets/src/mage/cards/a/AshayaSoulOfTheWild.java +++ b/Mage.Sets/src/mage/cards/a/AshayaSoulOfTheWild.java @@ -3,8 +3,7 @@ package mage.cards.a; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.dynamicvalue.common.LandsYouControlCount; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.common.continuous.SetPowerToughnessSourceEffect; import mage.abilities.mana.GreenManaAbility; @@ -12,7 +11,6 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.FilterPermanent; -import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.Game; @@ -25,9 +23,6 @@ import java.util.UUID; */ public final class AshayaSoulOfTheWild extends CardImpl { - private static final DynamicValue xValue - = new PermanentsOnBattlefieldCount(StaticFilters.FILTER_CONTROLLED_PERMANENT_LANDS); - public AshayaSoulOfTheWild(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}{G}"); @@ -38,9 +33,8 @@ public final class AshayaSoulOfTheWild extends CardImpl { // Ashaya, Soul of the Wild’s power and toughness are each equal to the number of lands you control. this.addAbility(new SimpleStaticAbility( - Zone.ALL, new SetPowerToughnessSourceEffect( - xValue, Duration.EndOfGame - ))); + Zone.ALL, new SetPowerToughnessSourceEffect(LandsYouControlCount.instance, Duration.EndOfGame) + )); // Nontoken creatures you control are Forest lands in addition to their other types. (They’re still affected by summoning sickness.) this.addAbility(new SimpleStaticAbility(new AshayaSoulOfTheWildEffect())); diff --git a/Mage.Sets/src/mage/cards/a/AshesOfTheFallen.java b/Mage.Sets/src/mage/cards/a/AshesOfTheFallen.java index 0dfd2a82906..2f90d505899 100644 --- a/Mage.Sets/src/mage/cards/a/AshesOfTheFallen.java +++ b/Mage.Sets/src/mage/cards/a/AshesOfTheFallen.java @@ -66,7 +66,7 @@ class AshesOfTheFallenEffect extends ContinuousEffectImpl { } } } else { - discard();; + discard(); } return true; } diff --git a/Mage.Sets/src/mage/cards/a/AshiokDreamRender.java b/Mage.Sets/src/mage/cards/a/AshiokDreamRender.java index ac680020c0d..cd5f208f858 100644 --- a/Mage.Sets/src/mage/cards/a/AshiokDreamRender.java +++ b/Mage.Sets/src/mage/cards/a/AshiokDreamRender.java @@ -3,7 +3,6 @@ package mage.cards.a; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; import mage.abilities.effects.common.ExileGraveyardAllPlayersEffect; @@ -29,7 +28,7 @@ public final class AshiokDreamRender extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.ASHIOK); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // Spells and abilities your opponents control can't cause their controller to search their library. this.addAbility(new SimpleStaticAbility(new AshiokDreamRenderEffect())); diff --git a/Mage.Sets/src/mage/cards/a/AshiokNightmareMuse.java b/Mage.Sets/src/mage/cards/a/AshiokNightmareMuse.java index 6c783fb101d..b9bf0eee652 100644 --- a/Mage.Sets/src/mage/cards/a/AshiokNightmareMuse.java +++ b/Mage.Sets/src/mage/cards/a/AshiokNightmareMuse.java @@ -4,7 +4,6 @@ import java.util.UUID; import mage.ApprovingObject; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.Card; @@ -30,7 +29,7 @@ public final class AshiokNightmareMuse extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.ASHIOK); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +1: Create a 2/3 blue and black Nightmare creature token with "Whenever this creature attacks or blocks, each opponent exiles the top two cards of their library." this.addAbility(new LoyaltyAbility(new CreateTokenEffect(new AshiokNightmareMuseToken()), 1)); diff --git a/Mage.Sets/src/mage/cards/a/AshiokNightmareWeaver.java b/Mage.Sets/src/mage/cards/a/AshiokNightmareWeaver.java index e52d6911130..5dbeb2719aa 100644 --- a/Mage.Sets/src/mage/cards/a/AshiokNightmareWeaver.java +++ b/Mage.Sets/src/mage/cards/a/AshiokNightmareWeaver.java @@ -4,7 +4,6 @@ package mage.cards.a; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.costs.Cost; import mage.abilities.costs.common.PayVariableLoyaltyCost; import mage.abilities.effects.ContinuousEffectImpl; @@ -35,7 +34,7 @@ public final class AshiokNightmareWeaver extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.ASHIOK); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +2: Exile the top three cards of target opponent's library. LoyaltyAbility ability = new LoyaltyAbility(new AshiokNightmareWeaverExileEffect(), 2); diff --git a/Mage.Sets/src/mage/cards/a/AshiokSculptorOfFears.java b/Mage.Sets/src/mage/cards/a/AshiokSculptorOfFears.java index ec9e24c8fd1..a2feb015812 100644 --- a/Mage.Sets/src/mage/cards/a/AshiokSculptorOfFears.java +++ b/Mage.Sets/src/mage/cards/a/AshiokSculptorOfFears.java @@ -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.DrawCardSourceControllerEffect; import mage.abilities.effects.common.MillCardsEachPlayerEffect; @@ -34,7 +33,7 @@ public final class AshiokSculptorOfFears extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.ASHIOK); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +2: Draw a card. Each player puts the top two cards of their library into their graveyard. Ability ability = new LoyaltyAbility( diff --git a/Mage.Sets/src/mage/cards/a/AshmouthDragon.java b/Mage.Sets/src/mage/cards/a/AshmouthDragon.java index aa7fae2f965..f4c11eb6b55 100644 --- a/Mage.Sets/src/mage/cards/a/AshmouthDragon.java +++ b/Mage.Sets/src/mage/cards/a/AshmouthDragon.java @@ -26,7 +26,6 @@ public final class AshmouthDragon extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(4); this.color.setRed(true); - this.transformable = true; this.nightCard = true; // Flying diff --git a/Mage.Sets/src/mage/cards/a/AshnodsBattleGear.java b/Mage.Sets/src/mage/cards/a/AshnodsBattleGear.java index 00edd56f7be..e7a7b4314ab 100644 --- a/Mage.Sets/src/mage/cards/a/AshnodsBattleGear.java +++ b/Mage.Sets/src/mage/cards/a/AshnodsBattleGear.java @@ -29,7 +29,7 @@ public final class AshnodsBattleGear extends CardImpl { this.addAbility(new SkipUntapOptionalAbility()); // {2}, {tap}: Target creature you control gets +2/-2 for as long as Ashnod's Battle Gear remains tapped. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect( - new BoostTargetEffect(2, -2, Duration.Custom), SourceTappedCondition.instance, + new BoostTargetEffect(2, -2, Duration.Custom), SourceTappedCondition.TAPPED, "target creature you control gets +2/-2 for as long as {this} remains tapped"), new ManaCostsImpl("{2}")); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetControlledCreaturePermanent()); diff --git a/Mage.Sets/src/mage/cards/a/AssassinsInk.java b/Mage.Sets/src/mage/cards/a/AssassinsInk.java new file mode 100644 index 00000000000..481772c113f --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AssassinsInk.java @@ -0,0 +1,53 @@ +package mage.cards.a; + +import java.util.UUID; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; +import mage.abilities.hint.ConditionHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.target.common.TargetCreatureOrPlaneswalker; + +/** + * + * @author weirddan455 + */ +public final class AssassinsInk extends CardImpl { + + public AssassinsInk(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{B}{B}"); + + // This spell costs {1} less to cast if you control an artifact and {1} less to cast if you control an enchantment. + Condition artifactCondition = new PermanentsOnTheBattlefieldCondition(StaticFilters.FILTER_PERMANENT_ARTIFACT); + Condition enchantmentCondition = new PermanentsOnTheBattlefieldCondition(StaticFilters.FILTER_PERMANENT_ENCHANTMENT); + Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(1, artifactCondition) + .setText("This spell costs {1} less to cast if you control an artifact")); + ability.addEffect(new SpellCostReductionSourceEffect(1, enchantmentCondition) + .setText("and {1} less to cast if you control an enchantment")); + ability.addHint(new ConditionHint(artifactCondition, "you control an artifact")); + ability.addHint(new ConditionHint(enchantmentCondition, "you control an enchantment")); + ability.setRuleAtTheTop(true); + this.addAbility(ability); + + // Destroy target creature or planeswalker. + this.getSpellAbility().addEffect(new DestroyTargetEffect()); + this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalker()); + } + + private AssassinsInk(final AssassinsInk card) { + super(card); + } + + @Override + public AssassinsInk copy() { + return new AssassinsInk(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AssaultSuit.java b/Mage.Sets/src/mage/cards/a/AssaultSuit.java index 29c8e677e0e..ccacf12f502 100644 --- a/Mage.Sets/src/mage/cards/a/AssaultSuit.java +++ b/Mage.Sets/src/mage/cards/a/AssaultSuit.java @@ -131,7 +131,7 @@ class AssaultSuitGainControlEffect extends OneShotEffect { "Let have " + activePlayer.getLogName() + " gain control of " + equippedCreature.getLogName() + '?', source, game)) { equippedCreature.untap(game); ContinuousEffect effect = new GainControlTargetEffect(Duration.EndOfTurn, activePlayer.getId()); - effect.setTargetPointer(new FixedTarget(equipment.getAttachedTo())); + effect.setTargetPointer(new FixedTarget(equipment.getAttachedTo(), game)); game.addEffect(effect, source); } } diff --git a/Mage.Sets/src/mage/cards/a/AssureAssemble.java b/Mage.Sets/src/mage/cards/a/AssureAssemble.java index b318a5a9dbe..934e7c6e815 100644 --- a/Mage.Sets/src/mage/cards/a/AssureAssemble.java +++ b/Mage.Sets/src/mage/cards/a/AssureAssemble.java @@ -32,7 +32,7 @@ public final class AssureAssemble extends SplitCard { new GainAbilityTargetEffect( IndestructibleAbility.getInstance(), Duration.EndOfTurn - ).setText("It gains indestructible until end of turn.") + ).setText("That creature gains indestructible until end of turn.") ); this.getLeftHalfCard().getSpellAbility().addTarget( new TargetCreaturePermanent() diff --git a/Mage.Sets/src/mage/cards/a/AtarkaWorldRender.java b/Mage.Sets/src/mage/cards/a/AtarkaWorldRender.java index 968eaec1036..4eb29bcf77f 100644 --- a/Mage.Sets/src/mage/cards/a/AtarkaWorldRender.java +++ b/Mage.Sets/src/mage/cards/a/AtarkaWorldRender.java @@ -84,7 +84,7 @@ class AtarkaWorldRenderEffect extends TriggeredAbilityImpl { if (attacker != null && filter.match(attacker, sourceId, controllerId, game)) { for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(attacker.getId())); + effect.setTargetPointer(new FixedTarget(attacker.getId(), game)); } return true; } diff --git a/Mage.Sets/src/mage/cards/a/AtrisOracleOfHalfTruths.java b/Mage.Sets/src/mage/cards/a/AtrisOracleOfHalfTruths.java index a73e886ae70..4b131130bf0 100644 --- a/Mage.Sets/src/mage/cards/a/AtrisOracleOfHalfTruths.java +++ b/Mage.Sets/src/mage/cards/a/AtrisOracleOfHalfTruths.java @@ -34,7 +34,7 @@ public final class AtrisOracleOfHalfTruths extends CardImpl { this.toughness = new MageInt(2); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // When Atris, Oracle of Half-Truths enters the battlefield, target opponent looks at the top three cards of your library and separates them into a face-down pile and a face-up pile. Put one pile into your hand and the other into your graveyard. Ability ability = new EntersBattlefieldTriggeredAbility(new AtrisOracleOfHalfTruthsEffect()); diff --git a/Mage.Sets/src/mage/cards/a/AtsushiTheBlazingSky.java b/Mage.Sets/src/mage/cards/a/AtsushiTheBlazingSky.java new file mode 100644 index 00000000000..84c2e721ca4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AtsushiTheBlazingSky.java @@ -0,0 +1,60 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.ExileTopXMayPlayUntilEndOfTurnEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.game.permanent.token.TreasureToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AtsushiTheBlazingSky extends CardImpl { + + public AtsushiTheBlazingSky(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{R}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.DRAGON); + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // When Atsushi, the Blazing Sky dies, choose one — + // • Exile the top two cards of your library. Until the end of your next turn, you may play those cards. + Ability ability = new DiesSourceTriggeredAbility(new ExileTopXMayPlayUntilEndOfTurnEffect( + 2, false, Duration.UntilEndOfYourNextTurn + ), false); + + // • Create three Treasure tokens. + ability.addMode(new Mode(new CreateTokenEffect(new TreasureToken(), 3))); + this.addAbility(ability); + } + + private AtsushiTheBlazingSky(final AtsushiTheBlazingSky card) { + super(card); + } + + @Override + public AtsushiTheBlazingSky copy() { + return new AtsushiTheBlazingSky(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/Attrition.java b/Mage.Sets/src/mage/cards/a/Attrition.java index a08c7b506e4..87ba1b29fc1 100644 --- a/Mage.Sets/src/mage/cards/a/Attrition.java +++ b/Mage.Sets/src/mage/cards/a/Attrition.java @@ -1,8 +1,6 @@ - package mage.cards.a; import java.util.UUID; -import mage.ObjectColor; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.costs.mana.ManaCostsImpl; @@ -11,10 +9,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Zone; -import static mage.filter.StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ColorPredicate; +import mage.filter.StaticFilters; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; @@ -24,19 +19,13 @@ import mage.target.common.TargetCreaturePermanent; */ public final class Attrition extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public Attrition(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}{B}"); //{B}, Sacrifice a creature: Destroy target nonblack creature. SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new ManaCostsImpl("{B}")); - ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(FILTER_CONTROLLED_CREATURE_SHORT_TEXT))); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT))); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/a/AtzocanArcher.java b/Mage.Sets/src/mage/cards/a/AtzocanArcher.java index d9d0853a918..44df92aefba 100644 --- a/Mage.Sets/src/mage/cards/a/AtzocanArcher.java +++ b/Mage.Sets/src/mage/cards/a/AtzocanArcher.java @@ -41,7 +41,8 @@ public final class AtzocanArcher extends CardImpl { // When Atzocan Archer enters the battlefield, you may have it fight another target creature. Effect effect = new FightTargetSourceEffect(); - effect.setText("you may have it fight another target creature"); + effect.setText("you may have it fight another target creature. " + + "(Each deals damage equal to its power to the other.)"); Ability ability = new EntersBattlefieldTriggeredAbility(effect, true); ability.addTarget(new TargetCreaturePermanent(filter)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/a/AuraThief.java b/Mage.Sets/src/mage/cards/a/AuraThief.java index 0c29f34f9cf..792ea8b9874 100644 --- a/Mage.Sets/src/mage/cards/a/AuraThief.java +++ b/Mage.Sets/src/mage/cards/a/AuraThief.java @@ -71,9 +71,9 @@ class AuraThiefDiesTriggeredEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { boolean ret = false; - for(Permanent enchantment : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_ENCHANTMENT_PERMANENT, source.getControllerId(), source.getControllerId(), game)) { + for(Permanent enchantment : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_ENCHANTMENT, source.getControllerId(), source.getControllerId(), game)) { ContinuousEffect gainControl = new GainControlTargetEffect(Duration.EndOfGame); - gainControl.setTargetPointer(new FixedTarget(enchantment.getId())); + gainControl.setTargetPointer(new FixedTarget(enchantment.getId(), game)); game.addEffect(gainControl, source); ret = true; } diff --git a/Mage.Sets/src/mage/cards/a/Aurification.java b/Mage.Sets/src/mage/cards/a/Aurification.java index 0a70702e639..6d794715bf2 100644 --- a/Mage.Sets/src/mage/cards/a/Aurification.java +++ b/Mage.Sets/src/mage/cards/a/Aurification.java @@ -89,7 +89,7 @@ public final class Aurification extends CardImpl { Permanent permanent = game.getPermanent(event.getSourceId()); if (permanent != null && permanent.isCreature(game)) { for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getSourceId())); + effect.setTargetPointer(new FixedTarget(event.getSourceId(), game)); } return true; } diff --git a/Mage.Sets/src/mage/cards/a/Aurochs.java b/Mage.Sets/src/mage/cards/a/Aurochs.java index ec3c832c9c5..4663e7e33d3 100644 --- a/Mage.Sets/src/mage/cards/a/Aurochs.java +++ b/Mage.Sets/src/mage/cards/a/Aurochs.java @@ -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; @@ -22,13 +22,15 @@ import mage.filter.predicate.mageobject.AnotherPredicate; */ public final class Aurochs extends CardImpl { - private static final FilterAttackingCreature filter1 = new FilterAttackingCreature("other attacking Aurochs"); + private static final FilterAttackingCreature filter = new FilterAttackingCreature("other attacking Aurochs"); static { - filter1.add(SubType.AUROCHS.getPredicate()); - filter1.add(AnotherPredicate.instance); + filter.add(SubType.AUROCHS.getPredicate()); + filter.add(AnotherPredicate.instance); } + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter); + public Aurochs(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{G}"); this.subtype.add(SubType.AUROCHS); @@ -38,8 +40,7 @@ public final class Aurochs extends CardImpl { // Trample this.addAbility(TrampleAbility.getInstance()); // Whenever Aurochs attacks, it gets +1/+0 until end of turn for each other attacking Aurochs. - PermanentsOnBattlefieldCount value = new PermanentsOnBattlefieldCount(filter1, 1); - this.addAbility(new AttacksTriggeredAbility(new BoostSourceEffect(value, StaticValue.get(0), Duration.EndOfTurn, true), false)); + this.addAbility(new AttacksTriggeredAbility(new BoostSourceEffect(xValue, StaticValue.get(0), Duration.EndOfTurn, true, "it"), false)); } private Aurochs(final Aurochs card) { diff --git a/Mage.Sets/src/mage/cards/a/AurochsHerd.java b/Mage.Sets/src/mage/cards/a/AurochsHerd.java index baf2a98d7df..854c6206f8c 100644 --- a/Mage.Sets/src/mage/cards/a/AurochsHerd.java +++ b/Mage.Sets/src/mage/cards/a/AurochsHerd.java @@ -1,10 +1,10 @@ - package mage.cards.a; import java.util.UUID; import mage.MageInt; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.common.continuous.BoostSourceEffect; @@ -35,6 +35,8 @@ public final class AurochsHerd extends CardImpl { filter2.add(AnotherPredicate.instance); } + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter2); + public AurochsHerd(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{5}{G}"); this.subtype.add(SubType.AUROCHS); @@ -47,8 +49,7 @@ public final class AurochsHerd extends CardImpl { this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryPutInHandEffect( new TargetCardInLibrary(filter1), true), true)); // Whenever Aurochs Herd attacks, it gets +1/+0 until end of turn for each other attacking Aurochs. - PermanentsOnBattlefieldCount value = new PermanentsOnBattlefieldCount(filter2, 1); - this.addAbility(new AttacksTriggeredAbility(new BoostSourceEffect(value, StaticValue.get(0), Duration.EndOfTurn, true), false)); + this.addAbility(new AttacksTriggeredAbility(new BoostSourceEffect(xValue, StaticValue.get(0), Duration.EndOfTurn, true, "it"), false)); } private AurochsHerd(final AurochsHerd card) { diff --git a/Mage.Sets/src/mage/cards/a/AurraSingBaneOfJedi.java b/Mage.Sets/src/mage/cards/a/AurraSingBaneOfJedi.java index 0d33dabdcc4..ff4648cf3e4 100644 --- a/Mage.Sets/src/mage/cards/a/AurraSingBaneOfJedi.java +++ b/Mage.Sets/src/mage/cards/a/AurraSingBaneOfJedi.java @@ -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.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageControllerEffect; @@ -38,7 +37,7 @@ public final class AurraSingBaneOfJedi extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.AURRA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +1: You may have {this} deal 2 damage to target creature. If you don't, {this} deals 1 damage to you. Ability ability = new LoyaltyAbility(new AurraSingBaneOfJediEffect(), +1); diff --git a/Mage.Sets/src/mage/cards/a/AuthorOfShadows.java b/Mage.Sets/src/mage/cards/a/AuthorOfShadows.java index 272b6acf96b..312f79a7e8c 100644 --- a/Mage.Sets/src/mage/cards/a/AuthorOfShadows.java +++ b/Mage.Sets/src/mage/cards/a/AuthorOfShadows.java @@ -48,7 +48,7 @@ class AuthorOfShadowsEffect extends OneShotEffect { AuthorOfShadowsEffect() { super(Outcome.Benefit); - staticText = "exile all cards from all opponents' graveyards. Choose a nonland card exiled this way. " + + staticText = "exile all opponents' graveyards. Choose a nonland card exiled this way. " + "You may cast that card for as long as it remains exiled, and you may spend mana " + "as though it were mana of any color to cast that spell"; } diff --git a/Mage.Sets/src/mage/cards/a/AuthorityOfTheConsuls.java b/Mage.Sets/src/mage/cards/a/AuthorityOfTheConsuls.java index 57a3a22bd11..5ad578250d3 100644 --- a/Mage.Sets/src/mage/cards/a/AuthorityOfTheConsuls.java +++ b/Mage.Sets/src/mage/cards/a/AuthorityOfTheConsuls.java @@ -1,42 +1,36 @@ - package mage.cards.a; -import java.util.UUID; -import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.PermanentsEnterBattlefieldTappedEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.filter.common.FilterCreaturePermanent; -import mage.game.Game; -import mage.game.events.EntersTheBattlefieldEvent; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; +import mage.constants.CardType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterOpponentsCreaturePermanent; + +import java.util.UUID; /** - * * @author fireshoes */ public final class AuthorityOfTheConsuls extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } + private static final FilterPermanent filter + = new FilterOpponentsCreaturePermanent("creatures your opponents control"); public AuthorityOfTheConsuls(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}"); // Creatures your opponents control enter the battlefield tapped. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new AuthorityOfTheConsulsTapEffect())); + this.addAbility(new SimpleStaticAbility(new PermanentsEnterBattlefieldTappedEffect(filter))); // Whenever a creature enters the battlefield under an opponent's control, you gain 1 life. - this.addAbility(new EntersBattlefieldAllTriggeredAbility(new GainLifeEffect(1), filter, - "Whenever a creature enters the battlefield under an opponent's control, you gain 1 life.")); + this.addAbility(new EntersBattlefieldAllTriggeredAbility( + new GainLifeEffect(1), filter, "Whenever a creature enters " + + "the battlefield under an opponent's control, you gain 1 life." + )); } private AuthorityOfTheConsuls(final AuthorityOfTheConsuls card) { @@ -48,45 +42,3 @@ public final class AuthorityOfTheConsuls extends CardImpl { return new AuthorityOfTheConsuls(this); } } - -class AuthorityOfTheConsulsTapEffect extends ReplacementEffectImpl { - - AuthorityOfTheConsulsTapEffect() { - super(Duration.WhileOnBattlefield, Outcome.Tap); - staticText = "Creatures your opponents control enter the battlefield tapped"; - } - - AuthorityOfTheConsulsTapEffect(final AuthorityOfTheConsulsTapEffect effect) { - super(effect); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Permanent target = ((EntersTheBattlefieldEvent) event).getTarget(); - if (target != null) { - target.setTapped(true); - } - return false; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (game.getOpponents(source.getControllerId()).contains(event.getPlayerId())) { - Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget(); - if (permanent != null && permanent.isCreature(game)) { - return true; - } - } - return false; - } - - @Override - public AuthorityOfTheConsulsTapEffect copy() { - return new AuthorityOfTheConsulsTapEffect(this); - } -} diff --git a/Mage.Sets/src/mage/cards/a/AutomatedArtificer.java b/Mage.Sets/src/mage/cards/a/AutomatedArtificer.java new file mode 100644 index 00000000000..e39f0d2b9c1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AutomatedArtificer.java @@ -0,0 +1,101 @@ +package mage.cards.a; + +import mage.ConditionalMana; +import mage.MageInt; +import mage.MageObject; +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.costs.Cost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.mana.ConditionalColorlessManaAbility; +import mage.abilities.mana.builder.ConditionalManaBuilder; +import mage.abilities.mana.conditional.ManaCondition; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.Game; +import mage.game.command.Commander; +import mage.game.stack.StackObject; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AutomatedArtificer extends CardImpl { + + public AutomatedArtificer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{2}"); + + this.subtype.add(SubType.ARTIFICER); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // {T}: Add {C}. Spend this mana only to activate an ability or cast an artifact spell. + this.addAbility(new ConditionalColorlessManaAbility( + new TapSourceCost(), 1, new AutomatedArtificerManaBuilder() + )); + } + + private AutomatedArtificer(final AutomatedArtificer card) { + super(card); + } + + @Override + public AutomatedArtificer copy() { + return new AutomatedArtificer(this); + } +} + +class AutomatedArtificerManaBuilder extends ConditionalManaBuilder { + + @Override + public ConditionalMana build(Object... options) { + return new AutomatedArtificerConditionalMana(this.mana); + } + + @Override + public String getRule() { + return "Spend this mana only to activate an ability or cast an artifact spell"; + } +} + +class AutomatedArtificerConditionalMana extends ConditionalMana { + + AutomatedArtificerConditionalMana(Mana mana) { + super(mana); + staticText = "Spend this mana only to activate an ability or cast an artifact spell"; + addCondition(new AutomatedArtificerManaCondition()); + } +} + +class AutomatedArtificerManaCondition extends ManaCondition { + + @Override + public boolean apply(Game game, Ability source) { + if (source == null) { + return false; + } + switch (source.getAbilityType()) { + case MANA: + case ACTIVATED: + return true; + case SPELL: + MageObject object = source.getSourceObject(game); + if (!(object instanceof StackObject) && !game.inCheckPlayableState()) { + return false; + } + if (object instanceof Commander) { + return ((Commander) object).getSourceObject().isArtifact(game); + } + return object.isArtifact(game); + } + return false; + } + + @Override + public boolean apply(Game game, Ability source, UUID originalId, Cost costsToPay) { + return apply(game, source); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AutumnalGloom.java b/Mage.Sets/src/mage/cards/a/AutumnalGloom.java index 41d6cb6a286..fbf7ac43200 100644 --- a/Mage.Sets/src/mage/cards/a/AutumnalGloom.java +++ b/Mage.Sets/src/mage/cards/a/AutumnalGloom.java @@ -25,7 +25,6 @@ public final class AutumnalGloom extends CardImpl { public AutumnalGloom(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}"); - this.transformable = true; this.secondSideCardClazz = AncientOfTheEquinox.class; // {B}: Put the top card of your library into your graveyard. @@ -33,7 +32,7 @@ public final class AutumnalGloom extends CardImpl { // Delirium — At the beginning of your end step, if there are four or more card types among cards in your graveyard, transform Autumnal Gloom. this.addAbility(new TransformAbility()); - Ability ability = new BeginningOfEndStepTriggeredAbility(Zone.BATTLEFIELD, new TransformSourceEffect(true), TargetController.YOU, DeliriumCondition.instance, false); + Ability ability = new BeginningOfEndStepTriggeredAbility(Zone.BATTLEFIELD, new TransformSourceEffect(), TargetController.YOU, DeliriumCondition.instance, false); ability.setAbilityWord(AbilityWord.DELIRIUM); ability.addHint(CardTypesInGraveyardHint.YOU); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/a/AvabruckCaretaker.java b/Mage.Sets/src/mage/cards/a/AvabruckCaretaker.java new file mode 100644 index 00000000000..f2f7336a064 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AvabruckCaretaker.java @@ -0,0 +1,67 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfCombatTriggeredAbility; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.DayboundAbility; +import mage.abilities.keyword.HexproofAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AvabruckCaretaker extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("another target creature you control"); + + static { + filter.add(AnotherPredicate.instance); + } + + public AvabruckCaretaker(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}{G}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WEREWOLF); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + this.secondSideCardClazz = mage.cards.h.HollowhengeHuntmaster.class; + + // Hexproof + this.addAbility(HexproofAbility.getInstance()); + + // At the beginning of combat on your turn, put two +1/+1 counters on another target creature you control. + Ability ability = new BeginningOfCombatTriggeredAbility( + new AddCountersTargetEffect( + CounterType.P1P1.createInstance(2) + ), TargetController.YOU, false + ); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + + // Daybound + this.addAbility(new DayboundAbility()); + } + + private AvabruckCaretaker(final AvabruckCaretaker card) { + super(card); + } + + @Override + public AvabruckCaretaker copy() { + return new AvabruckCaretaker(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AvacynThePurifier.java b/Mage.Sets/src/mage/cards/a/AvacynThePurifier.java index b9b085e3389..de4441a598f 100644 --- a/Mage.Sets/src/mage/cards/a/AvacynThePurifier.java +++ b/Mage.Sets/src/mage/cards/a/AvacynThePurifier.java @@ -2,20 +2,20 @@ package mage.cards.a; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.common.TransformIntoSourceTriggeredAbility; +import mage.abilities.effects.common.DamageAllEffect; +import mage.abilities.effects.common.DamagePlayersEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.AnotherPredicate; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.players.Player; -import java.util.List; import java.util.UUID; /** @@ -23,6 +23,12 @@ import java.util.UUID; */ public final class AvacynThePurifier extends CardImpl { + private static final FilterPermanent filter = new FilterCreaturePermanent("other creature"); + + static { + filter.add(AnotherPredicate.instance); + } + public AvacynThePurifier(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); addSuperType(SuperType.LEGENDARY); @@ -38,7 +44,11 @@ public final class AvacynThePurifier extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When this creature transforms into Avacyn, the Purifier, it deals 3 damage to each other creature and each opponent. - this.addAbility(new AvacynThePurifierAbility()); + Ability ability = new TransformIntoSourceTriggeredAbility( + new DamageAllEffect(3, "it", filter) + ); + ability.addEffect(new DamagePlayersEffect(3, TargetController.OPPONENT).setText("and each opponent")); + this.addAbility(ability); } private AvacynThePurifier(final AvacynThePurifier card) { @@ -50,71 +60,3 @@ public final class AvacynThePurifier extends CardImpl { return new AvacynThePurifier(this); } } - -class AvacynThePurifierAbility extends TriggeredAbilityImpl { - - public AvacynThePurifierAbility() { - super(Zone.BATTLEFIELD, new AvacynThePurifierEffect(), false); - } - - public AvacynThePurifierAbility(final AvacynThePurifierAbility ability) { - super(ability); - } - - @Override - public AvacynThePurifierAbility copy() { - return new AvacynThePurifierAbility(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); - return permanent != null && permanent.isTransformed(); - } - return false; - } - - @Override - public String getRule() { - return "Whenever this creature transforms into Avacyn, the Purifier, it deals 3 damage to each other creature and each opponent."; - } -} - -class AvacynThePurifierEffect extends OneShotEffect { - - public AvacynThePurifierEffect() { - super(Outcome.Damage); - } - - public AvacynThePurifierEffect(final AvacynThePurifierEffect effect) { - super(effect); - } - - @Override - public AvacynThePurifierEffect copy() { - return new AvacynThePurifierEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - FilterCreaturePermanent filter = new FilterCreaturePermanent("each other creature"); - filter.add(AnotherPredicate.instance); - List permanents = game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game); - for (Permanent permanent : permanents) { - permanent.damage(3, source.getSourceId(), source, game, false, true); - } - for (UUID opponentId : game.getOpponents(source.getControllerId())) { - Player opponent = game.getPlayer(opponentId); - if (opponent != null) { - opponent.damage(3, source.getSourceId(), source, game); - } - } - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/a/AvacynianMissionaries.java b/Mage.Sets/src/mage/cards/a/AvacynianMissionaries.java index 44308396600..a9469350d33 100644 --- a/Mage.Sets/src/mage/cards/a/AvacynianMissionaries.java +++ b/Mage.Sets/src/mage/cards/a/AvacynianMissionaries.java @@ -27,12 +27,11 @@ public final class AvacynianMissionaries extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(3); - this.transformable = true; this.secondSideCardClazz = mage.cards.l.LunarchInquisitors.class; // At the beginning of your end step, if Avacynian Missionaries is equipped, transform it. this.addAbility(new TransformAbility()); - this.addAbility(new BeginningOfEndStepTriggeredAbility(Zone.BATTLEFIELD, new TransformSourceEffect(true), TargetController.YOU, EquippedSourceCondition.instance, false)); + this.addAbility(new BeginningOfEndStepTriggeredAbility(Zone.BATTLEFIELD, new TransformSourceEffect(), TargetController.YOU, EquippedSourceCondition.instance, false)); } diff --git a/Mage.Sets/src/mage/cards/a/AvacynsJudgment.java b/Mage.Sets/src/mage/cards/a/AvacynsJudgment.java index 4465bb28992..3fe86e5fb30 100644 --- a/Mage.Sets/src/mage/cards/a/AvacynsJudgment.java +++ b/Mage.Sets/src/mage/cards/a/AvacynsJudgment.java @@ -32,7 +32,7 @@ public final class AvacynsJudgment extends CardImpl { // Avacyn's Judgment deals 2 damage divided as you choose among any number of target creatures and/or players. If Avacyn's Judgment's madness cost was paid, it deals X damage divided as you choose among those creatures and/or players instead. DynamicValue xValue = new AvacynsJudgmentManacostVariableValue(); Effect effect = new DamageMultiEffect(xValue); - effect.setText("{this} deals 2 damage divided as you choose among any number of target creatures and/or players. If {this}'s madness cost was paid, it deals X damage divided as you choose among those creatures and/or players instead."); + effect.setText("{this} deals 2 damage divided as you choose among any number of targets. If this spell's madness cost was paid, it deals X damage divided as you choose among those permanents and/or players instead."); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addTarget(new TargetAnyTargetAmount(xValue)); } diff --git a/Mage.Sets/src/mage/cards/a/AvatarOfTheResolute.java b/Mage.Sets/src/mage/cards/a/AvatarOfTheResolute.java index d3d44beea71..b6d79c1ef1c 100644 --- a/Mage.Sets/src/mage/cards/a/AvatarOfTheResolute.java +++ b/Mage.Sets/src/mage/cards/a/AvatarOfTheResolute.java @@ -14,21 +14,13 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.counters.CounterType; -import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.filter.StaticFilters; /** * * @author jeffwadsworth */ public final class AvatarOfTheResolute extends CardImpl { - - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("other creature you control with a +1/+1 counter on it"); - - static { - filter.add(CounterType.P1P1.getPredicate()); - filter.add(AnotherPredicate.instance); - } public AvatarOfTheResolute(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{G}{G}"); @@ -43,9 +35,11 @@ public final class AvatarOfTheResolute extends CardImpl { this.addAbility(TrampleAbility.getInstance()); // Avatar of the Resolute enters the battlefield with a +1/+1 counter on it for each other creature you control with a +1/+1 counter on it. - DynamicValue numberCounters = new PermanentsOnBattlefieldCount(filter); - this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance(0), numberCounters, true), - "with a +1/+1 counter on it for each other creature you control with a +1/+1 counter on it")); + DynamicValue numberCounters = new PermanentsOnBattlefieldCount(StaticFilters.FILTER_OTHER_CONTROLLED_CREATURE_P1P1); + this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect( + CounterType.P1P1.createInstance(0), numberCounters, true), + "with a +1/+1 counter on it for each other creature you control with a +1/+1 counter on it") + ); } diff --git a/Mage.Sets/src/mage/cards/a/AwakenedAwareness.java b/Mage.Sets/src/mage/cards/a/AwakenedAwareness.java new file mode 100644 index 00000000000..ed286399481 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AwakenedAwareness.java @@ -0,0 +1,87 @@ +package mage.cards.a; + +import java.util.UUID; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.common.ManacostVariableValue; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.common.counter.AddCountersAttachedEffect; +import mage.constants.*; +import mage.abilities.effects.common.AttachEffect; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; + +/** + * + * @author weirddan455 + */ +public final class AwakenedAwareness extends CardImpl { + + public AwakenedAwareness(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{X}{U}{U}"); + + 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())); + + // When Awakened Awareness enters the battlefield, put X +1/+1 counters on enchanted permanent. + this.addAbility(new EntersBattlefieldTriggeredAbility( + new AddCountersAttachedEffect(CounterType.P1P1.createInstance(), ManacostVariableValue.ETB, "enchanted permanent") + )); + + // As long as enchanted permanent is a creature, it has base power and toughness 1/1. + this.addAbility(new SimpleStaticAbility(new AwakenedAwarenessEffect())); + } + + private AwakenedAwareness(final AwakenedAwareness card) { + super(card); + } + + @Override + public AwakenedAwareness copy() { + return new AwakenedAwareness(this); + } +} + +class AwakenedAwarenessEffect extends ContinuousEffectImpl { + + public AwakenedAwarenessEffect() { + super(Duration.WhileOnBattlefield, Layer.PTChangingEffects_7, SubLayer.SetPT_7b, Outcome.UnboostCreature); + this.staticText = "As long as enchanted permanent is a creature, it has base power and toughness 1/1"; + } + + private AwakenedAwarenessEffect(final AwakenedAwarenessEffect effect) { + super(effect); + } + + @Override + public AwakenedAwarenessEffect copy() { + return new AwakenedAwarenessEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent enchantment = source.getSourcePermanentIfItStillExists(game); + if (enchantment != null) { + Permanent creature = game.getPermanent(enchantment.getAttachedTo()); + if (creature != null && creature.isCreature(game)) { + creature.getPower().setValue(1); + creature.getToughness().setValue(1); + return true; + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/a/AwokenDemon.java b/Mage.Sets/src/mage/cards/a/AwokenDemon.java index d7b9e1c6edc..dabf328e6ce 100644 --- a/Mage.Sets/src/mage/cards/a/AwokenDemon.java +++ b/Mage.Sets/src/mage/cards/a/AwokenDemon.java @@ -20,7 +20,6 @@ public final class AwokenDemon extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(4); this.color.setBlack(true); - this.transformable = true; this.nightCard = true; } diff --git a/Mage.Sets/src/mage/cards/a/AwokenHorror.java b/Mage.Sets/src/mage/cards/a/AwokenHorror.java index e57f317b982..1fab7bc271c 100644 --- a/Mage.Sets/src/mage/cards/a/AwokenHorror.java +++ b/Mage.Sets/src/mage/cards/a/AwokenHorror.java @@ -1,29 +1,30 @@ - package mage.cards.a; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.TransformIntoSourceTriggeredAbility; import mage.abilities.effects.common.ReturnToHandFromBattlefieldAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; + +import java.util.UUID; /** - * * @author fireshoes */ public final class AwokenHorror extends CardImpl { + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("non-Horror creatures"); + + static { + filter.add(Predicates.not(SubType.HORROR.getPredicate())); + } + public AwokenHorror(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},""); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); this.subtype.add(SubType.KRAKEN); this.subtype.add(SubType.HORROR); this.power = new MageInt(7); @@ -33,7 +34,7 @@ public final class AwokenHorror extends CardImpl { this.nightCard = true; // When this creature transforms into Awoken Horrow, return all non-Horror creatures to their owners' hands. - this.addAbility(new AwokenHorrorAbility()); + this.addAbility(new TransformIntoSourceTriggeredAbility(new ReturnToHandFromBattlefieldAllEffect(filter))); } private AwokenHorror(final AwokenHorror card) { @@ -45,46 +46,3 @@ public final class AwokenHorror extends CardImpl { return new AwokenHorror(this); } } - -class AwokenHorrorAbility extends TriggeredAbilityImpl { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("non-Horror creatures"); - - static { - filter.add(Predicates.not(SubType.HORROR.getPredicate())); - } - - public AwokenHorrorAbility() { - super(Zone.BATTLEFIELD, new ReturnToHandFromBattlefieldAllEffect(filter), false); - } - - public AwokenHorrorAbility(final AwokenHorrorAbility ability) { - super(ability); - } - - @Override - public AwokenHorrorAbility copy() { - return new AwokenHorrorAbility(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 "Whenever this creature transforms into Awoken Horror, return all non-Horror creatures to their owners' hands."; - } -} diff --git a/Mage.Sets/src/mage/cards/a/AyeshaTanaka.java b/Mage.Sets/src/mage/cards/a/AyeshaTanaka.java index 02ab49e6904..159ba11a224 100644 --- a/Mage.Sets/src/mage/cards/a/AyeshaTanaka.java +++ b/Mage.Sets/src/mage/cards/a/AyeshaTanaka.java @@ -28,7 +28,7 @@ public final class AyeshaTanaka extends CardImpl { private static final FilterStackObject filter = new FilterStackObject("activated ability from an artifact source"); static { - filter.add(new ArtifactSourcePredicate()); + filter.add(ArtifactSourcePredicate.instance); } public AyeshaTanaka(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/a/AyulaQueenAmongBears.java b/Mage.Sets/src/mage/cards/a/AyulaQueenAmongBears.java index cbbd241f965..88b7d742c02 100644 --- a/Mage.Sets/src/mage/cards/a/AyulaQueenAmongBears.java +++ b/Mage.Sets/src/mage/cards/a/AyulaQueenAmongBears.java @@ -28,7 +28,7 @@ public final class AyulaQueenAmongBears extends CardImpl { private static final FilterPermanent filter = new FilterPermanent(SubType.BEAR, "another Bear"); private static final FilterPermanent filter2 = new FilterPermanent(SubType.BEAR, "Bear"); - private static final FilterControlledPermanent filter3 = new FilterControlledPermanent("Bear you controls"); + private static final FilterControlledPermanent filter3 = new FilterControlledPermanent("Bear you control"); static { filter.add(AnotherPredicate.instance); @@ -51,7 +51,7 @@ public final class AyulaQueenAmongBears extends CardImpl { ability.addTarget(new TargetPermanent(filter2)); // • Target Bear you control fights target creature you don't control. - Mode mode = new Mode(new FightTargetsEffect()); + Mode mode = new Mode(new FightTargetsEffect(false)); mode.addTarget(new TargetControlledPermanent(filter3)); mode.addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); ability.addMode(mode); diff --git a/Mage.Sets/src/mage/cards/a/AzcantaTheSunkenRuin.java b/Mage.Sets/src/mage/cards/a/AzcantaTheSunkenRuin.java index 32fa65cc5ff..782169ae2b3 100644 --- a/Mage.Sets/src/mage/cards/a/AzcantaTheSunkenRuin.java +++ b/Mage.Sets/src/mage/cards/a/AzcantaTheSunkenRuin.java @@ -23,7 +23,7 @@ import mage.filter.predicate.Predicates; */ public final class AzcantaTheSunkenRuin extends CardImpl { - private static final FilterCard filter = new FilterCard("noncreature, nonland card"); + private static final FilterCard filter = new FilterCard("a noncreature, nonland card"); static { filter.add(Predicates.not(CardType.CREATURE.getPredicate())); @@ -37,7 +37,6 @@ public final class AzcantaTheSunkenRuin extends CardImpl { // this card is the second face of double-faced card this.nightCard = true; - this.transformable = true; // (Transforms from Search for Azcanta)/ // {T} : Add {U}. diff --git a/Mage.Sets/src/mage/cards/a/AzoriusArrester.java b/Mage.Sets/src/mage/cards/a/AzoriusArrester.java index 3f541ce0d04..689a8c32388 100644 --- a/Mage.Sets/src/mage/cards/a/AzoriusArrester.java +++ b/Mage.Sets/src/mage/cards/a/AzoriusArrester.java @@ -10,8 +10,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; /** @@ -19,12 +18,6 @@ import mage.target.common.TargetCreaturePermanent; * @author LevelX2 */ public final class AzoriusArrester extends CardImpl { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } public AzoriusArrester(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{W}"); @@ -36,7 +29,7 @@ public final class AzoriusArrester extends CardImpl { // When Azorius Arrester enters the battlefield, detain target creature an opponent controls. Ability ability = new EntersBattlefieldTriggeredAbility(new DetainTargetEffect(), false); - TargetCreaturePermanent target = new TargetCreaturePermanent(filter); + TargetCreaturePermanent target = new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE); ability.addTarget(target); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/a/AzorsGateway.java b/Mage.Sets/src/mage/cards/a/AzorsGateway.java index a8d4649e8b2..9e97185af0c 100644 --- a/Mage.Sets/src/mage/cards/a/AzorsGateway.java +++ b/Mage.Sets/src/mage/cards/a/AzorsGateway.java @@ -36,7 +36,6 @@ public final class AzorsGateway extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); this.addSuperType(SuperType.LEGENDARY); - this.transformable = true; this.secondSideCardClazz = mage.cards.s.SanctumOfTheSun.class; // {1}, {T}: Draw a card, then exile a card from your hand. If cards with five or more different converted mana costs are exiled with Azor's Gateway, you gain 5 life, untap Azor's Gateway, and transform it. @@ -94,7 +93,7 @@ class AzorsGatewayEffect extends OneShotEffect { if (usedCMC.size() > 4) { controller.gainLife(4, game, source); new UntapSourceEffect().apply(game, source); - new TransformSourceEffect(true).apply(game, source); + new TransformSourceEffect().apply(game, source); } } return true; diff --git a/Mage.Sets/src/mage/cards/a/AzraSmokeshaper.java b/Mage.Sets/src/mage/cards/a/AzraSmokeshaper.java index 0dd520fc02e..8cd084b490f 100644 --- a/Mage.Sets/src/mage/cards/a/AzraSmokeshaper.java +++ b/Mage.Sets/src/mage/cards/a/AzraSmokeshaper.java @@ -30,7 +30,7 @@ public final class AzraSmokeshaper extends CardImpl { this.toughness = new MageInt(3); // Ninjutsu {1}{B} - this.addAbility(new NinjutsuAbility(new ManaCostsImpl("{1}{B}"))); + this.addAbility(new NinjutsuAbility("{1}{B}")); // When Azra Smokeshaper enters the battlefield, target creature you control gains indestructible until end of turn. Ability ability = new EntersBattlefieldTriggeredAbility(new GainAbilityTargetEffect( diff --git a/Mage.Sets/src/mage/cards/a/AzusasManyJourneys.java b/Mage.Sets/src/mage/cards/a/AzusasManyJourneys.java new file mode 100644 index 00000000000..b62d3daf6f5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AzusasManyJourneys.java @@ -0,0 +1,53 @@ +package mage.cards.a; + +import java.util.UUID; + +import mage.abilities.common.SagaAbility; +import mage.abilities.effects.common.ExileSagaAndReturnTransformedEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.continuous.PlayAdditionalLandsControllerEffect; +import mage.abilities.keyword.TransformAbility; +import mage.constants.Duration; +import mage.constants.SagaChapter; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; + +/** + * + * @author weirddan455 + */ +public final class AzusasManyJourneys extends CardImpl { + + public AzusasManyJourneys(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{G}"); + + this.subtype.add(SubType.SAGA); + this.secondSideCardClazz = mage.cards.l.LikenessOfTheSeeker.class; + + // (As this Saga enters and after your draw step, add a lore counter.) + SagaAbility sagaAbility = new SagaAbility(this); + + // I — You may play an additional land this turn. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, new PlayAdditionalLandsControllerEffect(1, Duration.EndOfTurn)); + + // II — You gain 3 life. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_II, new GainLifeEffect(3)); + + // III — Exile this Saga, then return it to the battlefield transformed under your control. + this.addAbility(new TransformAbility()); + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, new ExileSagaAndReturnTransformedEffect()); + + this.addAbility(sagaAbility); + } + + private AzusasManyJourneys(final AzusasManyJourneys card) { + super(card); + } + + @Override + public AzusasManyJourneys copy() { + return new AzusasManyJourneys(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BackForMore.java b/Mage.Sets/src/mage/cards/b/BackForMore.java index 26ed102c20f..6d2ee7840d9 100644 --- a/Mage.Sets/src/mage/cards/b/BackForMore.java +++ b/Mage.Sets/src/mage/cards/b/BackForMore.java @@ -58,7 +58,8 @@ class BackForMoreEffect extends OneShotEffect { BackForMoreEffect() { super(Outcome.Benefit); staticText = "Return target creature card from your graveyard to the battlefield. " + - "When you do, it fights up to one target creature you don't control."; + "When you do, it fights up to one target creature you don't control. " + + "(Each deals damage equal to its power to the other.)"; } private BackForMoreEffect(final BackForMoreEffect effect) { diff --git a/Mage.Sets/src/mage/cards/b/BaithookAngler.java b/Mage.Sets/src/mage/cards/b/BaithookAngler.java index 200ccb38c7a..1c3126ea98f 100644 --- a/Mage.Sets/src/mage/cards/b/BaithookAngler.java +++ b/Mage.Sets/src/mage/cards/b/BaithookAngler.java @@ -3,7 +3,6 @@ package mage.cards.b; import mage.MageInt; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.keyword.DisturbAbility; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -23,12 +22,10 @@ public final class BaithookAngler extends CardImpl { this.subtype.add(SubType.PEASANT); this.power = new MageInt(2); this.toughness = new MageInt(1); - this.transformable = true; this.secondSideCardClazz = mage.cards.h.HookHauntDrifter.class; // Disturb {1}{U} - this.addAbility(new TransformAbility()); - this.addAbility(new DisturbAbility(new ManaCostsImpl<>("{1}{U}"))); + this.addAbility(new DisturbAbility(this, "{1}{U}")); } private BaithookAngler(final BaithookAngler card) { diff --git a/Mage.Sets/src/mage/cards/b/BakuAltar.java b/Mage.Sets/src/mage/cards/b/BakuAltar.java index 4ff6551b2ca..d0bb684ee49 100644 --- a/Mage.Sets/src/mage/cards/b/BakuAltar.java +++ b/Mage.Sets/src/mage/cards/b/BakuAltar.java @@ -27,7 +27,7 @@ public final class BakuAltar extends CardImpl { public BakuAltar(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{2}"); // Whenever you cast a Spirit or Arcane spell, you may put a ki counter on Baku Altar. - this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.KI.createInstance(1)), StaticFilters.SPIRIT_OR_ARCANE_CARD, true)); + this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.KI.createInstance(1)), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true)); // {2}, {tap}, Remove a ki counter from Baku Altar: Create a 1/1 colorless Spirit creature token. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new SpiritToken(), 1), new GenericManaCost(2)); ability.addCost(new TapSourceCost()); diff --git a/Mage.Sets/src/mage/cards/b/BalaGedThief.java b/Mage.Sets/src/mage/cards/b/BalaGedThief.java index a8c92dfa370..f27ca9a8548 100644 --- a/Mage.Sets/src/mage/cards/b/BalaGedThief.java +++ b/Mage.Sets/src/mage/cards/b/BalaGedThief.java @@ -1,22 +1,19 @@ - package mage.cards.b; -import java.util.List; import java.util.UUID; + import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AllyEntersBattlefieldTriggeredAbility; -import mage.abilities.effects.OneShotEffect; -import mage.cards.*; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.discard.DiscardCardYouChooseTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.FilterCard; +import mage.constants.TargetController; import mage.filter.common.FilterControlledPermanent; -import mage.game.Game; -import mage.players.Player; -import mage.target.TargetCard; import mage.target.TargetPlayer; /** @@ -25,6 +22,8 @@ import mage.target.TargetPlayer; */ public final class BalaGedThief extends CardImpl { + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(new FilterControlledPermanent(SubType.ALLY, "Allies you control"), null); + public BalaGedThief(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}"); this.subtype.add(SubType.HUMAN, SubType.ROGUE, SubType.ALLY); @@ -32,8 +31,10 @@ public final class BalaGedThief extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); - // Whenever Bala Ged Thief or another Ally enters the battlefield under your control, target player reveals a number of cards from their hand equal to the number of Allies you control. You choose one of them. That player discards that card. - Ability ability = new AllyEntersBattlefieldTriggeredAbility(new BalaGedThiefEffect(), false); + // Whenever Bala Ged Thief or another Ally enters the battlefield under your control, + // target player reveals a number of cards from their hand equal to the number of Allies you control. + // You choose one of them. That player discards that card. + Ability ability = new AllyEntersBattlefieldTriggeredAbility(new DiscardCardYouChooseTargetEffect(TargetController.ANY, xValue), false); ability.addTarget(new TargetPlayer()); this.addAbility(ability); } @@ -47,67 +48,3 @@ public final class BalaGedThief extends CardImpl { return new BalaGedThief(this); } } - -class BalaGedThiefEffect extends OneShotEffect { - - public BalaGedThiefEffect() { - super(Outcome.Discard); - this.staticText = "target player reveals a number of cards from their hand equal to the number of Allies you control. You choose one of them. That player discards that card"; - } - - public BalaGedThiefEffect(final BalaGedThiefEffect effect) { - super(effect); - } - - @Override - public BalaGedThiefEffect copy() { - return new BalaGedThiefEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player targetPlayer = game.getPlayer(source.getFirstTarget()); - - if (targetPlayer == null) { - return false; - } - - Player you = game.getPlayer(source.getControllerId()); - - FilterControlledPermanent filter = new FilterControlledPermanent(); - filter.add(SubType.ALLY.getPredicate()); - - int numberOfAllies = game.getBattlefield().countAll(filter, you.getId(), game); - - Cards cardsInHand = new CardsImpl(); - cardsInHand.addAll(targetPlayer.getHand()); - - int count = Math.min(cardsInHand.size(), numberOfAllies); - - TargetCard target = new TargetCard(count, Zone.HAND, new FilterCard()); - Cards revealedCards = new CardsImpl(); - - if (targetPlayer.choose(Outcome.DrawCard, cardsInHand, target, game)) { - List targets = target.getTargets(); - for (UUID targetId : targets) { - Card card = game.getCard(targetId); - if (card != null) { - revealedCards.add(card); - } - } - } - - TargetCard targetInHand = new TargetCard(Zone.HAND, new FilterCard("card to discard")); - - if (!revealedCards.isEmpty()) { - targetPlayer.revealCards("Bala Ged Thief", revealedCards, game); - you.choose(Outcome.Neutral, revealedCards, targetInHand, game); - Card card = revealedCards.get(targetInHand.getFirstTarget(), game); - if (card != null) { - targetPlayer.discard(card, false, source, game); - game.informPlayers("Bala Ged Thief: " + targetPlayer.getLogName() + " discarded " + card.getName()); - } - } - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/b/BalefulBeholder.java b/Mage.Sets/src/mage/cards/b/BalefulBeholder.java index 99de6d32fa4..03b68288ad4 100644 --- a/Mage.Sets/src/mage/cards/b/BalefulBeholder.java +++ b/Mage.Sets/src/mage/cards/b/BalefulBeholder.java @@ -30,7 +30,7 @@ public final class BalefulBeholder extends CardImpl { // When Baleful Beholder enters the battlefield, choose one — // • Antimagic Cone — Each opponent sacrifices an enchantment. - Ability ability = new EntersBattlefieldTriggeredAbility(new SacrificeOpponentsEffect(StaticFilters.FILTER_ENCHANTMENT_PERMANENT)); + Ability ability = new EntersBattlefieldTriggeredAbility(new SacrificeOpponentsEffect(StaticFilters.FILTER_PERMANENT_ENCHANTMENT)); ability.getModes().getMode().withFlavorWord("Antimagic Cone"); // • Fear Ray — Creatures you control gain menace until end of turn. diff --git a/Mage.Sets/src/mage/cards/b/BallistaWatcher.java b/Mage.Sets/src/mage/cards/b/BallistaWatcher.java new file mode 100644 index 00000000000..809f160eda2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BallistaWatcher.java @@ -0,0 +1,53 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.keyword.DayboundAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.target.common.TargetAnyTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BallistaWatcher extends CardImpl { + + public BallistaWatcher(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{R}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.subtype.add(SubType.WEREWOLF); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + this.secondSideCardClazz = mage.cards.b.BallistaWielder.class; + + // {2}{R}, {T}: Ballista Watcher deals 1 damage to any target. + Ability ability = new SimpleActivatedAbility( + new DamageTargetEffect(1), new ManaCostsImpl<>("{2}{R}") + ); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetAnyTarget()); + this.addAbility(ability); + + // Daybound + this.addAbility(new DayboundAbility()); + } + + private BallistaWatcher(final BallistaWatcher card) { + super(card); + } + + @Override + public BallistaWatcher copy() { + return new BallistaWatcher(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BallistaWielder.java b/Mage.Sets/src/mage/cards/b/BallistaWielder.java new file mode 100644 index 00000000000..72f2f8b952a --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BallistaWielder.java @@ -0,0 +1,86 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.combat.CantBlockTargetEffect; +import mage.abilities.keyword.NightboundAbility; +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.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetAnyTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BallistaWielder extends CardImpl { + + public BallistaWielder(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); + + this.subtype.add(SubType.WEREWOLF); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + this.color.setRed(true); + this.nightCard = true; + + // {2}{R}: Ballista Wielder deals 1 damage to any target. A creature dealt damage this way can't block this turn. + Ability ability = new SimpleActivatedAbility(new BallistaWielderEffect(), new ManaCostsImpl<>("{2}{R}")); + ability.addTarget(new TargetAnyTarget()); + this.addAbility(ability); + + // Nightbound + this.addAbility(new NightboundAbility()); + } + + private BallistaWielder(final BallistaWielder card) { + super(card); + } + + @Override + public BallistaWielder copy() { + return new BallistaWielder(this); + } +} + +class BallistaWielderEffect extends OneShotEffect { + + BallistaWielderEffect() { + super(Outcome.Benefit); + staticText = "{this} deals 1 damage to any target. A creature dealt damage this way can't block this turn"; + } + + private BallistaWielderEffect(final BallistaWielderEffect effect) { + super(effect); + } + + @Override + public BallistaWielderEffect copy() { + return new BallistaWielderEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent == null) { + Player player = game.getPlayer(source.getFirstTarget()); + return player != null && player.damage(1, source, game) > 0; + } + if (permanent.damage(1, source, game) > 0) { + game.addEffect(new CantBlockTargetEffect(Duration.EndOfTurn), source); + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/b/BallynockCohort.java b/Mage.Sets/src/mage/cards/b/BallynockCohort.java index a7afdb28be3..539e3f0a082 100644 --- a/Mage.Sets/src/mage/cards/b/BallynockCohort.java +++ b/Mage.Sets/src/mage/cards/b/BallynockCohort.java @@ -35,7 +35,7 @@ public final class BallynockCohort extends CardImpl { filter.add(AnotherPredicate.instance); } - private String rule = "{this} gets +1/+1 as long as you control another white creature"; + private static final String rule = "{this} gets +1/+1 as long as you control another white creature"; public BallynockCohort(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}"); diff --git a/Mage.Sets/src/mage/cards/b/BalothNull.java b/Mage.Sets/src/mage/cards/b/BalothNull.java index c34915f93b8..97c1ab790e6 100644 --- a/Mage.Sets/src/mage/cards/b/BalothNull.java +++ b/Mage.Sets/src/mage/cards/b/BalothNull.java @@ -1,4 +1,3 @@ - package mage.cards.b; import java.util.UUID; @@ -10,7 +9,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.filter.common.FilterCreatureCard; +import mage.filter.StaticFilters; import mage.target.common.TargetCardInYourGraveyard; /** @@ -27,7 +26,7 @@ public final class BalothNull extends CardImpl { // When Baloth Null enters the battlefield, return up to two target creature cards from your graveyard to your hand. Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect(), false); - ability.addTarget(new TargetCardInYourGraveyard(0, 2, new FilterCreatureCard("creature cards from your graveyard"))); + ability.addTarget(new TargetCardInYourGraveyard(0, 2, StaticFilters.FILTER_CARD_CREATURES_YOUR_GRAVEYARD)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/b/BambooGroveArcher.java b/Mage.Sets/src/mage/cards/b/BambooGroveArcher.java new file mode 100644 index 00000000000..d7e63242f5b --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BambooGroveArcher.java @@ -0,0 +1,59 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.keyword.ChannelAbility; +import mage.abilities.keyword.DefenderAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.ReachAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BambooGroveArcher extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with flying"); + + static { + filter.add(new AbilityPredicate(FlyingAbility.class)); + } + + public BambooGroveArcher(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{1}{G}"); + + this.subtype.add(SubType.SNAKE); + this.subtype.add(SubType.ARCHER); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Defender + this.addAbility(DefenderAbility.getInstance()); + + // Reach + this.addAbility(ReachAbility.getInstance()); + + // Channel — {4}{G}, Discard Bamboo Grove Archer: Destroy target creature with flying. + Ability ability = new ChannelAbility("{4}{G}", new DestroyTargetEffect()); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + } + + private BambooGroveArcher(final BambooGroveArcher card) { + super(card); + } + + @Override + public BambooGroveArcher copy() { + return new BambooGroveArcher(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BaneAlleyBroker.java b/Mage.Sets/src/mage/cards/b/BaneAlleyBroker.java index 32ee4804fbe..26b383bded4 100644 --- a/Mage.Sets/src/mage/cards/b/BaneAlleyBroker.java +++ b/Mage.Sets/src/mage/cards/b/BaneAlleyBroker.java @@ -1,7 +1,6 @@ package mage.cards.b; import mage.MageInt; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; @@ -111,12 +110,11 @@ class BaneAlleyBrokerDrawExileEffect extends OneShotEffect { TargetCard target = new TargetCardInHand().withChooseHint("to exile"); controller.chooseTarget(outcome, controller.getHand(), target, source, game); Card card = game.getCard(target.getFirstTarget()); - MageObject sourceObject = source.getSourcePermanentOrLKI(game); - if (card == null || sourceObject == null) { + if (card == null) { return false; } if (!controller.moveCardsToExile( - card, source, game, false, CardUtil.getExileZoneId(game, source), sourceObject.getIdName() + card, source, game, false, CardUtil.getExileZoneId(game, source), CardUtil.getSourceName(game, source) )) { return false; } diff --git a/Mage.Sets/src/mage/cards/b/BaneOfHanweir.java b/Mage.Sets/src/mage/cards/b/BaneOfHanweir.java index a787fbe86ec..db8f96ac32a 100644 --- a/Mage.Sets/src/mage/cards/b/BaneOfHanweir.java +++ b/Mage.Sets/src/mage/cards/b/BaneOfHanweir.java @@ -22,7 +22,6 @@ public final class BaneOfHanweir extends CardImpl { // this card is the second face of double-faced card this.nightCard = true; - this.transformable = true; this.power = new MageInt(5); this.toughness = new MageInt(5); diff --git a/Mage.Sets/src/mage/cards/b/BaneOfTheLiving.java b/Mage.Sets/src/mage/cards/b/BaneOfTheLiving.java index add6dd81d5e..e0765a87d3b 100644 --- a/Mage.Sets/src/mage/cards/b/BaneOfTheLiving.java +++ b/Mage.Sets/src/mage/cards/b/BaneOfTheLiving.java @@ -1,4 +1,3 @@ - package mage.cards.b; import java.util.UUID; @@ -15,7 +14,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; /** * @@ -23,6 +22,8 @@ import mage.filter.common.FilterCreaturePermanent; */ public final class BaneOfTheLiving extends CardImpl { + private static final DynamicValue morphX = new SignInversionDynamicValue(MorphManacostVariableValue.instance); + public BaneOfTheLiving(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{B}{B}"); this.subtype.add(SubType.INSECT); @@ -31,9 +32,9 @@ public final class BaneOfTheLiving extends CardImpl { // Morph {X}{B}{B} this.addAbility(new MorphAbility(this, new ManaCostsImpl("{X}{B}{B}"))); + // When Bane of the Living is turned face up, all creatures get -X/-X until end of turn. - DynamicValue morphX = new SignInversionDynamicValue(MorphManacostVariableValue.instance); - this.addAbility(new TurnedFaceUpSourceTriggeredAbility(new BoostAllEffect(morphX, morphX, Duration.EndOfTurn, new FilterCreaturePermanent("all creatures"), false, "", true))); + this.addAbility(new TurnedFaceUpSourceTriggeredAbility(new BoostAllEffect(morphX, morphX, Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_ALL_CREATURES, false, null, true))); } private BaneOfTheLiving(final BaneOfTheLiving card) { diff --git a/Mage.Sets/src/mage/cards/b/BanebladeScoundrel.java b/Mage.Sets/src/mage/cards/b/BanebladeScoundrel.java new file mode 100644 index 00000000000..7645832128f --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BanebladeScoundrel.java @@ -0,0 +1,55 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; +import mage.abilities.effects.common.continuous.BoostAllEffect; +import mage.abilities.keyword.DayboundAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.BlockingOrBlockedBySourcePredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BanebladeScoundrel extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); + + static { + filter.add(BlockingOrBlockedBySourcePredicate.BLOCKING); + } + + public BanebladeScoundrel(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ROGUE); + this.subtype.add(SubType.WEREWOLF); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + this.secondSideCardClazz = mage.cards.b.BaneclawMarauder.class; + + // Whenever Baneblade Scoundrel becomes blocked, each creature blocking it gets -1/-1 until end of turn. + this.addAbility(new BecomesBlockedSourceTriggeredAbility(new BoostAllEffect( + -1, -1, Duration.EndOfTurn, filter, false + ).setText("each creature blocking it gets -1/-1 until end of turn"), false)); + + // Daybound + this.addAbility(new DayboundAbility()); + } + + private BanebladeScoundrel(final BanebladeScoundrel card) { + super(card); + } + + @Override + public BanebladeScoundrel copy() { + return new BanebladeScoundrel(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BaneclawMarauder.java b/Mage.Sets/src/mage/cards/b/BaneclawMarauder.java new file mode 100644 index 00000000000..2a2394c962e --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BaneclawMarauder.java @@ -0,0 +1,126 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.MageObjectReference; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; +import mage.abilities.common.DiesCreatureTriggeredAbility; +import mage.abilities.effects.common.LoseLifeTargetControllerEffect; +import mage.abilities.effects.common.continuous.BoostAllEffect; +import mage.abilities.keyword.NightboundAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.WatcherScope; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.filter.predicate.permanent.BlockingOrBlockedBySourcePredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.watchers.Watcher; + +import java.util.*; + +/** + * @author TheElk801 + */ +public final class BaneclawMarauder extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); + private static final FilterPermanent filter2 = new FilterCreaturePermanent("a creature blocking {this}"); + + static { + filter.add(BlockingOrBlockedBySourcePredicate.BLOCKING); + filter2.add(BaneclawMarauderPredicate.instance); + } + + public BaneclawMarauder(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); + + this.subtype.add(SubType.WEREWOLF); + this.power = new MageInt(5); + this.toughness = new MageInt(4); + this.color.setBlack(true); + this.nightCard = true; + + // Whenever Baneclaw Marauder becomes blocked, each creature blocking it gets -1/-1 until end of turn. + this.addAbility(new BecomesBlockedSourceTriggeredAbility(new BoostAllEffect( + -1, -1, Duration.EndOfTurn, filter, false + ).setText("each creature blocking it gets -1/-1 until end of turn"), false)); + + // Whenever a creature blocking Baneclaw Marauder dies, its controller loses 1 life. + this.addAbility(new DiesCreatureTriggeredAbility( + new LoseLifeTargetControllerEffect(1) + .setText("that creature's controller loses 1 life"), + false, filter2, true + ), new BaneclawMarauderWatcher()); + + // Nightbound + this.addAbility(new NightboundAbility()); + } + + private BaneclawMarauder(final BaneclawMarauder card) { + super(card); + } + + @Override + public BaneclawMarauder copy() { + return new BaneclawMarauder(this); + } +} + +enum BaneclawMarauderPredicate implements ObjectSourcePlayerPredicate { + instance; + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + return BaneclawMarauderWatcher.check(input.getSourceId(), input.getObject(), game); + } +} + +class BaneclawMarauderWatcher extends Watcher { + + private final Map> blockerMap = new HashMap<>(); + + BaneclawMarauderWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + switch (event.getType()) { + case BLOCKER_DECLARED: + blockerMap + .computeIfAbsent(new MageObjectReference(event.getTargetId(), game), x -> new HashSet<>()) + .add(new MageObjectReference(event.getSourceId(), game)); + return; + case END_COMBAT_STEP_POST: + blockerMap.clear(); + return; + case REMOVED_FROM_COMBAT: + blockerMap + .values() + .stream() + .forEach(set -> set.removeIf(mor -> mor.refersTo(event.getTargetId(), game))); + } + } + + @Override + public void reset() { + super.reset(); + blockerMap.clear(); + } + + static boolean check(UUID sourceId, Permanent blocker, Game game) { + return game.getState() + .getWatcher(BaneclawMarauderWatcher.class) + .blockerMap + .getOrDefault(new MageObjectReference(sourceId, game), Collections.emptySet()) + .stream() + .anyMatch(mor -> mor.refersTo(blocker, game)); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BanisherPriest.java b/Mage.Sets/src/mage/cards/b/BanisherPriest.java index 281c87381ff..76c35eda02e 100644 --- a/Mage.Sets/src/mage/cards/b/BanisherPriest.java +++ b/Mage.Sets/src/mage/cards/b/BanisherPriest.java @@ -14,8 +14,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; @@ -27,12 +26,6 @@ import mage.util.CardUtil; */ public final class BanisherPriest extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public BanisherPriest(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{W}{W}"); this.subtype.add(SubType.HUMAN, SubType.CLERIC); @@ -42,7 +35,7 @@ public final class BanisherPriest extends CardImpl { // When Banisher Priest enters the battlefield, exile target creature an opponent controls until Banisher Priest leaves the battlefield. Ability ability = new EntersBattlefieldTriggeredAbility(new BanisherPriestExileEffect()); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); ability.addEffect(new CreateDelayedTriggeredAbilityEffect(new OnLeaveReturnExiledToBattlefieldAbility())); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/b/BanishingSlash.java b/Mage.Sets/src/mage/cards/b/BanishingSlash.java new file mode 100644 index 00000000000..62df43ea818 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BanishingSlash.java @@ -0,0 +1,59 @@ +package mage.cards.b; + +import mage.abilities.condition.common.ControlArtifactAndEnchantmentCondition; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.hint.common.ControlArtifactAndEnchantmentHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.TappedPredicate; +import mage.game.permanent.token.SamuraiToken; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BanishingSlash extends CardImpl { + + private static final FilterPermanent filter + = new FilterPermanent("artifact, enchantment, or tapped creature"); + + static { + filter.add(Predicates.or( + CardType.ENCHANTMENT.getPredicate(), + CardType.ARTIFACT.getPredicate(), + Predicates.and( + CardType.CREATURE.getPredicate(), + TappedPredicate.TAPPED + ) + )); + } + + public BanishingSlash(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{W}{W}"); + + // Destroy up to one target artifact, enchantment, or tapped creature. Then if you control an artifact and an enchantment, create a 2/2 white Samurai creature token with vigilance. + this.getSpellAbility().addEffect(new DestroyTargetEffect()); + this.getSpellAbility().addTarget(new TargetPermanent(0, 1, filter)); + this.getSpellAbility().addEffect(new ConditionalOneShotEffect( + new CreateTokenEffect(new SamuraiToken()), ControlArtifactAndEnchantmentCondition.instance, "Then " + + "if you control an artifact and an enchantment, create a 2/2 white Samurai creature token with vigilance" + )); + this.getSpellAbility().addHint(ControlArtifactAndEnchantmentHint.instance); + } + + private BanishingSlash(final BanishingSlash card) { + super(card); + } + + @Override + public BanishingSlash copy() { + return new BanishingSlash(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BaralChiefOfCompliance.java b/Mage.Sets/src/mage/cards/b/BaralChiefOfCompliance.java index f22bd18f2fc..6a6bc26cd0f 100644 --- a/Mage.Sets/src/mage/cards/b/BaralChiefOfCompliance.java +++ b/Mage.Sets/src/mage/cards/b/BaralChiefOfCompliance.java @@ -18,7 +18,6 @@ import mage.filter.predicate.Predicates; import java.util.UUID; /** - * * @author fireshoes */ public final class BaralChiefOfCompliance extends CardImpl { @@ -35,7 +34,7 @@ public final class BaralChiefOfCompliance extends CardImpl { public BaralChiefOfCompliance(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); - addSuperType(SuperType.LEGENDARY); + addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.HUMAN, SubType.WIZARD); this.power = new MageInt(1); this.toughness = new MageInt(3); @@ -44,7 +43,7 @@ public final class BaralChiefOfCompliance extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filter, 1))); // Whenever a spell or ability you control counters a spell, you may draw a card. If you do, discard a card. - this.addAbility(new SpellCounteredControllerTriggeredAbility(new DrawDiscardControllerEffect(), true)); + this.addAbility(new SpellCounteredControllerTriggeredAbility(new DrawDiscardControllerEffect(true))); } private BaralChiefOfCompliance(final BaralChiefOfCompliance card) { diff --git a/Mage.Sets/src/mage/cards/b/BarbedSliver.java b/Mage.Sets/src/mage/cards/b/BarbedSliver.java index 6eb399d6442..0b8a258bb3e 100644 --- a/Mage.Sets/src/mage/cards/b/BarbedSliver.java +++ b/Mage.Sets/src/mage/cards/b/BarbedSliver.java @@ -29,7 +29,7 @@ public final class BarbedSliver extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1, 0, Duration.EndOfTurn).setText("this creature gets +1/+0 until end of turn"), - new GenericManaCost(2)), Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS, false))); + new GenericManaCost(2)), Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_ALL_SLIVERS, false))); } private BarbedSliver(final BarbedSliver card) { diff --git a/Mage.Sets/src/mage/cards/b/BarbedWire.java b/Mage.Sets/src/mage/cards/b/BarbedWire.java index e8f42a84fe0..6fcc49d55f3 100644 --- a/Mage.Sets/src/mage/cards/b/BarbedWire.java +++ b/Mage.Sets/src/mage/cards/b/BarbedWire.java @@ -24,7 +24,7 @@ import mage.players.Player; */ public final class BarbedWire extends CardImpl { - private final String rule = "At the beginning of each player's upkeep, " + private static final String rule = "At the beginning of each player's upkeep, " + "Barbed Wire deals 1 damage to that player."; public BarbedWire(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/b/BarkshellBlessing.java b/Mage.Sets/src/mage/cards/b/BarkshellBlessing.java index 15a587256d3..86c46bfb2bd 100644 --- a/Mage.Sets/src/mage/cards/b/BarkshellBlessing.java +++ b/Mage.Sets/src/mage/cards/b/BarkshellBlessing.java @@ -24,7 +24,7 @@ public final class BarkshellBlessing extends CardImpl { this.getSpellAbility().addEffect(new BoostTargetEffect(2, 2, Duration.EndOfTurn)); // Conspire - this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.ONE)); + this.addAbility(new ConspireAbility(ConspireAbility.ConspireTargets.ONE)); } private BarkshellBlessing(final BarkshellBlessing card) { diff --git a/Mage.Sets/src/mage/cards/b/BasriDevotedPaladin.java b/Mage.Sets/src/mage/cards/b/BasriDevotedPaladin.java index c484ec5995a..310a1694c41 100644 --- a/Mage.Sets/src/mage/cards/b/BasriDevotedPaladin.java +++ b/Mage.Sets/src/mage/cards/b/BasriDevotedPaladin.java @@ -3,7 +3,6 @@ package mage.cards.b; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; import mage.abilities.effects.common.continuous.BoostControlledEffect; @@ -39,7 +38,7 @@ public final class BasriDevotedPaladin extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.BASRI); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Put a +1/+1 counter on up to one target creature. It gains vigilance until end of turn. Ability ability = new LoyaltyAbility(new AddCountersTargetEffect( diff --git a/Mage.Sets/src/mage/cards/b/BasriKet.java b/Mage.Sets/src/mage/cards/b/BasriKet.java index 2c4bd29ff94..d1fbff60631 100644 --- a/Mage.Sets/src/mage/cards/b/BasriKet.java +++ b/Mage.Sets/src/mage/cards/b/BasriKet.java @@ -3,7 +3,6 @@ package mage.cards.b; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.GetEmblemEffect; @@ -39,7 +38,7 @@ public final class BasriKet extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.BASRI); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +1: Put a +1/+1 counter on up to one target creature. It gains indestructible until end of turn. Ability ability = new LoyaltyAbility(new AddCountersTargetEffect( diff --git a/Mage.Sets/src/mage/cards/b/BattleCry.java b/Mage.Sets/src/mage/cards/b/BattleCry.java index 0ea82b50e97..c5f60fcb347 100644 --- a/Mage.Sets/src/mage/cards/b/BattleCry.java +++ b/Mage.Sets/src/mage/cards/b/BattleCry.java @@ -1,4 +1,3 @@ - package mage.cards.b; import java.util.UUID; @@ -71,7 +70,7 @@ class BattleCryTriggeredAbility extends DelayedTriggeredAbility { @Override public boolean checkTrigger(GameEvent event, Game game) { - getEffects().get(0).setTargetPointer(new FixedTarget(event.getSourceId())); + getEffects().get(0).setTargetPointer(new FixedTarget(event.getSourceId(), game)); return true; } diff --git a/Mage.Sets/src/mage/cards/b/BattleForBretagard.java b/Mage.Sets/src/mage/cards/b/BattleForBretagard.java index d0a9e664a87..ed2e3c190a5 100644 --- a/Mage.Sets/src/mage/cards/b/BattleForBretagard.java +++ b/Mage.Sets/src/mage/cards/b/BattleForBretagard.java @@ -40,7 +40,7 @@ public final class BattleForBretagard extends CardImpl { this.subtype.add(SubType.SAGA); // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) - SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); + SagaAbility sagaAbility = new SagaAbility(this); // I — Create a 1/1 white Human Warrior creature token. sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, new CreateTokenEffect(new HumanWarriorToken())); diff --git a/Mage.Sets/src/mage/cards/b/BattleOfFrostAndFire.java b/Mage.Sets/src/mage/cards/b/BattleOfFrostAndFire.java index da6b07143c0..57c19aebf73 100644 --- a/Mage.Sets/src/mage/cards/b/BattleOfFrostAndFire.java +++ b/Mage.Sets/src/mage/cards/b/BattleOfFrostAndFire.java @@ -42,7 +42,7 @@ public final class BattleOfFrostAndFire extends CardImpl { this.subtype.add(SubType.SAGA); // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) - SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); + SagaAbility sagaAbility = new SagaAbility(this); // I — Battle of Frost and Fire deals 4 damage to each non-Giant creature and each planeswalker. sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, new DamageAllEffect(4, filter)); diff --git a/Mage.Sets/src/mage/cards/b/BattleSliver.java b/Mage.Sets/src/mage/cards/b/BattleSliver.java index 7bf8276baa1..29cde1e0dc7 100644 --- a/Mage.Sets/src/mage/cards/b/BattleSliver.java +++ b/Mage.Sets/src/mage/cards/b/BattleSliver.java @@ -1,7 +1,5 @@ - package mage.cards.b; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.continuous.BoostControlledEffect; @@ -10,11 +8,11 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.Zone; import mage.filter.StaticFilters; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class BattleSliver extends CardImpl { @@ -27,9 +25,10 @@ public final class BattleSliver extends CardImpl { this.toughness = new MageInt(3); // Sliver creatures you control get +2/+0. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, - new BoostControlledEffect(2, 0, Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS))); - + this.addAbility(new SimpleStaticAbility(new BoostControlledEffect( + 2, 0, Duration.WhileOnBattlefield, + StaticFilters.FILTER_PERMANENT_SLIVERS + ))); } private BattleSliver(final BattleSliver card) { diff --git a/Mage.Sets/src/mage/cards/b/BattlefrontKrushok.java b/Mage.Sets/src/mage/cards/b/BattlefrontKrushok.java index 976c58b1e5f..afc317aa324 100644 --- a/Mage.Sets/src/mage/cards/b/BattlefrontKrushok.java +++ b/Mage.Sets/src/mage/cards/b/BattlefrontKrushok.java @@ -11,6 +11,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; import mage.counters.CounterType; +import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; import java.util.UUID; @@ -21,12 +22,6 @@ import java.util.UUID; */ public final class BattlefrontKrushok extends CardImpl { - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("creature you control with a +1/+1 counter on it"); - - static { - filter.add(CounterType.P1P1.getPredicate()); - } - public BattlefrontKrushok(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{G}"); this.subtype.add(SubType.BEAST); @@ -37,7 +32,9 @@ public final class BattlefrontKrushok extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantBeBlockedByMoreThanOneSourceEffect())); // Each creature you control with a +1/+1 counter on it can't be blocked by more than one creature. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantBeBlockedByMoreThanOneAllEffect(filter))); + this.addAbility(new SimpleStaticAbility( + Zone.BATTLEFIELD, + new CantBeBlockedByMoreThanOneAllEffect(StaticFilters.FILTER_CONTROLLED_CREATURE_P1P1))); } private BattlefrontKrushok(final BattlefrontKrushok card) { diff --git a/Mage.Sets/src/mage/cards/b/BattlegateMimic.java b/Mage.Sets/src/mage/cards/b/BattlegateMimic.java index 7466db08d17..24e080bc71d 100644 --- a/Mage.Sets/src/mage/cards/b/BattlegateMimic.java +++ b/Mage.Sets/src/mage/cards/b/BattlegateMimic.java @@ -31,7 +31,7 @@ public final class BattlegateMimic extends CardImpl { filter.add(new ColorPredicate(ObjectColor.WHITE)); } - private String rule = "Whenever you cast a spell that's both red and white, {this} has base power and toughness 4/2 and gains first strike until end of turn."; + private static final String rule = "Whenever you cast a spell that's both red and white, {this} has base power and toughness 4/2 and gains first strike until end of turn."; public BattlegateMimic(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{R/W}"); diff --git a/Mage.Sets/src/mage/cards/b/BattlegraceAngel.java b/Mage.Sets/src/mage/cards/b/BattlegraceAngel.java index e02d98bc4cc..7d7c48646ee 100644 --- a/Mage.Sets/src/mage/cards/b/BattlegraceAngel.java +++ b/Mage.Sets/src/mage/cards/b/BattlegraceAngel.java @@ -1,11 +1,7 @@ - - package mage.cards.b; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; +import mage.abilities.common.AttacksAloneControlledTriggeredAbility; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.keyword.ExaltedAbility; import mage.abilities.keyword.FlyingAbility; @@ -15,20 +11,16 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; /** - * * @author Loki */ public final class BattlegraceAngel extends CardImpl { - public BattlegraceAngel (UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{W}{W}"); + public BattlegraceAngel(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}{W}"); this.subtype.add(SubType.ANGEL); this.power = new MageInt(4); @@ -41,10 +33,12 @@ public final class BattlegraceAngel extends CardImpl { this.addAbility(new ExaltedAbility()); // Whenever a creature you control attacks alone, it gains lifelink until end of turn. - this.addAbility(new BattlegraceAngelAbility()); + this.addAbility(new AttacksAloneControlledTriggeredAbility(new GainAbilityTargetEffect( + LifelinkAbility.getInstance(), Duration.EndOfTurn + ).setText("it gains lifelink until end of turn"))); } - public BattlegraceAngel (final BattlegraceAngel card) { + public BattlegraceAngel(final BattlegraceAngel card) { super(card); } @@ -54,43 +48,3 @@ public final class BattlegraceAngel extends CardImpl { } } - -class BattlegraceAngelAbility extends TriggeredAbilityImpl { - - public BattlegraceAngelAbility() { - super(Zone.BATTLEFIELD, new GainAbilityTargetEffect(LifelinkAbility.getInstance(), Duration.EndOfTurn), false); - } - - public BattlegraceAngelAbility(final BattlegraceAngelAbility ability) { - super(ability); - } - - @Override - public BattlegraceAngelAbility copy() { - return new BattlegraceAngelAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DECLARED_ATTACKERS; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (game.isActivePlayer(this.controllerId) ) { - if (game.getCombat().attacksAlone()) { - for (Effect effect: this.getEffects()) { - effect.setTargetPointer(new FixedTarget(game.getCombat().getAttackers().get(0))); - } - return true; - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever a creature you control attacks alone, it gains lifelink until end of turn."; - } - -} diff --git a/Mage.Sets/src/mage/cards/b/BattlewiseHoplite.java b/Mage.Sets/src/mage/cards/b/BattlewiseHoplite.java index db612dbb0e1..205b00562ef 100644 --- a/Mage.Sets/src/mage/cards/b/BattlewiseHoplite.java +++ b/Mage.Sets/src/mage/cards/b/BattlewiseHoplite.java @@ -28,7 +28,7 @@ public final class BattlewiseHoplite extends CardImpl { // Heroic - Whenever you cast a spell that targets Battlewise Hoplite, put a +1/+1 counter on Battlewise Hoplite, then scry 1. Ability ability = new HeroicAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance())); - ability.addEffect(new ScryEffect(1)); + ability.addEffect(new ScryEffect(1, false)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/b/BazaarKrovod.java b/Mage.Sets/src/mage/cards/b/BazaarKrovod.java index baf732610c0..50e59ffe4b2 100644 --- a/Mage.Sets/src/mage/cards/b/BazaarKrovod.java +++ b/Mage.Sets/src/mage/cards/b/BazaarKrovod.java @@ -1,22 +1,18 @@ - package mage.cards.b; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.UntapTargetEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; 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.filter.common.FilterAttackingCreature; import mage.filter.predicate.mageobject.AnotherPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; import mage.target.common.TargetAttackingCreature; /** @@ -39,7 +35,8 @@ public final class BazaarKrovod extends CardImpl { this.toughness = new MageInt(5); // Whenever Bazaar Krovod attacks, another target attacking creature gets +0/+2 until end of turn. Untap that creature. - Ability ability = new AttacksTriggeredAbility(new BazaarKrovodEffect(), false); + Ability ability = new AttacksTriggeredAbility(new BoostTargetEffect(0, 2, Duration.EndOfTurn), false); + ability.addEffect(new UntapTargetEffect().setText("Untap that creature")); ability.addTarget(new TargetAttackingCreature(1, 1, filter, false)); this.addAbility(ability); } @@ -53,32 +50,3 @@ public final class BazaarKrovod extends CardImpl { return new BazaarKrovod(this); } } - -class BazaarKrovodEffect extends OneShotEffect { - - public BazaarKrovodEffect() { - super(Outcome.Benefit); - staticText = "another target attacking creature gets +0/+2 until end of turn. Untap that creature"; - } - - public BazaarKrovodEffect(BazaarKrovodEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getFirstTarget()); - if (permanent != null) { - game.addEffect(new BoostTargetEffect(0, 2, Duration.EndOfTurn), source); - permanent.untap(game); - return true; - } - return false; - } - - @Override - public BazaarKrovodEffect copy() { - return new BazaarKrovodEffect(this); - } - -} diff --git a/Mage.Sets/src/mage/cards/b/BazaarOfWonders.java b/Mage.Sets/src/mage/cards/b/BazaarOfWonders.java index f2801df9c35..439c4fd6213 100644 --- a/Mage.Sets/src/mage/cards/b/BazaarOfWonders.java +++ b/Mage.Sets/src/mage/cards/b/BazaarOfWonders.java @@ -91,8 +91,7 @@ class BazaarOfWondersEffect extends OneShotEffect { continue; } if (player.getGraveyard().count(filter2, game) > 0) { - spell.counter(source, game); - return true; + return game.getStack().counter(spell.getId(), source, game); } } return false; diff --git a/Mage.Sets/src/mage/cards/b/BearUmbra.java b/Mage.Sets/src/mage/cards/b/BearUmbra.java index 66a746ee6f2..d0a3fe5b28f 100644 --- a/Mage.Sets/src/mage/cards/b/BearUmbra.java +++ b/Mage.Sets/src/mage/cards/b/BearUmbra.java @@ -1,7 +1,5 @@ - package mage.cards.b; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; @@ -13,33 +11,36 @@ import mage.abilities.keyword.EnchantAbility; import mage.abilities.keyword.TotemArmorAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class BearUmbra extends CardImpl { public BearUmbra(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{G}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}{G}"); this.subtype.add(SubType.AURA); - // Enchant creature TargetPermanent auraTarget = new TargetCreaturePermanent(); this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); - Ability ability = new EnchantAbility(auraTarget.getTargetName()); - this.addAbility(ability); - + this.addAbility(new EnchantAbility(auraTarget.getTargetName())); + // Enchanted creature gets +2/+2 and has "Whenever this creature attacks, untap all lands you control." - Ability attachedAbility = new AttacksTriggeredAbility(new UntapAllLandsControllerEffect(), false); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(2, 2, Duration.WhileOnBattlefield))); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(attachedAbility, AttachmentType.AURA))); - + Ability ability = new SimpleStaticAbility(new BoostEnchantedEffect(2, 2)); + ability.addEffect(new GainAbilityAttachedEffect(new AttacksTriggeredAbility( + new UntapAllLandsControllerEffect(), false + ), AttachmentType.AURA).setText("and has \"Whenever this creature attacks, untap all lands you control.\"")); + // Totem armor this.addAbility(new TotemArmorAbility()); } diff --git a/Mage.Sets/src/mage/cards/b/BearerOfMemory.java b/Mage.Sets/src/mage/cards/b/BearerOfMemory.java new file mode 100644 index 00000000000..3b3db4e573d --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BearerOfMemory.java @@ -0,0 +1,59 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BearerOfMemory extends CardImpl { + + private static final FilterPermanent filter = new FilterCreaturePermanent("enchantment creature"); + + static { + filter.add(CardType.ENCHANTMENT.getPredicate()); + } + + public BearerOfMemory(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{2}{G}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.MONK); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // {5}{G}: Put a +1/+1 counter on target enchantment creature. It gains trample until end of turn. + Ability ability = new SimpleActivatedAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance()), new ManaCostsImpl<>("{5}{G}") + ); + ability.addEffect(new GainAbilityTargetEffect( + TrampleAbility.getInstance() + ).setText("It gains trample until end of turn")); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + } + + private BearerOfMemory(final BearerOfMemory card) { + super(card); + } + + @Override + public BearerOfMemory copy() { + return new BearerOfMemory(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/Bearscape.java b/Mage.Sets/src/mage/cards/b/Bearscape.java index a88823a0c7c..8374503c335 100644 --- a/Mage.Sets/src/mage/cards/b/Bearscape.java +++ b/Mage.Sets/src/mage/cards/b/Bearscape.java @@ -11,7 +11,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Zone; -import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.game.permanent.token.BearToken; import mage.target.common.TargetCardInYourGraveyard; @@ -27,7 +27,7 @@ public final class Bearscape extends CardImpl { // {1}{G}, Exile two cards from your graveyard: Create a 2/2 green Bear creature token. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new BearToken()), new ManaCostsImpl("{1}{G}")); - ability.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard(2, new FilterCard("cards from your graveyard")))); + ability.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard(2, StaticFilters.FILTER_CARDS_FROM_YOUR_GRAVEYARD))); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/b/BefriendingTheMoths.java b/Mage.Sets/src/mage/cards/b/BefriendingTheMoths.java new file mode 100644 index 00000000000..23ce37cae8a --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BefriendingTheMoths.java @@ -0,0 +1,59 @@ +package mage.cards.b; + +import mage.abilities.common.SagaAbility; +import mage.abilities.effects.Effects; +import mage.abilities.effects.common.ExileSagaAndReturnTransformedEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.TransformAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SagaChapter; +import mage.constants.SubType; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BefriendingTheMoths extends CardImpl { + + public BefriendingTheMoths(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{W}"); + + this.subtype.add(SubType.SAGA); + this.secondSideCardClazz = mage.cards.i.ImperialMoth.class; + + // (As this Saga enters and after your draw step, add a lore counter.) + SagaAbility sagaAbility = new SagaAbility(this); + + // I, II — Target creature you control gets +1/+1 and gains flying until end of turn. + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_II, + new Effects( + new BoostTargetEffect(1, 1) + .setText("target creature you control gets +1/+1"), + new GainAbilityTargetEffect(FlyingAbility.getInstance()) + .setText("and gains flying until end of turn") + ), new TargetControlledCreaturePermanent() + ); + + // III — Exile this Saga, then return it to the battlefield transformed under your control. + this.addAbility(new TransformAbility()); + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, new ExileSagaAndReturnTransformedEffect()); + + this.addAbility(sagaAbility); + } + + private BefriendingTheMoths(final BefriendingTheMoths card) { + super(card); + } + + @Override + public BefriendingTheMoths copy() { + return new BefriendingTheMoths(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BeholdTheUnspeakable.java b/Mage.Sets/src/mage/cards/b/BeholdTheUnspeakable.java new file mode 100644 index 00000000000..c42d0c20e54 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BeholdTheUnspeakable.java @@ -0,0 +1,63 @@ +package mage.cards.b; + +import mage.abilities.common.SagaAbility; +import mage.abilities.condition.common.HeckbentCondition; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.ExileSagaAndReturnTransformedEffect; +import mage.abilities.effects.common.continuous.BoostAllEffect; +import mage.abilities.effects.keyword.ScryEffect; +import mage.abilities.keyword.TransformAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SagaChapter; +import mage.constants.SubType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BeholdTheUnspeakable extends CardImpl { + + public BeholdTheUnspeakable(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{U}{U}"); + + this.subtype.add(SubType.SAGA); + this.secondSideCardClazz = mage.cards.v.VisionOfTheUnspeakable.class; + + // (As this Saga enters and after your draw step, add a lore counter.) + SagaAbility sagaAbility = new SagaAbility(this); + + // I — Creatures you don't control get -2/-0 until your next turn. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, new BoostAllEffect( + -2, 0, Duration.UntilYourNextTurn, + StaticFilters.FILTER_CREATURES_YOU_DONT_CONTROL, false + )); + + // II — If you have one or fewer cards in hand, draw four cards. Otherwise, scry 2, then draw two cards. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_II, new ConditionalOneShotEffect( + new DrawCardSourceControllerEffect(4), new ScryEffect(2), + HeckbentCondition.instance, "if you have one or fewer cards in hand, " + + "draw four cards. Otherwise, scry 2, then draw two cards" + ).addOtherwiseEffect(new DrawCardSourceControllerEffect(2))); + + // III — Exile this Saga, then return it to the battlefield transformed under your control. + this.addAbility(new TransformAbility()); + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, new ExileSagaAndReturnTransformedEffect()); + + this.addAbility(sagaAbility); + } + + private BeholdTheUnspeakable(final BeholdTheUnspeakable card) { + super(card); + } + + @Override + public BeholdTheUnspeakable copy() { + return new BeholdTheUnspeakable(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BellBorcaSpectralSergeant.java b/Mage.Sets/src/mage/cards/b/BellBorcaSpectralSergeant.java index 98218038742..8fb3f13aa2f 100644 --- a/Mage.Sets/src/mage/cards/b/BellBorcaSpectralSergeant.java +++ b/Mage.Sets/src/mage/cards/b/BellBorcaSpectralSergeant.java @@ -133,8 +133,10 @@ class BellBorcaSpectralSergeantWatcher extends Watcher { return; } int cmc = card.getManaValue(); - for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, game.getActivePlayerId(), game)) { - if (permanent == null) { + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filter, game)) { + if (permanent == null + || cmcMap.get(permanent.getId()) != null + && cmcMap.get(permanent.getId()) >= cmc) { continue; } cmcMap.put(permanent.getId(), cmc); diff --git a/Mage.Sets/src/mage/cards/b/BelligerentGuest.java b/Mage.Sets/src/mage/cards/b/BelligerentGuest.java new file mode 100644 index 00000000000..5f5230a3d30 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BelligerentGuest.java @@ -0,0 +1,44 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.BloodToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BelligerentGuest extends CardImpl { + + public BelligerentGuest(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); + + this.subtype.add(SubType.VAMPIRE); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Whenever Belligerent Guest deals combat damage to a player, create a Blood token. + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility( + new CreateTokenEffect(new BloodToken()), false + )); + } + + private BelligerentGuest(final BelligerentGuest card) { + super(card); + } + + @Override + public BelligerentGuest copy() { + return new BelligerentGuest(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BelovedBeggar.java b/Mage.Sets/src/mage/cards/b/BelovedBeggar.java new file mode 100644 index 00000000000..a07558efdd3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BelovedBeggar.java @@ -0,0 +1,40 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.keyword.DisturbAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author weirddan455 + */ +public final class BelovedBeggar extends CardImpl { + + public BelovedBeggar(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.PEASANT); + this.power = new MageInt(0); + this.toughness = new MageInt(4); + + this.secondSideCardClazz = mage.cards.g.GenerousSoul.class; + + // Disturb {4}{W}{W} + this.addAbility(new DisturbAbility(this, "{4}{W}{W}")); + } + + private BelovedBeggar(final BelovedBeggar card) { + super(card); + } + + @Override + public BelovedBeggar copy() { + return new BelovedBeggar(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BeltOfGiantStrength.java b/Mage.Sets/src/mage/cards/b/BeltOfGiantStrength.java index 72ccd75714a..b16c69d1ffa 100644 --- a/Mage.Sets/src/mage/cards/b/BeltOfGiantStrength.java +++ b/Mage.Sets/src/mage/cards/b/BeltOfGiantStrength.java @@ -51,10 +51,28 @@ enum BeltOfGiantStrengthAdjuster implements CostAdjuster { @Override public void adjustCosts(Ability ability, Game game) { - Permanent permanent = game.getPermanent(ability.getFirstTarget()); - if (permanent == null) { - return; + if (game.inCheckPlayableState()) { + int maxPower = 0; + for (UUID permId : CardUtil.getAllPossibleTargets(ability, game)) { + Permanent permanent = game.getPermanent(permId); + if (permanent != null) { + int power = permanent.getPower().getValue(); + if (power > maxPower) { + maxPower = power; + } + } + } + if (maxPower > 0) { + CardUtil.reduceCost(ability, maxPower); + } + } else { + Permanent permanent = game.getPermanent(ability.getFirstTarget()); + if (permanent != null) { + int power = permanent.getPower().getValue(); + if (power > 0) { + CardUtil.reduceCost(ability, power); + } + } } - CardUtil.reduceCost(ability, Integer.max(permanent.getPower().getValue(), 0)); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/b/BenevolentGeist.java b/Mage.Sets/src/mage/cards/b/BenevolentGeist.java index f54295208ea..814302e5078 100644 --- a/Mage.Sets/src/mage/cards/b/BenevolentGeist.java +++ b/Mage.Sets/src/mage/cards/b/BenevolentGeist.java @@ -28,7 +28,6 @@ public final class BenevolentGeist extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); this.color.setBlue(true); - this.transformable = true; this.nightCard = true; // Flying diff --git a/Mage.Sets/src/mage/cards/b/BenevolentOffering.java b/Mage.Sets/src/mage/cards/b/BenevolentOffering.java index d454671c0f4..c6bd066c468 100644 --- a/Mage.Sets/src/mage/cards/b/BenevolentOffering.java +++ b/Mage.Sets/src/mage/cards/b/BenevolentOffering.java @@ -83,7 +83,7 @@ class BenevolentOfferingEffect2 extends OneShotEffect { BenevolentOfferingEffect2() { super(Outcome.Sacrifice); - this.staticText = "Choose an opponent. You gain 2 life for each creature you control and that player gains 2 life for each creature they control"; + this.staticText = "
Choose an opponent. You gain 2 life for each creature you control and that player gains 2 life for each creature they control"; } BenevolentOfferingEffect2(final BenevolentOfferingEffect2 effect) { diff --git a/Mage.Sets/src/mage/cards/b/BereavedSurvivor.java b/Mage.Sets/src/mage/cards/b/BereavedSurvivor.java index 69b57d3edae..463b0716483 100644 --- a/Mage.Sets/src/mage/cards/b/BereavedSurvivor.java +++ b/Mage.Sets/src/mage/cards/b/BereavedSurvivor.java @@ -24,13 +24,12 @@ public final class BereavedSurvivor extends CardImpl { this.subtype.add(SubType.PEASANT); this.power = new MageInt(2); this.toughness = new MageInt(1); - this.transformable = true; this.secondSideCardClazz = mage.cards.d.DauntlessAvenger.class; // When another creature you control dies, transform Bereaved Survivor. this.addAbility(new TransformAbility()); this.addAbility(new DiesCreatureTriggeredAbility( - new TransformSourceEffect(true), false, + new TransformSourceEffect(), false, StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE ).setTriggerPhrase("When another creature you control dies, ")); } diff --git a/Mage.Sets/src/mage/cards/b/Berserk.java b/Mage.Sets/src/mage/cards/b/Berserk.java index c009938457d..ab4ac242365 100644 --- a/Mage.Sets/src/mage/cards/b/Berserk.java +++ b/Mage.Sets/src/mage/cards/b/Berserk.java @@ -1,4 +1,3 @@ - package mage.cards.b; import mage.MageObjectReference; @@ -131,7 +130,7 @@ class BerserkDestroyEffect extends OneShotEffect { if (controller != null) { //create delayed triggered ability Effect effect = new BerserkDelayedDestroyEffect(); - effect.setTargetPointer(new FixedTarget(this.getTargetPointer().getFirst(game, source))); + effect.setTargetPointer(new FixedTarget(this.getTargetPointer().getFirst(game, source), game)); AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect); game.addDelayedTriggeredAbility(delayedAbility, source); return true; diff --git a/Mage.Sets/src/mage/cards/b/BerserkersFrenzy.java b/Mage.Sets/src/mage/cards/b/BerserkersFrenzy.java new file mode 100644 index 00000000000..2dc7617417e --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BerserkersFrenzy.java @@ -0,0 +1,107 @@ +package mage.cards.b; + +import mage.abilities.Ability; +import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility; +import mage.abilities.condition.Condition; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.RollDieWithResultTableEffect; +import mage.abilities.effects.common.combat.BlocksIfAbleTargetEffect; +import mage.abilities.effects.common.combat.ChooseBlockersEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; +import mage.constants.*; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetpointer.FixedTargets; +import mage.watchers.common.ControlCombatRedundancyWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BerserkersFrenzy extends CardImpl { + + private static final Hint hint = new ConditionHint(BerserkersFrenzyCondition.instance, "Can be cast"); + + public BerserkersFrenzy(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{R}"); + + // Cast this spell only before combat or during combat before blockers are declared. + this.addAbility(new CastOnlyDuringPhaseStepSourceAbility( + null, null, BerserkersFrenzyCondition.instance, + "Cast this spell only before combat or during combat before blockers are declared" + ).addHint(hint)); + + // Roll two d20 and ignore the lower roll. + RollDieWithResultTableEffect effect = new RollDieWithResultTableEffect( + 20, "roll two d20 and ignore the lower roll", StaticValue.get(0), 1 + ); + + // 1-14 | Choose any number of creatures. They block this turn if able. + effect.addTableEntry(1, 14, new BerserkersFrenzyEffect()); + + // 15-20 | You choose which creatures block this turn and how those creatures block. + effect.addTableEntry(15, 20, new ChooseBlockersEffect(Duration.EndOfTurn)); + this.getSpellAbility().addEffect(effect); + this.getSpellAbility().addWatcher(new ControlCombatRedundancyWatcher()); + } + + private BerserkersFrenzy(final BerserkersFrenzy card) { + super(card); + } + + @Override + public BerserkersFrenzy copy() { + return new BerserkersFrenzy(this); + } +} + +enum BerserkersFrenzyCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + if (game.getPhase().getType() == TurnPhase.COMBAT) { + return game.getStep().getType().isBefore(PhaseStep.DECLARE_BLOCKERS); + } + return !game.getTurn().isDeclareAttackersStepStarted(); + } +} + +class BerserkersFrenzyEffect extends OneShotEffect { + + BerserkersFrenzyEffect() { + super(Outcome.Benefit); + staticText = "choose any number of creatures. They block this turn if able"; + } + + private BerserkersFrenzyEffect(final BerserkersFrenzyEffect effect) { + super(effect); + } + + @Override + public BerserkersFrenzyEffect copy() { + return new BerserkersFrenzyEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + TargetPermanent target = new TargetCreaturePermanent(0, Integer.MAX_VALUE); + target.setNotTarget(true); + player.choose(outcome, target, source.getSourceId(), game); + game.addEffect(new BlocksIfAbleTargetEffect(Duration.EndOfTurn) + .setTargetPointer(new FixedTargets(new CardsImpl(target.getTargets()), game)), source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/b/BerserkersOnslaught.java b/Mage.Sets/src/mage/cards/b/BerserkersOnslaught.java index bee14ad6270..3a0d056aaf4 100644 --- a/Mage.Sets/src/mage/cards/b/BerserkersOnslaught.java +++ b/Mage.Sets/src/mage/cards/b/BerserkersOnslaught.java @@ -1,4 +1,3 @@ - package mage.cards.b; import java.util.UUID; @@ -9,8 +8,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Zone; -import mage.filter.common.FilterAttackingCreature; +import mage.filter.StaticFilters; /** * @@ -22,9 +20,7 @@ public final class BerserkersOnslaught extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{3}{R}{R}"); // Attacking creatures you control have double strike. - GainAbilityControlledEffect gainEffect = new GainAbilityControlledEffect(DoubleStrikeAbility.getInstance(), Duration.WhileOnBattlefield, new FilterAttackingCreature("Attacking creatures"), false); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, gainEffect)); - + this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect(DoubleStrikeAbility.getInstance(), Duration.WhileOnBattlefield, StaticFilters.FILTER_ATTACKING_CREATURES, false))); } private BerserkersOnslaught(final BerserkersOnslaught card) { diff --git a/Mage.Sets/src/mage/cards/b/Besmirch.java b/Mage.Sets/src/mage/cards/b/Besmirch.java index 9a475034502..f510600418d 100644 --- a/Mage.Sets/src/mage/cards/b/Besmirch.java +++ b/Mage.Sets/src/mage/cards/b/Besmirch.java @@ -1,10 +1,6 @@ - package mage.cards.b; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.UntapTargetEffect; import mage.abilities.effects.common.combat.GoadTargetEffect; @@ -21,8 +17,9 @@ import mage.target.common.TargetCreaturePermanent; import mage.target.targetpointer.FixedTarget; import mage.target.targetpointer.TargetPointer; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class Besmirch extends CardImpl { @@ -64,27 +61,22 @@ class BesmirchEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { if (game.getPermanent(source.getFirstTarget()) != null) { - TargetPointer target = new FixedTarget(source.getFirstTarget()); + TargetPointer target = new FixedTarget(source.getFirstTarget(), game); // gain control - ContinuousEffect effect = new GainControlTargetEffect(Duration.EndOfTurn); - effect.setTargetPointer(target); - game.addEffect(effect, source); + game.addEffect(new GainControlTargetEffect(Duration.EndOfTurn) + .setTargetPointer(target), source); // haste - effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn); - effect.setTargetPointer(target); - game.addEffect(effect, source); + game.addEffect(new GainAbilityTargetEffect( + HasteAbility.getInstance(), Duration.EndOfTurn + ).setTargetPointer(target), source); // goad - Effect effect2 = new GoadTargetEffect(); - effect2.setTargetPointer(target); - effect2.apply(game, source); + game.addEffect(new GoadTargetEffect().setTargetPointer(target), source); // untap - effect2 = new UntapTargetEffect(); - effect2.setTargetPointer(target); - effect2.apply(game, source); + new UntapTargetEffect().setTargetPointer(target).apply(game, source); return true; } diff --git a/Mage.Sets/src/mage/cards/b/BestialMenace.java b/Mage.Sets/src/mage/cards/b/BestialMenace.java index 75e1740db7d..4486ec31b81 100644 --- a/Mage.Sets/src/mage/cards/b/BestialMenace.java +++ b/Mage.Sets/src/mage/cards/b/BestialMenace.java @@ -18,6 +18,7 @@ public final class BestialMenace extends CardImpl { public BestialMenace(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{G}{G}"); + // Create a 1/1 green Snake creature token, a 2/2 green Wolf creature token, and a 3/3 green Elephant creature token. this.getSpellAbility().addEffect(new CreateTokenEffect(new SnakeToken())); this.getSpellAbility().addEffect(new CreateTokenEffect(new WolfToken()).setText(", a 2/2 green Wolf creature token")); this.getSpellAbility().addEffect(new CreateTokenEffect(new ElephantToken()).setText(", and a 3/3 green Elephant creature token")); diff --git a/Mage.Sets/src/mage/cards/b/Betrayal.java b/Mage.Sets/src/mage/cards/b/Betrayal.java index cd56c028f6e..da2508db48e 100644 --- a/Mage.Sets/src/mage/cards/b/Betrayal.java +++ b/Mage.Sets/src/mage/cards/b/Betrayal.java @@ -10,8 +10,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; @@ -22,18 +21,12 @@ import java.util.UUID; */ public final class Betrayal extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public Betrayal(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{U}"); this.subtype.add(SubType.AURA); // Enchant creature an opponent controls - TargetPermanent auraTarget = new TargetCreaturePermanent(filter); + TargetPermanent auraTarget = new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE); this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.Detriment)); Ability ability = new EnchantAbility(auraTarget.getTargetName()); diff --git a/Mage.Sets/src/mage/cards/b/BindingGeist.java b/Mage.Sets/src/mage/cards/b/BindingGeist.java new file mode 100644 index 00000000000..df255c28543 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BindingGeist.java @@ -0,0 +1,47 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.keyword.DisturbAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.target.common.TargetOpponentsCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BindingGeist extends CardImpl { + + public BindingGeist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); + + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(3); + this.toughness = new MageInt(1); + this.secondSideCardClazz = mage.cards.s.SpectralBinding.class; + + // Whenever Binding Geist attacks, target creature an opponent controls gets -2/-0 until end of turn. + Ability ability = new AttacksTriggeredAbility(new BoostTargetEffect(-2, 0)); + ability.addTarget(new TargetOpponentsCreaturePermanent()); + this.addAbility(ability); + + // Disturb {1}{U} + this.addAbility(new DisturbAbility(this, "{1}{U}")); + } + + private BindingGeist(final BindingGeist card) { + super(card); + } + + @Override + public BindingGeist copy() { + return new BindingGeist(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BindingTheOldGods.java b/Mage.Sets/src/mage/cards/b/BindingTheOldGods.java index 828bf9dcc0f..2651b188742 100644 --- a/Mage.Sets/src/mage/cards/b/BindingTheOldGods.java +++ b/Mage.Sets/src/mage/cards/b/BindingTheOldGods.java @@ -37,7 +37,7 @@ public final class BindingTheOldGods extends CardImpl { this.subtype.add(SubType.SAGA); // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) - SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); + SagaAbility sagaAbility = new SagaAbility(this); // I — Destroy target nonland permanent an opponent controls. sagaAbility.addChapterEffect( this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_I, diff --git a/Mage.Sets/src/mage/cards/b/BiolumeEgg.java b/Mage.Sets/src/mage/cards/b/BiolumeEgg.java new file mode 100644 index 00000000000..6c632c70b72 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BiolumeEgg.java @@ -0,0 +1,91 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SacrificeSourceTriggeredAbility; +import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.keyword.ScryEffect; +import mage.abilities.keyword.DefenderAbility; +import mage.abilities.keyword.TransformAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BiolumeEgg extends CardImpl { + + public BiolumeEgg(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); + + this.subtype.add(SubType.SERPENT); + this.subtype.add(SubType.EGG); + this.power = new MageInt(0); + this.toughness = new MageInt(4); + this.secondSideCardClazz = mage.cards.b.BiolumeSerpent.class; + + // Defender + this.addAbility(DefenderAbility.getInstance()); + + // When Biolume Egg enters the battlefield, scry 2. + this.addAbility(new EntersBattlefieldTriggeredAbility(new ScryEffect(2))); + + // When you sacrifice Biolume Egg, return it to the battlefield transformed under its owner's control at the beginning of the next end step. + this.addAbility(new TransformAbility()); + this.addAbility(new SacrificeSourceTriggeredAbility(new CreateDelayedTriggeredAbilityEffect( + new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new BiolumeEggEffect()), true + ).setText("return it to the battlefield transformed under its owner's control at the beginning of the next end step"), false)); + } + + private BiolumeEgg(final BiolumeEgg card) { + super(card); + } + + @Override + public BiolumeEgg copy() { + return new BiolumeEgg(this); + } +} + +class BiolumeEggEffect extends OneShotEffect { + + public BiolumeEggEffect() { + super(Outcome.PutCardInPlay); + this.staticText = "return it to the battlefield transformed under your control"; + } + + public BiolumeEggEffect(final BiolumeEggEffect effect) { + super(effect); + } + + @Override + public BiolumeEggEffect copy() { + return new BiolumeEggEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + Card card = game.getCard(getTargetPointer().getFirst(game, source)); + if (card != null) { + game.getState().setValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + card.getId(), Boolean.TRUE); + controller.moveCards(card, Zone.BATTLEFIELD, source, game, false, false, true, null); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/b/BiolumeSerpent.java b/Mage.Sets/src/mage/cards/b/BiolumeSerpent.java new file mode 100644 index 00000000000..80c351540fe --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BiolumeSerpent.java @@ -0,0 +1,49 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.effects.common.combat.CantBeBlockedSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.common.FilterControlledPermanent; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BiolumeSerpent extends CardImpl { + + private static final FilterControlledPermanent filter + = new FilterControlledPermanent(SubType.ISLAND, "Islands"); + + public BiolumeSerpent(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); + + this.subtype.add(SubType.SERPENT); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + this.color.setBlue(true); + this.nightCard = true; + + // Sacrifice two Islands: Biolume Serpent can't be blocked this turn. + this.addAbility(new SimpleActivatedAbility( + new CantBeBlockedSourceEffect(Duration.EndOfTurn), + new SacrificeTargetCost(new TargetControlledPermanent(2, filter)) + )); + } + + private BiolumeSerpent(final BiolumeSerpent card) { + super(card); + } + + @Override + public BiolumeSerpent copy() { + return new BiolumeSerpent(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BiomassMutation.java b/Mage.Sets/src/mage/cards/b/BiomassMutation.java index 9bccb523c1a..0ad4d1fc7db 100644 --- a/Mage.Sets/src/mage/cards/b/BiomassMutation.java +++ b/Mage.Sets/src/mage/cards/b/BiomassMutation.java @@ -9,7 +9,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; /** * @@ -23,7 +23,7 @@ public final class BiomassMutation extends CardImpl { // Creatures you control have base power and toughness X/X until end of turn. DynamicValue variableMana = ManacostVariableValue.REGULAR; - this.getSpellAbility().addEffect(new SetPowerToughnessAllEffect(variableMana, variableMana, Duration.EndOfTurn, new FilterControlledCreaturePermanent("Creatures you control"), true)); + this.getSpellAbility().addEffect(new SetPowerToughnessAllEffect(variableMana, variableMana, Duration.EndOfTurn, StaticFilters.FILTER_CONTROLLED_CREATURES, true)); } private BiomassMutation(final BiomassMutation card) { diff --git a/Mage.Sets/src/mage/cards/b/BirdAdmirer.java b/Mage.Sets/src/mage/cards/b/BirdAdmirer.java index 3ee4bc0bd70..b724b703f26 100644 --- a/Mage.Sets/src/mage/cards/b/BirdAdmirer.java +++ b/Mage.Sets/src/mage/cards/b/BirdAdmirer.java @@ -3,7 +3,6 @@ package mage.cards.b; import mage.MageInt; import mage.abilities.keyword.DayboundAbility; import mage.abilities.keyword.ReachAbility; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -24,14 +23,12 @@ public final class BirdAdmirer extends CardImpl { this.subtype.add(SubType.WEREWOLF); this.power = new MageInt(1); this.toughness = new MageInt(4); - this.transformable = true; this.secondSideCardClazz = mage.cards.w.WingShredder.class; // Reach this.addAbility(ReachAbility.getInstance()); // Daybound - this.addAbility(new TransformAbility()); this.addAbility(new DayboundAbility()); } diff --git a/Mage.Sets/src/mage/cards/b/BishopOfBinding.java b/Mage.Sets/src/mage/cards/b/BishopOfBinding.java index 852003ca560..4b346842373 100644 --- a/Mage.Sets/src/mage/cards/b/BishopOfBinding.java +++ b/Mage.Sets/src/mage/cards/b/BishopOfBinding.java @@ -20,7 +20,7 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.TargetController; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.game.ExileZone; import mage.game.Game; @@ -34,12 +34,6 @@ import mage.util.CardUtil; */ public final class BishopOfBinding extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public BishopOfBinding(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); @@ -50,7 +44,7 @@ public final class BishopOfBinding extends CardImpl { // When Bishop of Binding enters the battlefield, exile target creature an opponent controls until Bishop of Binding leaves the battlefield. Ability ability = new EntersBattlefieldTriggeredAbility(new BishopOfBindingExileEffect()); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); ability.addEffect(new CreateDelayedTriggeredAbilityEffect(new OnLeaveReturnExiledToBattlefieldAbility())); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/b/BitingPalmNinja.java b/Mage.Sets/src/mage/cards/b/BitingPalmNinja.java new file mode 100644 index 00000000000..8197edfd741 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BitingPalmNinja.java @@ -0,0 +1,59 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.costs.common.RemoveCountersSourceCost; +import mage.abilities.effects.common.DoWhenCostPaid; +import mage.abilities.effects.common.ExileCardYouChooseTargetOpponentEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.NinjutsuAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BitingPalmNinja extends CardImpl { + + public BitingPalmNinja(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.NINJA); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Ninjutsu {2}{B} + this.addAbility(new NinjutsuAbility("{2}{B}")); + + // Biting-Palm Ninja enters the battlefield with a menace counter on it. + this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect( + CounterType.MENACE.createInstance(1) + ), "with a menace counter on it")); + + // Whenever Biting-Palm Ninja deals combat damage to a player, you may remove a menace counter from it. When you do, that player reveals their hand and you choose a nonland card from it. Exile that card. + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility( + new DoWhenCostPaid(new ReflexiveTriggeredAbility( + new ExileCardYouChooseTargetOpponentEffect(StaticFilters.FILTER_CARD_A_NON_LAND), false, + "that player reveals their hand and you choose a nonland card from it. Exile that card" + ), new RemoveCountersSourceCost(CounterType.MENACE.createInstance()).setText("remove a menace counter from it"), "Remove a menace counter?"), false, true + )); + } + + private BitingPalmNinja(final BitingPalmNinja card) { + super(card); + } + + @Override + public BitingPalmNinja copy() { + return new BitingPalmNinja(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/Bitterblossom.java b/Mage.Sets/src/mage/cards/b/Bitterblossom.java index 6dccf29d3d0..fc09f2e01af 100644 --- a/Mage.Sets/src/mage/cards/b/Bitterblossom.java +++ b/Mage.Sets/src/mage/cards/b/Bitterblossom.java @@ -25,7 +25,7 @@ public final class Bitterblossom extends CardImpl { // At the beginning of your upkeep, you lose 1 life and create a 1/1 black Faerie Rogue creature token with flying. Ability ability = new BeginningOfUpkeepTriggeredAbility(new LoseLifeSourceControllerEffect(1), TargetController.YOU, false); - ability.addEffect(new CreateTokenEffect(new FaerieRogueToken(), 1)); + ability.addEffect(new CreateTokenEffect(new FaerieRogueToken(), 1).concatBy("and")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/b/BjornaNightfallAlchemist.java b/Mage.Sets/src/mage/cards/b/BjornaNightfallAlchemist.java new file mode 100644 index 00000000000..99b286dd644 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BjornaNightfallAlchemist.java @@ -0,0 +1,54 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.combat.GoadTargetEffect; +import mage.abilities.keyword.FriendsForeverAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.StaticFilters; +import mage.target.common.TargetControlledPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BjornaNightfallAlchemist extends CardImpl { + + public BjornaNightfallAlchemist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{U}{R}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // {T}, Sacrifice an artifact: Lucas, the Sharpshooter deals 1 damage to target creature. Goad that creature. + Ability ability = new SimpleActivatedAbility(new DamageTargetEffect(1), new TapSourceCost()); + ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT_AN))); + ability.addEffect(new GoadTargetEffect().setText("Goad that creature")); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + + // Friends forever + this.addAbility(FriendsForeverAbility.getInstance()); + } + + private BjornaNightfallAlchemist(final BjornaNightfallAlchemist card) { + super(card); + } + + @Override + public BjornaNightfallAlchemist copy() { + return new BjornaNightfallAlchemist(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BlackWard.java b/Mage.Sets/src/mage/cards/b/BlackWard.java index 1b124f23192..73bd956aa21 100644 --- a/Mage.Sets/src/mage/cards/b/BlackWard.java +++ b/Mage.Sets/src/mage/cards/b/BlackWard.java @@ -1,36 +1,29 @@ - package mage.cards.b; -import java.util.UUID; import mage.ObjectColor; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; import mage.abilities.keyword.EnchantAbility; import mage.abilities.keyword.ProtectionAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.ColorPredicate; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LoneFox */ public final class BlackWard extends CardImpl { - private static final FilterCard filter = new FilterCard("black"); - - static { - filter.add(new ColorPredicate(ObjectColor.BLACK)); - } - public BlackWard(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}"); this.subtype.add(SubType.AURA); // Enchant creature @@ -38,12 +31,11 @@ public final class BlackWard extends CardImpl { this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.Protect)); this.addAbility(new EnchantAbility(auraTarget.getTargetName())); + // Enchanted creature has protection from black. This effect doesn't remove Black Ward. - ProtectionAbility gainedAbility = new ProtectionAbility(filter); - gainedAbility.setAuraIdNotToBeRemoved(this.getId()); - Effect effect = new GainAbilityAttachedEffect(gainedAbility, AttachmentType.AURA); - effect.setText("Enchanted creature has protection from black. This effect doesn't remove {this}."); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); + this.addAbility(new SimpleStaticAbility(new GainAbilityAttachedEffect( + ProtectionAbility.from(ObjectColor.BLACK), AttachmentType.AURA + ).setDoesntRemoveItself(true))); } private BlackWard(final BlackWard card) { diff --git a/Mage.Sets/src/mage/cards/b/BladeBlizzardKitsune.java b/Mage.Sets/src/mage/cards/b/BladeBlizzardKitsune.java new file mode 100644 index 00000000000..fedacc7a589 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BladeBlizzardKitsune.java @@ -0,0 +1,41 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.keyword.DoubleStrikeAbility; +import mage.abilities.keyword.NinjutsuAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BladeBlizzardKitsune extends CardImpl { + + public BladeBlizzardKitsune(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.subtype.add(SubType.FOX); + this.subtype.add(SubType.NINJA); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Ninjutsu {3}{W} + this.addAbility(new NinjutsuAbility("{3}{W}")); + + // Double strike + this.addAbility(DoubleStrikeAbility.getInstance()); + } + + private BladeBlizzardKitsune(final BladeBlizzardKitsune card) { + super(card); + } + + @Override + public BladeBlizzardKitsune copy() { + return new BladeBlizzardKitsune(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BladeOfTheOni.java b/Mage.Sets/src/mage/cards/b/BladeOfTheOni.java new file mode 100644 index 00000000000..ed6a5b035e9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BladeOfTheOni.java @@ -0,0 +1,114 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.keyword.MenaceAbility; +import mage.abilities.keyword.ReconfigureAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.permanent.Permanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BladeOfTheOni extends CardImpl { + + public BladeOfTheOni(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{1}{B}"); + + this.subtype.add(SubType.EQUIPMENT); + this.subtype.add(SubType.DEMON); + this.power = new MageInt(3); + this.toughness = new MageInt(1); + + // Menace + this.addAbility(new MenaceAbility(false)); + + // Equipped creature has base power and toughness 5/5, has menace, and is a black Demon in addition to its other colors and types. + this.addAbility(new SimpleStaticAbility(new BladeOfTheOniEffect())); + + // Reconfigure {2}{B}{B} + this.addAbility(new ReconfigureAbility("{2}{B}{B}")); + } + + private BladeOfTheOni(final BladeOfTheOni card) { + super(card); + } + + @Override + public BladeOfTheOni copy() { + return new BladeOfTheOni(this); + } +} + +class BladeOfTheOniEffect extends ContinuousEffectImpl { + + BladeOfTheOniEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "equipped creature has base power and toughness 5/5, " + + "has menace, and is a black Demon in addition to its other colors and types"; + } + + private BladeOfTheOniEffect(final BladeOfTheOniEffect effect) { + super(effect); + } + + @Override + public BladeOfTheOniEffect copy() { + return new BladeOfTheOniEffect(this); + } + + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game); + if (sourcePermanent == null) { + return false; + } + Permanent permanent = game.getPermanent(sourcePermanent.getAttachedTo()); + if (permanent == null) { + return false; + } + switch (layer) { + case AbilityAddingRemovingEffects_6: + permanent.addAbility(new MenaceAbility(false), source.getSourceId(), game); + return true; + case ColorChangingEffects_5: + permanent.getColor(game).setBlack(true); + return true; + case TypeChangingEffects_4: + permanent.addSubType(game, SubType.DEMON); + return true; + case PTChangingEffects_7: + if (sublayer != SubLayer.SetPT_7b) { + return false; + } + permanent.getPower().setValue(5); + permanent.getToughness().setValue(5); + return true; + } + return false; + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean hasLayer(Layer layer) { + switch (layer) { + case TypeChangingEffects_4: + case ColorChangingEffects_5: + case AbilityAddingRemovingEffects_6: + case PTChangingEffects_7: + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/b/BladeSliver.java b/Mage.Sets/src/mage/cards/b/BladeSliver.java index 7fc07a17b9b..644d8fe77e9 100644 --- a/Mage.Sets/src/mage/cards/b/BladeSliver.java +++ b/Mage.Sets/src/mage/cards/b/BladeSliver.java @@ -28,7 +28,7 @@ public final class BladeSliver extends CardImpl { // All Sliver creatures get +1/+0. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, - new BoostAllEffect(1, 0, Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS, false))); + new BoostAllEffect(1, 0, Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_ALL_SLIVERS, false))); } private BladeSliver(final BladeSliver card) { diff --git a/Mage.Sets/src/mage/cards/b/BladebackSliver.java b/Mage.Sets/src/mage/cards/b/BladebackSliver.java index de3440775f2..109101d3c45 100644 --- a/Mage.Sets/src/mage/cards/b/BladebackSliver.java +++ b/Mage.Sets/src/mage/cards/b/BladebackSliver.java @@ -39,7 +39,7 @@ public final class BladebackSliver extends CardImpl { this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( new GainAbilityControlledEffect( ability, Duration.WhileOnBattlefield, - StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS + StaticFilters.FILTER_PERMANENT_ALL_SLIVERS ), HellbentCondition.instance, "Hellbent — " + "As long as you have no cards in hand, Sliver creatures you control have " + "\"{T}: This creature deals 1 damage to target player or planeswalker.\"" diff --git a/Mage.Sets/src/mage/cards/b/BlademaneBaku.java b/Mage.Sets/src/mage/cards/b/BlademaneBaku.java index b6d296197d2..fd9b07f0a49 100644 --- a/Mage.Sets/src/mage/cards/b/BlademaneBaku.java +++ b/Mage.Sets/src/mage/cards/b/BlademaneBaku.java @@ -1,30 +1,34 @@ - - package mage.cards.b; - import java.util.UUID; +import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility; -import mage.abilities.costs.Cost; import mage.abilities.costs.common.RemoveVariableCountersSourceCost; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.MultipliedValue; +import mage.abilities.dynamicvalue.common.RemovedCountersForCostValue; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.cards.CardImpl; - import mage.cards.CardSetInfo; - import mage.constants.*; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; import mage.counters.CounterType; import mage.filter.StaticFilters; -import mage.game.Game; /** * @author LevelX2 */ public final class BlademaneBaku extends CardImpl { + private static final DynamicValue xValue = new MultipliedValue(RemovedCountersForCostValue.instance, 2); + public BlademaneBaku(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{R}"); this.subtype.add(SubType.SPIRIT); @@ -32,12 +36,14 @@ public final class BlademaneBaku extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); - // Whenever you cast a Spirit or Arcane spell, you may put a ki counter on Skullmane Baku. - this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.KI.createInstance()), StaticFilters.SPIRIT_OR_ARCANE_CARD, true)); + // Whenever you cast a Spirit or Arcane spell, you may put a ki counter on Blademane Baku. + this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.KI.createInstance()), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true)); // {1}, Remove X ki counters from Blademane Baku: For each counter removed, Blademane Baku gets +2/+0 until end of turn. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BlademaneBakuBoostEffect(), new GenericManaCost(1)); - ability.addCost(new RemoveVariableCountersSourceCost(CounterType.KI.createInstance(1))); + Effect effect = new BoostSourceEffect(xValue, StaticValue.get(0), Duration.EndOfTurn); + effect.setText("for each counter removed, {this} gets +2/+0 until end of turn"); + Ability ability = new SimpleActivatedAbility(effect, new GenericManaCost(1)); + ability.addCost(new RemoveVariableCountersSourceCost(CounterType.KI.createInstance())); this.addAbility(ability); } @@ -49,37 +55,4 @@ public final class BlademaneBaku extends CardImpl { public BlademaneBaku copy() { return new BlademaneBaku(this); } - - static class BlademaneBakuBoostEffect extends OneShotEffect { - - public BlademaneBakuBoostEffect() { - super(Outcome.UnboostCreature); - staticText = "For each counter removed, {this} gets +2/+0 until end of turn"; - } - - public BlademaneBakuBoostEffect(BlademaneBakuBoostEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - int numberToBoost = 0; - for (Cost cost : source.getCosts()) { - if (cost instanceof RemoveVariableCountersSourceCost) { - numberToBoost = ((RemoveVariableCountersSourceCost)cost).getAmount() * 2; - } - } - if (numberToBoost >= 0) { - game.addEffect(new BoostSourceEffect(numberToBoost, 0, Duration.EndOfTurn), source); - return true; - } - return false; - } - - @Override - public BlademaneBakuBoostEffect copy() { - return new BlademaneBakuBoostEffect(this); - } - - } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/b/BlankaFerociousFriend.java b/Mage.Sets/src/mage/cards/b/BlankaFerociousFriend.java new file mode 100644 index 00000000000..090f31fe734 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BlankaFerociousFriend.java @@ -0,0 +1,76 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BecomesTargetTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.DamagePlayersEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.watchers.common.CastSpellLastTurnWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BlankaFerociousFriend extends CardImpl { + + public BlankaFerociousFriend(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.BEAST); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // Rolling Attack—Blanka, Ferocious Friend has trample as long as you've cast three or more spells this turn. + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new GainAbilitySourceEffect(TrampleAbility.getInstance()), BlankaFerociousFriendCondition.instance, + "{this} has trample as long as you've cast three or more spells this turn" + )).withFlavorWord("Rolling Attack")); + + // Electric Thunder—Whenever Blanka becomes the target of a spell, he gets +2/+2 until end of turn and deals 2 damage to each opponent. + Ability ability = new BecomesTargetTriggeredAbility(new BoostSourceEffect( + 2, 2, Duration.EndOfTurn + ).setText("he gets +2/+2 until end of turn"), StaticFilters.FILTER_SPELL_A).setTriggerPhrase("Whenever {this} becomes the target of a spell, "); + ability.addEffect(new DamagePlayersEffect(2, TargetController.OPPONENT) + .setText("and deals 2 damage to each opponent")); + this.addAbility(ability.withFlavorWord("Electric Thunder")); + } + + private BlankaFerociousFriend(final BlankaFerociousFriend card) { + super(card); + } + + @Override + public BlankaFerociousFriend copy() { + return new BlankaFerociousFriend(this); + } +} + +enum BlankaFerociousFriendCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + return game + .getState() + .getWatcher(CastSpellLastTurnWatcher.class) + .getAmountOfSpellsPlayerCastOnCurrentTurn(source.getControllerId()) >= 3; + } +} diff --git a/Mage.Sets/src/mage/cards/b/BlastingStation.java b/Mage.Sets/src/mage/cards/b/BlastingStation.java index 93e4367ec03..95bc9414b18 100644 --- a/Mage.Sets/src/mage/cards/b/BlastingStation.java +++ b/Mage.Sets/src/mage/cards/b/BlastingStation.java @@ -14,7 +14,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Zone; import static mage.filter.StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetAnyTarget; @@ -32,8 +32,9 @@ public final class BlastingStation extends CardImpl { ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(FILTER_CONTROLLED_CREATURE_SHORT_TEXT))); ability.addTarget(new TargetAnyTarget()); this.addAbility(ability); + // Whenever a creature enters the battlefield, you may untap Blasting Station. - this.addAbility(new EntersBattlefieldAllTriggeredAbility(Zone.BATTLEFIELD, new UntapSourceEffect(), new FilterCreaturePermanent("a creature"), true)); + this.addAbility(new EntersBattlefieldAllTriggeredAbility(Zone.BATTLEFIELD, new UntapSourceEffect(), StaticFilters.FILTER_PERMANENT_A_CREATURE, true)); } diff --git a/Mage.Sets/src/mage/cards/b/BlazingShoal.java b/Mage.Sets/src/mage/cards/b/BlazingShoal.java index c460a3d7228..ad753cc0da2 100644 --- a/Mage.Sets/src/mage/cards/b/BlazingShoal.java +++ b/Mage.Sets/src/mage/cards/b/BlazingShoal.java @@ -12,8 +12,6 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; import mage.filter.common.FilterOwnedCard; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardIdPredicate; import mage.filter.predicate.mageobject.ColorPredicate; import mage.target.common.TargetCardInHand; import mage.target.common.TargetCreaturePermanent; @@ -25,19 +23,27 @@ import java.util.UUID; */ public final class BlazingShoal extends CardImpl { + private static final FilterOwnedCard filter + = new FilterOwnedCard("a red card with mana value X from your hand"); + + static { + filter.add(new ColorPredicate(ObjectColor.RED)); + } + public BlazingShoal(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{X}{R}{R}"); this.subtype.add(SubType.ARCANE); - // You may exile a red card with converted mana cost X from your hand rather than pay Blazing Shoal's mana cost. - FilterOwnedCard filter = new FilterOwnedCard("a red card with mana value X from your hand"); - filter.add(new ColorPredicate(ObjectColor.RED)); - filter.add(Predicates.not(new CardIdPredicate(this.getId()))); // the exile cost can never be paid with the card itself - this.addAbility(new AlternativeCostSourceAbility(new ExileFromHandCost(new TargetCardInHand(filter), true))); + this.addAbility(new AlternativeCostSourceAbility(new ExileFromHandCost( + new TargetCardInHand(filter), true + ))); // Target creature gets +X/+0 until end of turn. - this.getSpellAbility().addEffect(new BoostTargetEffect(ExileFromHandCostCardConvertedMana.instance, StaticValue.get(0), Duration.EndOfTurn)); + this.getSpellAbility().addEffect(new BoostTargetEffect( + ExileFromHandCostCardConvertedMana.instance, + StaticValue.get(0), Duration.EndOfTurn + )); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } diff --git a/Mage.Sets/src/mage/cards/b/BleedDry.java b/Mage.Sets/src/mage/cards/b/BleedDry.java new file mode 100644 index 00000000000..74e601d910c --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BleedDry.java @@ -0,0 +1,36 @@ +package mage.cards.b; + +import java.util.UUID; + +import mage.abilities.effects.common.ExileTargetIfDiesEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author weirddan455 + */ +public final class BleedDry extends CardImpl { + + public BleedDry(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{B}{B}"); + + // Target creature gets -13/-13 until end of turn. If that creature would die this turn, exile it instead. + this.getSpellAbility().addEffect(new BoostTargetEffect(-13, -13, Duration.EndOfTurn)); + this.getSpellAbility().addEffect(new ExileTargetIfDiesEffect()); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + private BleedDry(final BleedDry card) { + super(card); + } + + @Override + public BleedDry copy() { + return new BleedDry(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BlessedReincarnation.java b/Mage.Sets/src/mage/cards/b/BlessedReincarnation.java index e1df1ecc56e..9e10070e71d 100644 --- a/Mage.Sets/src/mage/cards/b/BlessedReincarnation.java +++ b/Mage.Sets/src/mage/cards/b/BlessedReincarnation.java @@ -8,9 +8,8 @@ import mage.abilities.keyword.ReboundAbility; import mage.cards.*; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.TargetController; import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Library; @@ -23,12 +22,6 @@ import mage.target.common.TargetCreaturePermanent; */ public final class BlessedReincarnation extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public BlessedReincarnation(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{U}"); @@ -36,7 +29,7 @@ public final class BlessedReincarnation extends CardImpl { // That player reveals cards from the top of their library until a creature card is revealed. // The player puts that card onto the battlefield, then shuffles the rest into their library. this.getSpellAbility().addEffect(new BlessedReincarnationEffect()); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); // Rebound this.addAbility(new ReboundAbility()); diff --git a/Mage.Sets/src/mage/cards/b/BlessedSanctuary.java b/Mage.Sets/src/mage/cards/b/BlessedSanctuary.java index d58819c8c11..439e0b15220 100644 --- a/Mage.Sets/src/mage/cards/b/BlessedSanctuary.java +++ b/Mage.Sets/src/mage/cards/b/BlessedSanctuary.java @@ -9,7 +9,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Zone; -import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.permanent.token.UnicornToken; @@ -18,7 +18,6 @@ import java.util.UUID; public class BlessedSanctuary extends CardImpl { - private static final FilterPermanent filterYourCreatures = new FilterControlledCreaturePermanent("creatures you control"); private static final FilterControlledCreaturePermanent filterNontoken = new FilterControlledCreaturePermanent("a nontoken creature"); static { @@ -30,7 +29,7 @@ public class BlessedSanctuary extends CardImpl { //Prevent all noncombat damage that would be dealt to you and creatures you control. this.addAbility(new SimpleStaticAbility(new PreventAllNonCombatDamageToAllEffect( - Duration.WhileOnBattlefield, filterYourCreatures, true))); + Duration.WhileOnBattlefield, StaticFilters.FILTER_CONTROLLED_CREATURES, true))); //Whenever a nontoken creature enters the battlefield under your control, create a 2/2 white Unicorn creature token. this.addAbility(new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD, diff --git a/Mage.Sets/src/mage/cards/b/BlindObedience.java b/Mage.Sets/src/mage/cards/b/BlindObedience.java index bdc871b1930..5f0a7323da0 100644 --- a/Mage.Sets/src/mage/cards/b/BlindObedience.java +++ b/Mage.Sets/src/mage/cards/b/BlindObedience.java @@ -1,36 +1,30 @@ - package mage.cards.b; -import java.util.UUID; -import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.PermanentsEnterBattlefieldTappedEffect; import mage.abilities.keyword.ExtortAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.EntersTheBattlefieldEvent; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class BlindObedience extends CardImpl { public BlindObedience(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); // Extort (Whenever you cast a spell, you may pay {WB}. If you do, each opponent loses 1 life and you gain that much life.) this.addAbility(new ExtortAbility()); // Artifacts and creatures your opponents control enter the battlefield tapped. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BlindObedienceTapEffect())); + this.addAbility(new SimpleStaticAbility(new PermanentsEnterBattlefieldTappedEffect( + StaticFilters.FILTER_OPPONENTS_PERMANENT_ARTIFACT_OR_CREATURE + ).setText("artifacts and creatures your opponents control enter the battlefield tapped"))); } @@ -43,45 +37,3 @@ public final class BlindObedience extends CardImpl { return new BlindObedience(this); } } - -class BlindObedienceTapEffect extends ReplacementEffectImpl { - - BlindObedienceTapEffect() { - super(Duration.WhileOnBattlefield, Outcome.Tap); - staticText = "Artifacts and creatures your opponents control enter the battlefield tapped"; - } - - BlindObedienceTapEffect(final BlindObedienceTapEffect effect) { - super(effect); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Permanent target = ((EntersTheBattlefieldEvent) event).getTarget(); - if (target != null) { - target.setTapped(true); - } - return false; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (game.getOpponents(source.getControllerId()).contains(event.getPlayerId())) { - Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget(); - if (permanent != null && (permanent.isCreature(game) || permanent.isArtifact(game))) { - return true; - } - } - return false; - } - - @Override - public BlindObedienceTapEffect copy() { - return new BlindObedienceTapEffect(this); - } -} diff --git a/Mage.Sets/src/mage/cards/b/BlindingSouleater.java b/Mage.Sets/src/mage/cards/b/BlindingSouleater.java index b2e51005868..e9fd4a7a2e1 100644 --- a/Mage.Sets/src/mage/cards/b/BlindingSouleater.java +++ b/Mage.Sets/src/mage/cards/b/BlindingSouleater.java @@ -1,28 +1,25 @@ - package mage.cards.b; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.costs.mana.PhyrexianManaCost; +import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.TapTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.ColoredManaSymbol; import mage.constants.SubType; -import mage.constants.Zone; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author North */ public final class BlindingSouleater extends CardImpl { public BlindingSouleater(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{3}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}"); this.subtype.add(SubType.PHYREXIAN); this.subtype.add(SubType.CLERIC); @@ -30,9 +27,7 @@ public final class BlindingSouleater extends CardImpl { this.toughness = new MageInt(3); // {W/P},{T}: Tap target creature. ( can be paid with either or 2 life.) - SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, - new TapTargetEffect(), - new PhyrexianManaCost(ColoredManaSymbol.W)); + SimpleActivatedAbility ability = new SimpleActivatedAbility(new TapTargetEffect(), new ManaCostsImpl<>("{W/P}")); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/b/BlizzardBrawl.java b/Mage.Sets/src/mage/cards/b/BlizzardBrawl.java index 91b2bdd1e6a..50eacff9f7c 100644 --- a/Mage.Sets/src/mage/cards/b/BlizzardBrawl.java +++ b/Mage.Sets/src/mage/cards/b/BlizzardBrawl.java @@ -60,7 +60,9 @@ class BlizzardBrawlEffect extends OneShotEffect { super(Outcome.Benefit); staticText = "Choose target creature you control and target creature you don't control. " + "If you control three or more snow permanents, the creature you control gets +1/+0 " + - "and gains indestructible until end of turn. Then those creatures fight each other."; + "and gains indestructible until end of turn. " + + "Then those creatures fight each other. " + + "(Each deals damage equal to its power to the other.)"; } private BlizzardBrawlEffect(final BlizzardBrawlEffect effect) { diff --git a/Mage.Sets/src/mage/cards/b/BloodCurdle.java b/Mage.Sets/src/mage/cards/b/BloodCurdle.java index 6256c807ce0..9a75ba77a1e 100644 --- a/Mage.Sets/src/mage/cards/b/BloodCurdle.java +++ b/Mage.Sets/src/mage/cards/b/BloodCurdle.java @@ -45,7 +45,8 @@ class BloodCurdleEffect extends OneShotEffect { BloodCurdleEffect() { super(Outcome.Benefit); - staticText = "Put a menace counter on a creature you control"; + staticText = "Put a menace counter on a creature you control. " + + "(It can't be blocked except by two or more creatures.)"; } private BloodCurdleEffect(final BloodCurdleEffect effect) { diff --git a/Mage.Sets/src/mage/cards/b/BloodFeud.java b/Mage.Sets/src/mage/cards/b/BloodFeud.java index c2a9319a4af..d111a6aca6e 100644 --- a/Mage.Sets/src/mage/cards/b/BloodFeud.java +++ b/Mage.Sets/src/mage/cards/b/BloodFeud.java @@ -1,23 +1,21 @@ - package mage.cards.b; -import java.util.UUID; import mage.abilities.effects.common.FightTargetsEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.other.AnotherTargetPredicate; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author intimidatingant */ public final class BloodFeud extends CardImpl { public BloodFeud(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{4}{R}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{R}{R}"); // Target creature fights another target creature. this.getSpellAbility().addEffect(new FightTargetsEffect()); @@ -25,9 +23,7 @@ public final class BloodFeud extends CardImpl { target.setTargetTag(1); this.getSpellAbility().addTarget(target); - FilterCreaturePermanent filter = new FilterCreaturePermanent(); - filter.add(new AnotherTargetPredicate(2)); - TargetCreaturePermanent target2 = new TargetCreaturePermanent(filter); + TargetCreaturePermanent target2 = new TargetCreaturePermanent(StaticFilters.FILTER_ANOTHER_CREATURE_TARGET_2); target2.setTargetTag(2); this.getSpellAbility().addTarget(target2); } diff --git a/Mage.Sets/src/mage/cards/b/BloodFountain.java b/Mage.Sets/src/mage/cards/b/BloodFountain.java new file mode 100644 index 00000000000..d80ab7ab789 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BloodFountain.java @@ -0,0 +1,51 @@ +package mage.cards.b; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.game.permanent.token.BloodToken; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BloodFountain extends CardImpl { + + public BloodFountain(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{B}"); + + // When Blood Fountain enters the battlefield, create a Blood token. + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new BloodToken()))); + + // {3}{B}, {T}, Sacrifice Blood Fountain: Return up to two target creature cards from your graveyard to your hand. + Ability ability = new SimpleActivatedAbility( + new ReturnFromGraveyardToHandTargetEffect(), new ManaCostsImpl<>("{3}{B}") + ); + ability.addCost(new TapSourceCost()); + ability.addCost(new SacrificeSourceCost()); + ability.addTarget(new TargetCardInYourGraveyard( + 0, 2, StaticFilters.FILTER_CARD_CREATURES_YOUR_GRAVEYARD + )); + this.addAbility(ability); + } + + private BloodFountain(final BloodFountain card) { + super(card); + } + + @Override + public BloodFountain copy() { + return new BloodFountain(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BloodHypnotist.java b/Mage.Sets/src/mage/cards/b/BloodHypnotist.java new file mode 100644 index 00000000000..b8221365335 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BloodHypnotist.java @@ -0,0 +1,56 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.CantBlockAbility; +import mage.abilities.common.SacrificePermanentTriggeredAbility; +import mage.abilities.effects.common.combat.CantBlockTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BloodHypnotist extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent(SubType.BLOOD, "one or more Blood tokens"); + + static { + filter.add(TokenPredicate.TRUE); + } + + public BloodHypnotist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); + + this.subtype.add(SubType.VAMPIRE); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Blood Hypnotist can't block. + this.addAbility(new CantBlockAbility()); + + // Whenever you sacrifice one or more Blood tokens, target creature can't block this turn. This ability triggers only once each turn. + Ability ability = new SacrificePermanentTriggeredAbility( + new CantBlockTargetEffect(Duration.EndOfTurn), filter + ).setTriggersOnce(true); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + private BloodHypnotist(final BloodHypnotist card) { + super(card); + } + + @Override + public BloodHypnotist copy() { + return new BloodHypnotist(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BloodPetalCelebrant.java b/Mage.Sets/src/mage/cards/b/BloodPetalCelebrant.java new file mode 100644 index 00000000000..5126321a580 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BloodPetalCelebrant.java @@ -0,0 +1,50 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.SourceAttackingCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.game.permanent.token.BloodToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BloodPetalCelebrant extends CardImpl { + + public BloodPetalCelebrant(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); + + this.subtype.add(SubType.VAMPIRE); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Blood Petal Celebrant has first strike as long as it's attacking. + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new GainAbilitySourceEffect(FirstStrikeAbility.getInstance(), Duration.WhileOnBattlefield), + SourceAttackingCondition.instance, "{this} has first strike as long as it's attacking" + ))); + + // When Blood Petal Celebrant dies, create a Blood token. + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new BloodToken()))); + } + + private BloodPetalCelebrant(final BloodPetalCelebrant card) { + super(card); + } + + @Override + public BloodPetalCelebrant copy() { + return new BloodPetalCelebrant(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BloodPrice.java b/Mage.Sets/src/mage/cards/b/BloodPrice.java index e531bc1c500..2d203102c46 100644 --- a/Mage.Sets/src/mage/cards/b/BloodPrice.java +++ b/Mage.Sets/src/mage/cards/b/BloodPrice.java @@ -25,7 +25,8 @@ public final class BloodPrice extends CardImpl { StaticFilters.FILTER_CARD, Zone.LIBRARY, false, false, false, Zone.HAND, false, false, true - )); + ).setText("Look at the top four cards of your library. " + + "Put two of them into your hand and the rest on the bottom of your library in any order")); this.getSpellAbility().addEffect(new LoseLifeSourceControllerEffect(2)); } diff --git a/Mage.Sets/src/mage/cards/b/BloodServitor.java b/Mage.Sets/src/mage/cards/b/BloodServitor.java new file mode 100644 index 00000000000..df700d6c878 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BloodServitor.java @@ -0,0 +1,38 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.BloodToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BloodServitor extends CardImpl { + + public BloodServitor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}"); + + this.subtype.add(SubType.CONSTRUCT); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // When Blood Servitor enters the battlefield, create a Blood token. + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new BloodToken()))); + } + + private BloodServitor(final BloodServitor card) { + super(card); + } + + @Override + public BloodServitor copy() { + return new BloodServitor(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BloodTribute.java b/Mage.Sets/src/mage/cards/b/BloodTribute.java index d0dee861e18..2559e50297a 100644 --- a/Mage.Sets/src/mage/cards/b/BloodTribute.java +++ b/Mage.Sets/src/mage/cards/b/BloodTribute.java @@ -7,18 +7,16 @@ import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.KickerAbility; -import mage.abilities.text.TextPartSubType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; -import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.mageobject.TextPartSubtypePredicate; +import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.permanent.TappedPredicate; import mage.game.Game; import mage.players.Player; -import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetControlledPermanent; import mage.target.common.TargetOpponent; import java.util.UUID; @@ -28,15 +26,18 @@ import java.util.UUID; */ public final class BloodTribute extends CardImpl { + private static final FilterControlledPermanent filter + = new FilterControlledPermanent(SubType.VAMPIRE, "an untapped Vampire you control"); + + static { + filter.add(TappedPredicate.UNTAPPED); + } + public BloodTribute(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{B}{B}"); // Kicker - Tap an untapped Vampire you control. - TextPartSubType textPartVampire = (TextPartSubType) addTextPart(new TextPartSubType(SubType.VAMPIRE)); - FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("an untapped Vampire you control"); - filter.add(new TextPartSubtypePredicate(textPartVampire)); - filter.add(TappedPredicate.UNTAPPED); - this.addAbility(new KickerAbility(new TapTargetCost(new TargetControlledCreaturePermanent(1, 1, filter, true)))); + this.addAbility(new KickerAbility(new TapTargetCost(new TargetControlledPermanent(filter)))); // Target opponent loses half their life, rounded up. this.getSpellAbility().addEffect(new BloodTributeLoseLifeEffect()); diff --git a/Mage.Sets/src/mage/cards/b/BloodbatSummoner.java b/Mage.Sets/src/mage/cards/b/BloodbatSummoner.java new file mode 100644 index 00000000000..10e18bf835f --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BloodbatSummoner.java @@ -0,0 +1,69 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfCombatTriggeredAbility; +import mage.abilities.effects.common.continuous.BecomesCreatureTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.permanent.token.custom.CreatureToken; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BloodbatSummoner extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledPermanent(SubType.BLOOD, "Blood token you control"); + + static { + filter.add(TokenPredicate.TRUE); + } + + public BloodbatSummoner(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); + + this.subtype.add(SubType.VAMPIRE); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + this.color.setBlack(true); + this.nightCard = true; + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // At the beginning of combat on your turn, up to one target Blood token you control becomes a 2/2 black Bat creature with flying and haste in addition to its other types. + Ability ability = new BeginningOfCombatTriggeredAbility(new BecomesCreatureTargetEffect( + new CreatureToken(2, 2, "", SubType.BAT) + .withAbility(FlyingAbility.getInstance()) + .withAbility(HasteAbility.getInstance()) + .withColor("B"), + false, false, Duration.Custom + ).setText("up to one target Blood token you control becomes a " + + "2/2 black Bat creature with flying and haste in addition to its other types"), TargetController.YOU, false); + ability.addTarget(new TargetPermanent(0, 1, filter)); + this.addAbility(ability); + } + + private BloodbatSummoner(final BloodbatSummoner card) { + super(card); + } + + @Override + public BloodbatSummoner copy() { + return new BloodbatSummoner(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BloodcrazedHoplite.java b/Mage.Sets/src/mage/cards/b/BloodcrazedHoplite.java index a612d3b2f91..2eb3ca6f5b5 100644 --- a/Mage.Sets/src/mage/cards/b/BloodcrazedHoplite.java +++ b/Mage.Sets/src/mage/cards/b/BloodcrazedHoplite.java @@ -12,13 +12,11 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; import mage.constants.Zone; import mage.counters.CounterType; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.target.common.TargetCreaturePermanent; /** @@ -27,12 +25,6 @@ import mage.target.common.TargetCreaturePermanent; */ public final class BloodcrazedHoplite extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public BloodcrazedHoplite(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}"); this.subtype.add(SubType.HUMAN, SubType.SOLDIER); @@ -44,7 +36,7 @@ public final class BloodcrazedHoplite extends CardImpl { this.addAbility(new HeroicAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance(), false))); // Whenever a +1/+1 counter is put on Bloodcrazed Hoplite, remove a +1/+1 counter from target creature an opponent controls. Ability ability = new BloodcrazedHopliteTriggeredAbility(); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/b/BloodcrazedSocialite.java b/Mage.Sets/src/mage/cards/b/BloodcrazedSocialite.java new file mode 100644 index 00000000000..fbb4fb5c23c --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BloodcrazedSocialite.java @@ -0,0 +1,62 @@ +package mage.cards.b; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.permanent.token.BloodToken; + +/** + * + * @author weirddan455 + */ +public final class BloodcrazedSocialite extends CardImpl { + + private static final FilterControlledPermanent filter = new FilterControlledPermanent("Blood token"); + + static { + filter.add(SubType.BLOOD.getPredicate()); + filter.add(TokenPredicate.TRUE); + } + + public BloodcrazedSocialite(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); + + this.subtype.add(SubType.VAMPIRE); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Menace + this.addAbility(new MenaceAbility(false)); + + // When Bloodcrazed Socialite enters the battlefield, create a Blood token. + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new BloodToken()))); + + // Whenever Bloodcrazed Socialite attacks, you may sacrifice a Blood token. If you do, it gets +2/+2 until end of turn. + this.addAbility(new AttacksTriggeredAbility(new DoIfCostPaid( + new BoostSourceEffect(2, 2, Duration.EndOfTurn).setText("it gets +2/+2 until end of turn"), + new SacrificeTargetCost(filter) + ))); + } + + private BloodcrazedSocialite(final BloodcrazedSocialite card) { + super(card); + } + + @Override + public BloodcrazedSocialite copy() { + return new BloodcrazedSocialite(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/Bloodghast.java b/Mage.Sets/src/mage/cards/b/Bloodghast.java index 41683fe1a11..d4105255373 100644 --- a/Mage.Sets/src/mage/cards/b/Bloodghast.java +++ b/Mage.Sets/src/mage/cards/b/Bloodghast.java @@ -1,7 +1,6 @@ package mage.cards.b; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.CantBlockAbility; import mage.abilities.common.LandfallAbility; @@ -19,14 +18,15 @@ import mage.constants.Duration; import mage.constants.SubType; import mage.constants.Zone; +import java.util.UUID; + /** - * * @author maurer.it_at_gmail.com */ public final class Bloodghast extends CardImpl { public Bloodghast(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}{B}"); this.subtype.add(SubType.VAMPIRE, SubType.SPIRIT); this.power = new MageInt(2); @@ -38,9 +38,9 @@ public final class Bloodghast extends CardImpl { ContinuousEffect effect = new GainAbilitySourceEffect(HasteAbility.getInstance(), Duration.WhileOnBattlefield); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(effect, new XorLessLifeCondition(XorLessLifeCondition.CheckType.AN_OPPONENT, 10), - "Bloodghast has haste as long as an opponent has 10 or less life"))); + "{this} has haste as long as an opponent has 10 or less life"))); // Landfall — Whenever a land enters the battlefield under your control, you may return Bloodghast from your graveyard to the battlefield. - this.addAbility(new LandfallAbility(Zone.GRAVEYARD, new ReturnSourceFromGraveyardToBattlefieldEffect(), true)); + this.addAbility(new LandfallAbility(Zone.GRAVEYARD, new ReturnSourceFromGraveyardToBattlefieldEffect(false, false), true)); } private Bloodghast(final Bloodghast card) { diff --git a/Mage.Sets/src/mage/cards/b/BloodlineKeeper.java b/Mage.Sets/src/mage/cards/b/BloodlineKeeper.java index 90918844994..f651d1a1ec9 100644 --- a/Mage.Sets/src/mage/cards/b/BloodlineKeeper.java +++ b/Mage.Sets/src/mage/cards/b/BloodlineKeeper.java @@ -41,7 +41,6 @@ public final class BloodlineKeeper extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(3); - this.transformable = true; this.secondSideCardClazz = mage.cards.l.LordOfLineage.class; this.addAbility(FlyingAbility.getInstance()); @@ -50,7 +49,7 @@ public final class BloodlineKeeper extends CardImpl { // {B}: Transform Bloodline Keeper. Activate this ability only if you control five or more Vampires. this.addAbility(new TransformAbility()); Ability ability = new ActivateIfConditionActivatedAbility(Zone.BATTLEFIELD, - new TransformSourceEffect(true), + new TransformSourceEffect(), new ManaCostsImpl("{B}"), new PermanentsOnTheBattlefieldCondition(filter, ComparisonType.MORE_THAN, 4)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/b/BloodskyBerserker.java b/Mage.Sets/src/mage/cards/b/BloodskyBerserker.java index 0d3fe7916d1..093e5c4d949 100644 --- a/Mage.Sets/src/mage/cards/b/BloodskyBerserker.java +++ b/Mage.Sets/src/mage/cards/b/BloodskyBerserker.java @@ -28,13 +28,15 @@ public final class BloodskyBerserker extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); - // Whenever you cast your second spell each turn, put two +1/+1 counters on Bloodsky Berserker. It gains menace until end of turn. + // Whenever you cast your second spell each turn, put two +1/+1 counters on Bloodsky Berserker. Ability ability = new CastSecondSpellTriggeredAbility( new AddCountersSourceEffect(CounterType.P1P1.createInstance(2)) ); + // It gains menace until end of turn. ability.addEffect(new GainAbilitySourceEffect( new MenaceAbility(), Duration.EndOfTurn - ).setText("It gains menace until end of turn")); + ).setText("It gains menace until end of turn. " + + "(It can't be blocked except by two or more creatures.)")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/b/BloodsoakedReveler.java b/Mage.Sets/src/mage/cards/b/BloodsoakedReveler.java new file mode 100644 index 00000000000..881611ab1a2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BloodsoakedReveler.java @@ -0,0 +1,61 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.YouGainedLifeCondition; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.LoseLifeOpponentsEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.permanent.token.BloodToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BloodsoakedReveler extends CardImpl { + + private static final Condition condition = new YouGainedLifeCondition(ComparisonType.MORE_THAN, 0); + private static final Hint hint = new ConditionHint(condition, "You gained life this turn"); + + public BloodsoakedReveler(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); + + this.subtype.add(SubType.VAMPIRE); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + this.color.setBlack(true); + this.nightCard = true; + + // At the beginning of your end step, if you gained life this turn, create a Blood token. + this.addAbility(new BeginningOfEndStepTriggeredAbility( + Zone.BATTLEFIELD, new CreateTokenEffect(new BloodToken()), + TargetController.YOU, condition, false + ).addHint(hint)); + + // {4}{B}: Each opponent loses 2 life and you gain 2 life. + Ability ability = new SimpleActivatedAbility( + new LoseLifeOpponentsEffect(2), new ManaCostsImpl<>("{4}{B}") + ); + ability.addEffect(new GainLifeEffect(2).concatBy("and")); + this.addAbility(ability); + } + + private BloodsoakedReveler(final BloodsoakedReveler card) { + super(card); + } + + @Override + public BloodsoakedReveler copy() { + return new BloodsoakedReveler(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BloodstoneGoblin.java b/Mage.Sets/src/mage/cards/b/BloodstoneGoblin.java index fca05c17550..df74047ebe9 100644 --- a/Mage.Sets/src/mage/cards/b/BloodstoneGoblin.java +++ b/Mage.Sets/src/mage/cards/b/BloodstoneGoblin.java @@ -30,7 +30,8 @@ public final class BloodstoneGoblin extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); - // Whenever you cast a spell, if that spell was kicked, Bloodstone Goblin gets +1/+1 and gains menace until end of turn. + // Whenever you cast a spell, if that spell was kicked, + // Bloodstone Goblin gets +1/+1 and gains menace until end of turn. this.addAbility(new BloodstoneGoblinTriggeredAbility()); } @@ -47,8 +48,16 @@ public final class BloodstoneGoblin extends CardImpl { class BloodstoneGoblinTriggeredAbility extends TriggeredAbilityImpl { BloodstoneGoblinTriggeredAbility() { - super(Zone.BATTLEFIELD, new BoostSourceEffect(1, 1, Duration.EndOfTurn).setText("{this} gets +1/+1"), false); - this.addEffect(new GainAbilitySourceEffect(new MenaceAbility(), Duration.EndOfTurn).setText("and gains menace until end of turn")); + super( + Zone.BATTLEFIELD, + new BoostSourceEffect(1, 1, Duration.EndOfTurn).setText("{this} gets +1/+1"), + false); + this.addEffect( + new GainAbilitySourceEffect( + new MenaceAbility(false), + Duration.EndOfTurn + ).setText("and gains menace until end of turn. " + + "(It can't be blocked except by two or more creatures.)")); } BloodstoneGoblinTriggeredAbility(final BloodstoneGoblinTriggeredAbility ability) { diff --git a/Mage.Sets/src/mage/cards/b/BloodswornKnight.java b/Mage.Sets/src/mage/cards/b/BloodswornKnight.java new file mode 100644 index 00000000000..941c1fce0f8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BloodswornKnight.java @@ -0,0 +1,64 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; +import mage.abilities.effects.common.TapSourceEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.effects.common.continuous.SetPowerToughnessSourceEffect; +import mage.abilities.keyword.IndestructibleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author weirddan455 + */ +public final class BloodswornKnight extends CardImpl { + + private static final DynamicValue xValue = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_CREATURES); + + public BloodswornKnight(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); + + this.subtype.add(SubType.VAMPIRE); + this.subtype.add(SubType.KNIGHT); + this.power = new MageInt(0); + this.toughness = new MageInt(0); + this.color.setBlack(true); + + // Back half of Bloodsworn Squire + this.nightCard = true; + + // Bloodsworn Knight's power and toughness are each equal to the number of creature cards in your graveyard. + this.addAbility(new SimpleStaticAbility(new SetPowerToughnessSourceEffect(xValue, Duration.EndOfGame))); + + // {1}{B}, Discard a card: Bloodsworn Knight gains indestructible until end of turn. Tap it. + Ability ability = new SimpleActivatedAbility( + new GainAbilitySourceEffect(IndestructibleAbility.getInstance(), Duration.EndOfTurn), + new ManaCostsImpl<>("{1}{B}") + ); + ability.addCost(new DiscardCardCost()); + ability.addEffect(new TapSourceEffect().setText("tap it")); + this.addAbility(ability); + } + + private BloodswornKnight(final BloodswornKnight card) { + super(card); + } + + @Override + public BloodswornKnight copy() { + return new BloodswornKnight(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BloodswornSquire.java b/Mage.Sets/src/mage/cards/b/BloodswornSquire.java new file mode 100644 index 00000000000..a13ac16a0a7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BloodswornSquire.java @@ -0,0 +1,63 @@ +package mage.cards.b; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.condition.common.CardsInControllerGraveyardCondition; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.TapSourceEffect; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.keyword.IndestructibleAbility; +import mage.abilities.keyword.TransformAbility; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; + +/** + * + * @author weirddan455 + */ +public final class BloodswornSquire extends CardImpl { + + public BloodswornSquire(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); + + this.subtype.add(SubType.VAMPIRE); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + this.secondSideCardClazz = mage.cards.b.BloodswornKnight.class; + + this.addAbility(new TransformAbility()); + + // {1}{B}, Discard a card: Bloodsworn Squire gains indestructible until end of turn. Tap it. Then if there are four or more creature cards in your graveyard, transform Bloodsworn Squire. + Ability ability = new SimpleActivatedAbility( + new GainAbilitySourceEffect(IndestructibleAbility.getInstance(), Duration.EndOfTurn), + new ManaCostsImpl<>("{1}{B}") + ); + ability.addCost(new DiscardCardCost()); + ability.addEffect(new TapSourceEffect().setText("tap it")); + ability.addEffect(new ConditionalOneShotEffect( + new TransformSourceEffect(), + new CardsInControllerGraveyardCondition(4, StaticFilters.FILTER_CARD_CREATURES), + "Then if there are four or more creature cards in your graveyard, transform {this}" + )); + this.addAbility(ability); + } + + private BloodswornSquire(final BloodswornSquire card) { + super(card); + } + + @Override + public BloodswornSquire copy() { + return new BloodswornSquire(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BloodthirstyBlade.java b/Mage.Sets/src/mage/cards/b/BloodthirstyBlade.java index 164776fc09f..98961f8e187 100644 --- a/Mage.Sets/src/mage/cards/b/BloodthirstyBlade.java +++ b/Mage.Sets/src/mage/cards/b/BloodthirstyBlade.java @@ -2,9 +2,10 @@ package mage.cards.b; import mage.abilities.Ability; import mage.abilities.common.ActivateAsSorceryActivatedAbility; -import mage.abilities.common.GoadAttachedAbility; +import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.combat.GoadAttachedEffect; import mage.abilities.effects.common.continuous.BoostEquippedEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -27,10 +28,12 @@ public final class BloodthirstyBlade extends CardImpl { this.subtype.add(SubType.EQUIPMENT); // Equipped creature gets +2/+0 and is goaded. - this.addAbility(new GoadAttachedAbility(new BoostEquippedEffect(2, 0))); + Ability ability = new SimpleStaticAbility(new BoostEquippedEffect(2, 0)); + ability.addEffect(new GoadAttachedEffect()); + this.addAbility(ability); // {1}: Attach Bloodthirsty Blade to target creature an opponent controls. Active this ability only any time you could cast a sorcery. - Ability ability = new ActivateAsSorceryActivatedAbility( + ability = new ActivateAsSorceryActivatedAbility( Zone.BATTLEFIELD, new AttachEffect( Outcome.Detriment, "Attach {this} to target creature an opponent controls" diff --git a/Mage.Sets/src/mage/cards/b/BloodtitheHarvester.java b/Mage.Sets/src/mage/cards/b/BloodtitheHarvester.java new file mode 100644 index 00000000000..4bd171cdbc1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BloodtitheHarvester.java @@ -0,0 +1,71 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.permanent.token.BloodToken; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BloodtitheHarvester extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.BLOOD); + + static { + filter.add(TokenPredicate.TRUE); + } + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter, -2); + private static final Hint hint = new ValueHint( + "Blood tokens you control", new PermanentsOnBattlefieldCount(filter) + ); + + public BloodtitheHarvester(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}{R}"); + + this.subtype.add(SubType.VAMPIRE); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // When Bloodtithe Harvester enters the battlefield, create a Blood token. + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new BloodToken()))); + + // {T}, Sacrifice Bloodtithe Harvester: Target creature gets -X/-X until end of turn, where X is twice the number of Blood tokens you control. Activate only as a sorcery. + Ability ability = new ActivateAsSorceryActivatedAbility(new BoostTargetEffect( + xValue, xValue, Duration.EndOfTurn + ).setText("target creature gets -X/-X until end of turn, where X is twice the number of Blood tokens you control"), new TapSourceCost()); + ability.addCost(new SacrificeSourceCost()); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability.addHint(hint)); + } + + private BloodtitheHarvester(final BloodtitheHarvester card) { + super(card); + } + + @Override + public BloodtitheHarvester copy() { + return new BloodtitheHarvester(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BloodvialPurveyor.java b/Mage.Sets/src/mage/cards/b/BloodvialPurveyor.java new file mode 100644 index 00000000000..65095c681f2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BloodvialPurveyor.java @@ -0,0 +1,70 @@ +package mage.cards.b; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.common.SpellCastOpponentTriggeredAbility; +import mage.abilities.dynamicvalue.common.PermanentsTargetOpponentControlsCount; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.common.CreateTokenTargetEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.permanent.token.BloodToken; + +/** + * + * @author weirddan455 + */ +public final class BloodvialPurveyor extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent("Blood token"); + + static { + filter.add(SubType.BLOOD.getPredicate()); + filter.add(TokenPredicate.TRUE); + } + + public BloodvialPurveyor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{B}"); + + this.subtype.add(SubType.VAMPIRE); + this.power = new MageInt(5); + this.toughness = new MageInt(6); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Whenever an opponent casts a spell, that player creates a Blood token. + this.addAbility(new SpellCastOpponentTriggeredAbility( + Zone.BATTLEFIELD, new CreateTokenTargetEffect(new BloodToken()), + StaticFilters.FILTER_SPELL_A, false, + SetTargetPointer.PLAYER + )); + + // Whenever Bloodvial Purveyor attacks, it gets +1/+0 until end of turn for each Blood token defending player controls. + this.addAbility(new AttacksTriggeredAbility( + new BoostSourceEffect(new PermanentsTargetOpponentControlsCount(filter), StaticValue.get(0), Duration.EndOfTurn, true) + .setText("it gets +1/+0 until end of turn for each Blood token defending player controls"), + false, null, SetTargetPointer.PLAYER + )); + } + + private BloodvialPurveyor(final BloodvialPurveyor card) { + super(card); + } + + @Override + public BloodvialPurveyor copy() { + return new BloodvialPurveyor(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BloodyBetrayal.java b/Mage.Sets/src/mage/cards/b/BloodyBetrayal.java new file mode 100644 index 00000000000..bdb35e19334 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BloodyBetrayal.java @@ -0,0 +1,42 @@ +package mage.cards.b; + +import java.util.UUID; + +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.UntapTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.continuous.GainControlTargetEffect; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.game.permanent.token.BloodToken; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author weirddan455 + */ +public final class BloodyBetrayal extends CardImpl { + + public BloodyBetrayal(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{R}"); + + // Gain control of target creature until end of turn. Untap that creature. It gains haste until end of turn. Create a Blood token. + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + 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.")); + this.getSpellAbility().addEffect(new CreateTokenEffect(new BloodToken())); + } + + private BloodyBetrayal(final BloodyBetrayal card) { + super(card); + } + + @Override + public BloodyBetrayal copy() { + return new BloodyBetrayal(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BlossomCladWerewolf.java b/Mage.Sets/src/mage/cards/b/BlossomCladWerewolf.java new file mode 100644 index 00000000000..39737df7b9d --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BlossomCladWerewolf.java @@ -0,0 +1,47 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.mana.AddManaOfAnyColorEffect; +import mage.abilities.keyword.NightboundAbility; +import mage.abilities.mana.SimpleManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Zone; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BlossomCladWerewolf extends CardImpl { + + public BlossomCladWerewolf(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); + + this.subtype.add(SubType.WEREWOLF); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + this.color.setGreen(true); + this.nightCard = true; + + // {T}: Add two mana of any one color. + this.addAbility(new SimpleManaAbility( + Zone.BATTLEFIELD, new AddManaOfAnyColorEffect(2), new TapSourceCost() + )); + + // Nightbound + this.addAbility(new NightboundAbility()); + } + + private BlossomCladWerewolf(final BlossomCladWerewolf card) { + super(card); + } + + @Override + public BlossomCladWerewolf copy() { + return new BlossomCladWerewolf(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BlossomPrancer.java b/Mage.Sets/src/mage/cards/b/BlossomPrancer.java new file mode 100644 index 00000000000..7b526d5da71 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BlossomPrancer.java @@ -0,0 +1,99 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.ReachAbility; +import mage.cards.*; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInLibrary; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BlossomPrancer extends CardImpl { + + public BlossomPrancer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}{G}"); + + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Reach + this.addAbility(ReachAbility.getInstance()); + + // When Blossom Prancer enters the battlefield, look at the top five cards of your library. You may reveal a creature or enchantment card from among them and put it into your hand. Put the rest on the bottom of your library in a random order. If you didn't put a card into your hand this way, you gain 4 life. + this.addAbility(new EntersBattlefieldTriggeredAbility(new BlossomPrancerEffect())); + } + + private BlossomPrancer(final BlossomPrancer card) { + super(card); + } + + @Override + public BlossomPrancer copy() { + return new BlossomPrancer(this); + } +} + +class BlossomPrancerEffect extends OneShotEffect { + + private static final FilterCard filter = new FilterCard("creature or enchantment card"); + + static { + filter.add(Predicates.or( + CardType.CREATURE.getPredicate(), + CardType.ENCHANTMENT.getPredicate() + )); + } + + BlossomPrancerEffect() { + super(Outcome.Benefit); + staticText = "look at the top five cards of your library. You may reveal a creature or enchantment card from " + + "among them and put it into your hand. Put the rest on the bottom of your library in a random order. " + + "If you didn't put a card into your hand this way, you gain 4 life"; + } + + private BlossomPrancerEffect(final BlossomPrancerEffect effect) { + super(effect); + } + + @Override + public BlossomPrancerEffect copy() { + return new BlossomPrancerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Cards cards = new CardsImpl(player.getLibrary().getTopCards(game, 5)); + TargetCard target = new TargetCardInLibrary(0, 1, filter); + player.choose(outcome, cards, target, game); + Card card = game.getCard(target.getFirstTarget()); + if (card != null) { + player.revealCards(source, new CardsImpl(card), game); + player.moveCards(card, Zone.HAND, source, game); + cards.remove(card); + player.putCardsOnBottomOfLibrary(card, game, source, false); + } else { + player.putCardsOnBottomOfLibrary(card, game, source, false); + player.gainLife(4, game, source); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/b/BlueWard.java b/Mage.Sets/src/mage/cards/b/BlueWard.java index 3366356ce0d..8c2e631858c 100644 --- a/Mage.Sets/src/mage/cards/b/BlueWard.java +++ b/Mage.Sets/src/mage/cards/b/BlueWard.java @@ -1,36 +1,29 @@ - package mage.cards.b; -import java.util.UUID; import mage.ObjectColor; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; import mage.abilities.keyword.EnchantAbility; import mage.abilities.keyword.ProtectionAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.ColorPredicate; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LoneFox */ public final class BlueWard extends CardImpl { - private static final FilterCard filter = new FilterCard("blue"); - - static { - filter.add(new ColorPredicate(ObjectColor.BLUE)); - } - public BlueWard(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}"); this.subtype.add(SubType.AURA); // Enchant creature @@ -38,12 +31,11 @@ public final class BlueWard extends CardImpl { this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.Protect)); this.addAbility(new EnchantAbility(auraTarget.getTargetName())); + // Enchanted creature has protection from blue. This effect doesn't remove Blue Ward. - ProtectionAbility gainedAbility = new ProtectionAbility(filter); - gainedAbility.setAuraIdNotToBeRemoved(this.getId()); - Effect effect = new GainAbilityAttachedEffect(gainedAbility, AttachmentType.AURA); - effect.setText("Enchanted creature has protection from blue. This effect doesn't remove {this}."); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); + this.addAbility(new SimpleStaticAbility(new GainAbilityAttachedEffect( + ProtectionAbility.from(ObjectColor.BLUE), AttachmentType.AURA + ).setDoesntRemoveItself(true))); } private BlueWard(final BlueWard card) { diff --git a/Mage.Sets/src/mage/cards/b/BoardedWindow.java b/Mage.Sets/src/mage/cards/b/BoardedWindow.java new file mode 100644 index 00000000000..a762fc91c62 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BoardedWindow.java @@ -0,0 +1,102 @@ +package mage.cards.b; + +import java.util.UUID; + +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.effects.common.ExileSourceEffect; +import mage.abilities.effects.common.continuous.BoostAllEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.common.FilterAttackingCreature; +import mage.game.Game; +import mage.game.combat.CombatGroup; +import mage.game.permanent.Permanent; +import mage.watchers.common.DamageDoneWatcher; + +/** + * + * @author weirddan455 + */ +public final class BoardedWindow extends CardImpl { + + private static final BoardedWindowFilter filter = new BoardedWindowFilter(); + + public BoardedWindow(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); + + // Creatures attacking you get -1/-0. + this.addAbility(new SimpleStaticAbility(new BoostAllEffect(-1, 0, Duration.WhileOnBattlefield, filter, false))); + + // At the beginning of each end step, if you were dealt 4 or more damage this turn, exile Boarded Window. + this.addAbility(new BeginningOfEndStepTriggeredAbility( + Zone.BATTLEFIELD, new ExileSourceEffect(), TargetController.ANY, BoardedWindowCondition.instance, false + ), new DamageDoneWatcher()); + } + + private BoardedWindow(final BoardedWindow card) { + super(card); + } + + @Override + public BoardedWindow copy() { + return new BoardedWindow(this); + } +} + +class BoardedWindowFilter extends FilterAttackingCreature { + + public BoardedWindowFilter() { + super("creatures attacking you"); + } + + private BoardedWindowFilter(final BoardedWindowFilter filter) { + super(filter); + } + + @Override + public BoardedWindowFilter copy() { + return new BoardedWindowFilter(this); + } + + @Override + public boolean match(Permanent permanent, UUID sourceId, UUID playerId, Game game) { + if (!super.match(permanent, sourceId, playerId, game)) { + return false; + } + + for (CombatGroup group : game.getCombat().getGroups()) { + for (UUID attacker : group.getAttackers()) { + if (attacker.equals(permanent.getId())) { + UUID defenderId = group.getDefenderId(); + if (defenderId.equals(playerId)) { + return true; + } + } + } + } + + return false; + } +} + +enum BoardedWindowCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + DamageDoneWatcher watcher = game.getState().getWatcher(DamageDoneWatcher.class); + return watcher != null && watcher.damageDoneTo(source.getControllerId(), 0, game) >= 4; + } + + @Override + public String toString() { + return "you were dealt 4 or more damage this turn"; + } +} diff --git a/Mage.Sets/src/mage/cards/b/BodyOfKnowledge.java b/Mage.Sets/src/mage/cards/b/BodyOfKnowledge.java index 6cdacf39639..b28e836ac1d 100644 --- a/Mage.Sets/src/mage/cards/b/BodyOfKnowledge.java +++ b/Mage.Sets/src/mage/cards/b/BodyOfKnowledge.java @@ -44,7 +44,7 @@ public final class BodyOfKnowledge extends CardImpl { // Whenever Body of Knowledge is dealt damage, draw that many cards. this.addAbility(new DealtDamageToSourceTriggeredAbility( - new BodyOfKnowledgeEffect(), false, false, true + new BodyOfKnowledgeEffect(), false, false )); } diff --git a/Mage.Sets/src/mage/cards/b/BondOfFlourishing.java b/Mage.Sets/src/mage/cards/b/BondOfFlourishing.java index 840a0fe5767..96581841a43 100644 --- a/Mage.Sets/src/mage/cards/b/BondOfFlourishing.java +++ b/Mage.Sets/src/mage/cards/b/BondOfFlourishing.java @@ -16,7 +16,7 @@ import java.util.UUID; */ public final class BondOfFlourishing extends CardImpl { - private static final FilterCard filter = new FilterPermanentCard(); + private static final FilterCard filter = new FilterPermanentCard("a permanent card"); public BondOfFlourishing(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{G}"); diff --git a/Mage.Sets/src/mage/cards/b/BoneHarvest.java b/Mage.Sets/src/mage/cards/b/BoneHarvest.java index d914b6658be..abfb66a2c53 100644 --- a/Mage.Sets/src/mage/cards/b/BoneHarvest.java +++ b/Mage.Sets/src/mage/cards/b/BoneHarvest.java @@ -1,4 +1,3 @@ - package mage.cards.b; import java.util.UUID; @@ -9,7 +8,7 @@ import mage.abilities.effects.common.PutOnLibraryTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterCreatureCard; +import mage.filter.StaticFilters; import mage.target.common.TargetCardInYourGraveyard; /** @@ -23,7 +22,7 @@ public final class BoneHarvest extends CardImpl { // Put any number of target creature cards from your graveyard on top of your library. this.getSpellAbility().addEffect(new PutOnLibraryTargetEffect(true)); - this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, Integer.MAX_VALUE, new FilterCreatureCard("creature cards from your graveyard"))); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, Integer.MAX_VALUE, StaticFilters.FILTER_CARD_CREATURES_YOUR_GRAVEYARD)); // Draw a card at the beginning of the next turn's upkeep. this.getSpellAbility().addEffect(new CreateDelayedTriggeredAbilityEffect(new AtTheBeginOfNextUpkeepDelayedTriggeredAbility(new DrawCardSourceControllerEffect(1)), false)); diff --git a/Mage.Sets/src/mage/cards/b/BonescytheSliver.java b/Mage.Sets/src/mage/cards/b/BonescytheSliver.java index d386ccdd282..fc222e32f03 100644 --- a/Mage.Sets/src/mage/cards/b/BonescytheSliver.java +++ b/Mage.Sets/src/mage/cards/b/BonescytheSliver.java @@ -30,7 +30,7 @@ public final class BonescytheSliver extends CardImpl { // Sliver creatures you control have double strike. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledEffect(DoubleStrikeAbility.getInstance(), - Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS))); + Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_SLIVERS))); } private BonescytheSliver(final BonescytheSliver card) { diff --git a/Mage.Sets/src/mage/cards/b/BonesplitterSliver.java b/Mage.Sets/src/mage/cards/b/BonesplitterSliver.java index a3b89d1af4a..70c8ec855c0 100644 --- a/Mage.Sets/src/mage/cards/b/BonesplitterSliver.java +++ b/Mage.Sets/src/mage/cards/b/BonesplitterSliver.java @@ -27,7 +27,7 @@ public final class BonesplitterSliver extends CardImpl { // All Sliver creatures get +2/+0. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, - new BoostAllEffect(2, 0, Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS, false))); + new BoostAllEffect(2, 0, Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_ALL_SLIVERS, false))); } private BonesplitterSliver(final BonesplitterSliver card) { diff --git a/Mage.Sets/src/mage/cards/b/BontuTheGlorified.java b/Mage.Sets/src/mage/cards/b/BontuTheGlorified.java index 1788c63fcdb..25702c91152 100644 --- a/Mage.Sets/src/mage/cards/b/BontuTheGlorified.java +++ b/Mage.Sets/src/mage/cards/b/BontuTheGlorified.java @@ -47,7 +47,7 @@ public final class BontuTheGlorified extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BontuTheGlorifiedRestrictionEffect()), new CreaturesDiedWatcher()); // {1}{B}, Sacrifice another creature: Scry 1. Each opponent loses 1 life and you gain 1 life. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ScryEffect(1), new ManaCostsImpl("{1}{B}")); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ScryEffect(1, false), new ManaCostsImpl("{1}{B}")); ability.addEffect(new LoseLifeOpponentsEffect(1)); Effect effect = new GainLifeEffect(1); effect.setText("and you gain 1 life"); diff --git a/Mage.Sets/src/mage/cards/b/BoonOfBoseiju.java b/Mage.Sets/src/mage/cards/b/BoonOfBoseiju.java new file mode 100644 index 00000000000..868f4ecc395 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BoonOfBoseiju.java @@ -0,0 +1,78 @@ +package mage.cards.b; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.UntapTargetEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BoonOfBoseiju extends CardImpl { + + public BoonOfBoseiju(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}"); + + // Target creature gets +X/+X until end of turn, where X is the greatest mana value among permanents you control. Untap that creature. + this.getSpellAbility().addEffect(new BoostTargetEffect( + BoonOfBoseijuValue.instance, BoonOfBoseijuValue.instance, Duration.EndOfTurn + )); + this.getSpellAbility().addEffect(new UntapTargetEffect().setText("Untap it")); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + this.getSpellAbility().addHint(BoonOfBoseijuValue.getHint()); + } + + private BoonOfBoseiju(final BoonOfBoseiju card) { + super(card); + } + + @Override + public BoonOfBoseiju copy() { + return new BoonOfBoseiju(this); + } +} + +enum BoonOfBoseijuValue implements DynamicValue { + instance; + private static final Hint hint = new ValueHint("The greatest mana value among permanents you control", instance); + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + return game.getBattlefield().getActivePermanents( + StaticFilters.FILTER_CONTROLLED_PERMANENT, + sourceAbility.getControllerId(), sourceAbility.getSourceId(), game + ).stream().mapToInt(MageObject::getManaValue).sum(); + } + + @Override + public BoonOfBoseijuValue copy() { + return this; + } + + @Override + public String getMessage() { + return "the greatest mana value among permanents you control"; + } + + @Override + public String toString() { + return "X"; + } + + public static Hint getHint() { + return hint; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/b/BorborygmosEnraged.java b/Mage.Sets/src/mage/cards/b/BorborygmosEnraged.java index 21384ad2b10..41eb97420b2 100644 --- a/Mage.Sets/src/mage/cards/b/BorborygmosEnraged.java +++ b/Mage.Sets/src/mage/cards/b/BorborygmosEnraged.java @@ -16,7 +16,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; import mage.constants.Zone; -import mage.filter.common.FilterLandCard; +import mage.filter.StaticFilters; import mage.target.common.TargetCardInHand; import mage.target.common.TargetAnyTarget; @@ -39,10 +39,10 @@ public final class BorborygmosEnraged extends CardImpl { this.addAbility(TrampleAbility.getInstance()); //Whenever Borborygmous Enraged deals combat damage to a player, reveal the top three cards of your library. Put all land cards revealed this way into your hand and the rest into your graveyard. - this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new RevealLibraryPutIntoHandEffect(3, new FilterLandCard(), Zone.GRAVEYARD), false, false)); + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new RevealLibraryPutIntoHandEffect(3, StaticFilters.FILTER_CARD_LANDS, Zone.GRAVEYARD), false, false)); //Discard a land card: Borborygmos Enraged deals 3 damage to any target - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(3), new DiscardTargetCost(new TargetCardInHand(new FilterLandCard()))); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(3), new DiscardTargetCost(new TargetCardInHand(StaticFilters.FILTER_CARD_LAND_A))); ability.addTarget(new TargetAnyTarget()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/b/BornToDrive.java b/Mage.Sets/src/mage/cards/b/BornToDrive.java new file mode 100644 index 00000000000..2463ae4378a --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BornToDrive.java @@ -0,0 +1,77 @@ +package mage.cards.b; + +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.AttachedToMatchesFilterCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.ChannelAbility; +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.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.game.permanent.token.PilotToken; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BornToDrive extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent(); + + static { + filter.add(Predicates.or( + CardType.CREATURE.getPredicate(), + SubType.VEHICLE.getPredicate() + )); + } + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter); + private static final Hint hint = new ValueHint("Creatures and Vehicles you control", xValue); + private static final Condition condition = new AttachedToMatchesFilterCondition(StaticFilters.FILTER_PERMANENT_CREATURE); + + public BornToDrive(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); + + 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 +1/+1 for each creature and/or Vehicle you control. + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new BoostEnchantedEffect(xValue, xValue), condition, "as long as enchanted permanent " + + "is a creature, it gets +1/+1 for each creature and/or Vehicle you control" + )).addHint(hint)); + + // Channel — {2}{W}, Discard Born to Drive: Create two 1/1 colorless Pilot creature tokens with "This creature crews Vehicles as though its power were 2 greater." + this.addAbility(new ChannelAbility("{2}{W}", new CreateTokenEffect(new PilotToken(), 2))); + } + + private BornToDrive(final BornToDrive card) { + super(card); + } + + @Override + public BornToDrive copy() { + return new BornToDrive(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BorosBattleshaper.java b/Mage.Sets/src/mage/cards/b/BorosBattleshaper.java index e444b7fdece..c674162bc07 100644 --- a/Mage.Sets/src/mage/cards/b/BorosBattleshaper.java +++ b/Mage.Sets/src/mage/cards/b/BorosBattleshaper.java @@ -1,5 +1,3 @@ - - package mage.cards.b; import java.util.UUID; @@ -28,28 +26,24 @@ import mage.target.targetpointer.FixedTarget; * * @author LevelX2 */ - - public final class BorosBattleshaper extends CardImpl { - public BorosBattleshaper (UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{5}{R}{W}"); + public BorosBattleshaper(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{R}{W}"); this.subtype.add(SubType.MINOTAUR, SubType.SOLDIER); - this.power = new MageInt(5); this.toughness = new MageInt(5); // At the beginning of each combat, up to one target creature attacks or blocks this combat if able and up to one target creature can't attack or block this combat. Ability ability = new BeginningOfCombatTriggeredAbility(Zone.BATTLEFIELD, new BorosBattleshaperEffect(), TargetController.ANY, false, false); - ability.addTarget(new TargetCreaturePermanent(0,1,new FilterCreaturePermanent("creature that attacks or blocks if able"),false)); - ability.addTarget(new TargetCreaturePermanent(0,1,new FilterCreaturePermanent("creature that can't attack or block"),false)); + ability.addTarget(new TargetCreaturePermanent(0, 1, new FilterCreaturePermanent("creature that attacks or blocks if able"), false)); + ability.addTarget(new TargetCreaturePermanent(0, 1, new FilterCreaturePermanent("creature that can't attack or block"), false)); this.addAbility(ability); - } - public BorosBattleshaper (final BorosBattleshaper card) { + public BorosBattleshaper(final BorosBattleshaper card) { super(card); } @@ -83,18 +77,18 @@ class BorosBattleshaperEffect extends OneShotEffect { if (game.getOpponents(creature1.getControllerId()).contains(game.getActivePlayerId())) { // Blocks ContinuousEffectImpl effect = new BlocksIfAbleTargetEffect(Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(creature1.getId())); + effect.setTargetPointer(new FixedTarget(creature1.getId(), game)); game.addEffect(effect, source); effect = new GainAbilityTargetEffect(BlocksThisTurnMarkerAbility.getInstance(), Duration.EndOfTurn, ""); - effect.setTargetPointer(new FixedTarget(creature1.getId())); + effect.setTargetPointer(new FixedTarget(creature1.getId(), game)); game.addEffect(effect, source); } else { // Attacks ContinuousEffectImpl effect = new AttacksIfAbleTargetEffect(Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(creature1.getId())); + effect.setTargetPointer(new FixedTarget(creature1.getId(), game)); game.addEffect(effect, source); effect = new GainAbilityTargetEffect(AttacksThisTurnMarkerAbility.getInstance(), Duration.EndOfTurn, ""); - effect.setTargetPointer(new FixedTarget(creature1.getId())); + effect.setTargetPointer(new FixedTarget(creature1.getId(), game)); game.addEffect(effect, source); } @@ -104,12 +98,12 @@ class BorosBattleshaperEffect extends OneShotEffect { if (game.getOpponents(creature2.getControllerId()).contains(game.getActivePlayerId())) { // Blocks ContinuousEffectImpl effect = new CantBlockTargetEffect(Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(creature2.getId())); + effect.setTargetPointer(new FixedTarget(creature2.getId(), game)); game.addEffect(effect, source); } else { // Attacks ContinuousEffectImpl effect = new CantAttackTargetEffect(Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(creature2.getId())); + effect.setTargetPointer(new FixedTarget(creature2.getId(), game)); game.addEffect(effect, source); } } diff --git a/Mage.Sets/src/mage/cards/b/BorosReckoner.java b/Mage.Sets/src/mage/cards/b/BorosReckoner.java index e6130b9c524..bc3a19a925c 100644 --- a/Mage.Sets/src/mage/cards/b/BorosReckoner.java +++ b/Mage.Sets/src/mage/cards/b/BorosReckoner.java @@ -5,15 +5,15 @@ import mage.abilities.Ability; import mage.abilities.common.DealtDamageToSourceTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.dynamicvalue.common.SavedDamageValue; +import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; import mage.abilities.keyword.FirstStrikeAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; import mage.target.common.TargetAnyTarget; import java.util.UUID; @@ -31,13 +31,15 @@ public final class BorosReckoner extends CardImpl { this.toughness = new MageInt(3); // Whenever Boros Reckoner is dealt damage, it deals that much damage to any target. - Ability ability = new DealtDamageToSourceTriggeredAbility(new BorosReckonerDealDamageEffect(), false, false, true); + Ability ability = new DealtDamageToSourceTriggeredAbility(new DamageTargetEffect(SavedDamageValue.instance) + .setText("it deals that much damage to any target"), false, false); ability.addTarget(new TargetAnyTarget()); this.addAbility(ability); // {R/W}: Boros Reckoner gains first strike until end of turn. - this.addAbility(new SimpleActivatedAbility( - Zone.BATTLEFIELD, new GainAbilitySourceEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn), new ManaCostsImpl("{R/W}"))); + this.addAbility(new SimpleActivatedAbility(new GainAbilitySourceEffect( + FirstStrikeAbility.getInstance(), Duration.EndOfTurn + ), new ManaCostsImpl<>("{R/W}"))); } private BorosReckoner(final BorosReckoner card) { @@ -49,38 +51,3 @@ public final class BorosReckoner extends CardImpl { return new BorosReckoner(this); } } - -class BorosReckonerDealDamageEffect extends OneShotEffect { - - public BorosReckonerDealDamageEffect() { - super(Outcome.Damage); - this.staticText = "it deals that much damage to any target"; - } - - public BorosReckonerDealDamageEffect(final BorosReckonerDealDamageEffect effect) { - super(effect); - } - - @Override - public BorosReckonerDealDamageEffect copy() { - return new BorosReckonerDealDamageEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - int amount = (Integer) getValue("damage"); - if (amount > 0) { - Player player = game.getPlayer(targetPointer.getFirst(game, source)); - if (player != null) { - player.damage(amount, source.getSourceId(), source, game); - return true; - } - Permanent creature = game.getPermanent(targetPointer.getFirst(game, source)); - if (creature != null) { - creature.damage(amount, source.getSourceId(), source, game, false, true); - return true; - } - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/b/BoseijuReachesSkyward.java b/Mage.Sets/src/mage/cards/b/BoseijuReachesSkyward.java new file mode 100644 index 00000000000..61aa70ab4e1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BoseijuReachesSkyward.java @@ -0,0 +1,73 @@ +package mage.cards.b; + +import mage.abilities.common.SagaAbility; +import mage.abilities.effects.common.ExileSagaAndReturnTransformedEffect; +import mage.abilities.effects.common.PutOnLibraryTargetEffect; +import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; +import mage.abilities.keyword.TransformAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SagaChapter; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterCard; +import mage.filter.common.FilterLandCard; +import mage.target.common.TargetCardInLibrary; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BoseijuReachesSkyward extends CardImpl { + + private static final FilterCard filter = new FilterCard("basic Forest cards"); + private static final FilterCard filter2 = new FilterLandCard("land card from your graveyard"); + + static { + filter.add(SuperType.BASIC.getPredicate()); + filter.add(SubType.FOREST.getPredicate()); + } + + public BoseijuReachesSkyward(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{G}"); + + this.subtype.add(SubType.SAGA); + this.secondSideCardClazz = mage.cards.b.BranchOfBoseiju.class; + + // (As this Saga enters and after your draw step, add a lore counter.) + SagaAbility sagaAbility = new SagaAbility(this); + + // I — Search your library for up to two basic Forest cards, reveal them, put them into your hand, then shuffle. + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_I, + new SearchLibraryPutInHandEffect(new TargetCardInLibrary( + 0, 2, filter + ), true, true) + ); + + // II — Put up to one target land card from your graveyard on top of your library. + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_II, SagaChapter.CHAPTER_II, + new PutOnLibraryTargetEffect(true), + new TargetCardInYourGraveyard(0, 1, filter2) + ); + + // III — Exile this Saga, then return it to the battlefield transformed under your control. + this.addAbility(new TransformAbility()); + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, new ExileSagaAndReturnTransformedEffect()); + + this.addAbility(sagaAbility); + } + + private BoseijuReachesSkyward(final BoseijuReachesSkyward card) { + super(card); + } + + @Override + public BoseijuReachesSkyward copy() { + return new BoseijuReachesSkyward(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BoseijuWhoEndures.java b/Mage.Sets/src/mage/cards/b/BoseijuWhoEndures.java new file mode 100644 index 00000000000..8ea7d4862d2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BoseijuWhoEndures.java @@ -0,0 +1,121 @@ +package mage.cards.b; + +import mage.abilities.Ability; +import mage.abilities.costs.costadjusters.LegendaryCreatureCostAdjuster; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.ChannelAbility; +import mage.abilities.mana.GreenManaAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterLandCard; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.common.TargetCardInLibrary; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BoseijuWhoEndures extends CardImpl { + + private static final FilterPermanent filter + = new FilterPermanent("artifact, enchantment, or nonbasic land an opponent controls"); + + static { + filter.add(TargetController.OPPONENT.getControllerPredicate()); + filter.add(Predicates.or( + CardType.ARTIFACT.getPredicate(), + CardType.ENCHANTMENT.getPredicate(), + Predicates.and( + Predicates.not(SuperType.BASIC.getPredicate()), + CardType.LAND.getPredicate() + ) + )); + } + + public BoseijuWhoEndures(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + this.addSuperType(SuperType.LEGENDARY); + + // {T}: Add {G}. + this.addAbility(new GreenManaAbility()); + + // Channel — {1}{G}, Discard Boseiju, Who Endures: Destroy target artifact, enchantment, or nonbasic land an opponent controls. That player may search their library for a land card with a basic land type, put it onto the battlefield, then shuffle. This ability costs {1} less to activate for each legendary creature you control. + Ability ability = new ChannelAbility("{1}{G}", new BoseijuWhoEnduresEffect()); + ability.addTarget(new TargetPermanent(filter)); + ability.setCostAdjuster(LegendaryCreatureCostAdjuster.instance); + this.addAbility(ability); + } + + private BoseijuWhoEndures(final BoseijuWhoEndures card) { + super(card); + } + + @Override + public BoseijuWhoEndures copy() { + return new BoseijuWhoEndures(this); + } +} + +class BoseijuWhoEnduresEffect extends OneShotEffect { + + private static final FilterCard filter = new FilterLandCard("land card with a basic land type"); + + static { + filter.add(Predicates.or( + SubType.PLAINS.getPredicate(), + SubType.ISLAND.getPredicate(), + SubType.SWAMP.getPredicate(), + SubType.MOUNTAIN.getPredicate(), + SubType.FOREST.getPredicate() + )); + } + + BoseijuWhoEnduresEffect() { + super(Outcome.Benefit); + staticText = "destroy target artifact, enchantment, or nonbasic land an opponent controls. " + + "That player may search their library for a land card with a basic land type, " + + "put it onto the battlefield, then shuffle. " + + "This ability costs {1} less to activate for each legendary creature you control"; + } + + private BoseijuWhoEnduresEffect(final BoseijuWhoEnduresEffect effect) { + super(effect); + } + + @Override + public BoseijuWhoEnduresEffect copy() { + return new BoseijuWhoEnduresEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (controller == null || permanent == null) { + return false; + } + Player player = game.getPlayer(permanent.getControllerId()); + permanent.destroy(source, game); + if (!player.chooseUse(Outcome.PutCardInPlay, "Search your library for a land card?", source, game)) { + return true; + } + TargetCardInLibrary target = new TargetCardInLibrary(filter); + player.searchLibrary(target, source, game); + Card card = player.getLibrary().getCard(target.getFirstTarget(), game); + if (card != null) { + player.moveCards(card, Zone.BATTLEFIELD, source, game); + } + player.shuffleLibrary(source, game); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/b/BottledCloister.java b/Mage.Sets/src/mage/cards/b/BottledCloister.java index d64cea6e22f..91abfd1f7c3 100644 --- a/Mage.Sets/src/mage/cards/b/BottledCloister.java +++ b/Mage.Sets/src/mage/cards/b/BottledCloister.java @@ -1,6 +1,5 @@ package mage.cards.b; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.effects.OneShotEffect; @@ -12,6 +11,7 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.TargetController; import mage.constants.Zone; +import mage.game.ExileZone; import mage.game.Game; import mage.players.Player; import mage.util.CardUtil; @@ -66,15 +66,14 @@ class BottledCloisterExileEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = source.getSourceObject(game); - if (controller == null || sourceObject == null) { + if (controller == null) { return false; } Cards cards = new CardsImpl(controller.getHand()); if (cards.isEmpty()) { return false; } - controller.moveCardsToExile(cards.getCards(game), source, game, false, CardUtil.getExileZoneId(game, source), sourceObject.getIdName()); + controller.moveCardsToExile(cards.getCards(game), source, game, false, CardUtil.getExileZoneId(game, source), CardUtil.getSourceName(game, source)); cards.getCards(game) .stream() .filter(c -> game.getState().getZone(c.getId()) == Zone.EXILED) @@ -102,10 +101,11 @@ class BottledCloisterReturnEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - if (player == null) { + ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source)); + if (player == null || exileZone == null || exileZone.isEmpty()) { return false; } - Cards cards = new CardsImpl(game.getExile().getExileZone(CardUtil.getExileZoneId(game, source)).getCards(game)); + Cards cards = new CardsImpl(exileZone.getCards(game)); cards.removeIf(uuid -> !player.getId().equals(game.getOwnerId(uuid))); player.moveCards(cards, Zone.HAND, source, game); player.drawCards(1, source, game); diff --git a/Mage.Sets/src/mage/cards/b/BottomlessVault.java b/Mage.Sets/src/mage/cards/b/BottomlessVault.java index b6ec32d0f2a..ac3f4735a15 100644 --- a/Mage.Sets/src/mage/cards/b/BottomlessVault.java +++ b/Mage.Sets/src/mage/cards/b/BottomlessVault.java @@ -39,7 +39,7 @@ public final class BottomlessVault extends CardImpl { this.addAbility(new SkipUntapOptionalAbility()); // At the beginning of your upkeep, if Bottomless Vault is tapped, put a storage counter on it. OneShotEffect addStorageCounter = new AddCountersSourceEffect(CounterType.STORAGE.createInstance()); - Effect effect = new ConditionalOneShotEffect(addStorageCounter, SourceTappedCondition.instance, "if {this} is tapped, put a storage counter on it"); + Effect effect = new ConditionalOneShotEffect(addStorageCounter, SourceTappedCondition.TAPPED, "if {this} is tapped, put a storage counter on it"); this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, effect, TargetController.YOU, false)); // {tap}, Remove any number of storage counters from Bottomless Vault: Add {B} for each storage counter removed this way. Ability ability = new DynamicManaAbility( diff --git a/Mage.Sets/src/mage/cards/b/Boulderfall.java b/Mage.Sets/src/mage/cards/b/Boulderfall.java index b69157265bf..aa052a79005 100644 --- a/Mage.Sets/src/mage/cards/b/Boulderfall.java +++ b/Mage.Sets/src/mage/cards/b/Boulderfall.java @@ -1,4 +1,3 @@ - package mage.cards.b; import java.util.UUID; @@ -17,7 +16,7 @@ public final class Boulderfall extends CardImpl { public Boulderfall(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{6}{R}{R}"); - // Boulderfall deals 5 damage divided as you choose among any number of target creatures and/or players. + // Boulderfall deals 5 damage divided as you choose among any number of targets. this.getSpellAbility().addEffect(new DamageMultiEffect(5)); this.getSpellAbility().addTarget(new TargetAnyTargetAmount(5)); } diff --git a/Mage.Sets/src/mage/cards/b/BounteousKirin.java b/Mage.Sets/src/mage/cards/b/BounteousKirin.java index 52be3231c38..9ba1fb92226 100644 --- a/Mage.Sets/src/mage/cards/b/BounteousKirin.java +++ b/Mage.Sets/src/mage/cards/b/BounteousKirin.java @@ -32,7 +32,7 @@ public final class BounteousKirin extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); // Whenever you cast a Spirit or Arcane spell, you may gain life equal to that spell's converted mana cost. - this.addAbility(new SpellCastControllerTriggeredAbility(Zone.BATTLEFIELD, new BounteousKirinEffect(), StaticFilters.SPIRIT_OR_ARCANE_CARD, true, true)); + this.addAbility(new SpellCastControllerTriggeredAbility(Zone.BATTLEFIELD, new BounteousKirinEffect(), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true, true)); } private BounteousKirin(final BounteousKirin card) { diff --git a/Mage.Sets/src/mage/cards/b/BountyHunter.java b/Mage.Sets/src/mage/cards/b/BountyHunter.java index 1f232ae43cf..75bb15f9c3f 100644 --- a/Mage.Sets/src/mage/cards/b/BountyHunter.java +++ b/Mage.Sets/src/mage/cards/b/BountyHunter.java @@ -1,8 +1,6 @@ - package mage.cards.b; import mage.MageInt; -import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; @@ -14,9 +12,8 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; import mage.counters.CounterType; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ColorPredicate; import mage.target.common.TargetCreaturePermanent; import java.util.UUID; @@ -27,12 +24,10 @@ import java.util.UUID; */ public final class BountyHunter extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent("creature with a bounty counter on it"); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with a bounty counter on it"); static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - filter2.add(CounterType.BOUNTY.getPredicate()); + filter.add(CounterType.BOUNTY.getPredicate()); } public BountyHunter(UUID ownerId, CardSetInfo setInfo) { @@ -43,11 +38,11 @@ public final class BountyHunter extends CardImpl { // {tap}: Put a bounty counter on target nonblack creature. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersTargetEffect(CounterType.BOUNTY.createInstance()), new TapSourceCost()); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); this.addAbility(ability); // {tap}: Destroy target creature with a bounty counter on it. ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new TapSourceCost()); - ability.addTarget(new TargetCreaturePermanent(filter2)); + ability.addTarget(new TargetCreaturePermanent(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/b/BountyOfTheHunt.java b/Mage.Sets/src/mage/cards/b/BountyOfTheHunt.java index 16aeceb2d3b..6def70de482 100644 --- a/Mage.Sets/src/mage/cards/b/BountyOfTheHunt.java +++ b/Mage.Sets/src/mage/cards/b/BountyOfTheHunt.java @@ -1,7 +1,5 @@ - package mage.cards.b; -import java.util.UUID; import mage.ObjectColor; import mage.abilities.costs.AlternativeCostSourceAbility; import mage.abilities.costs.common.ExileFromHandCost; @@ -11,29 +9,35 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.counters.CounterType; import mage.filter.common.FilterOwnedCard; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardIdPredicate; import mage.filter.predicate.mageobject.ColorPredicate; import mage.target.common.TargetCardInHand; import mage.target.common.TargetCreaturePermanentAmount; +import java.util.UUID; + /** - * * @author LoneFox */ public final class BountyOfTheHunt extends CardImpl { + private static final FilterOwnedCard filter + = new FilterOwnedCard("a green card from your hand"); + + static { + filter.add(new ColorPredicate(ObjectColor.GREEN)); + } + public BountyOfTheHunt(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{3}{G}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{G}{G}"); // You may exile a green card from your hand rather than pay Bounty of the Hunt's mana cost. - FilterOwnedCard filter = new FilterOwnedCard("green card from your hand"); - filter.add(new ColorPredicate(ObjectColor.GREEN)); - filter.add(Predicates.not(new CardIdPredicate(this.getId()))); // the exile cost can never be paid with the card itself this.addAbility(new AlternativeCostSourceAbility(new ExileFromHandCost(new TargetCardInHand(filter)))); // Distribute three +1/+1 counters among one, two, or three target creatures. For each +1/+1 counter you put on a creature this way, remove a +1/+1 counter from that creature at the beginning of the next cleanup step. - this.getSpellAbility().addEffect(new DistributeCountersEffect(CounterType.P1P1, 3, true, "one, two, or three target creatures")); + this.getSpellAbility().addEffect(new DistributeCountersEffect( + CounterType.P1P1, 3, true, + "one, two, or three target creatures" + )); this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount(3)); } diff --git a/Mage.Sets/src/mage/cards/b/BowOfNylea.java b/Mage.Sets/src/mage/cards/b/BowOfNylea.java index e9cafccc9a6..c5ad23462ea 100644 --- a/Mage.Sets/src/mage/cards/b/BowOfNylea.java +++ b/Mage.Sets/src/mage/cards/b/BowOfNylea.java @@ -1,4 +1,3 @@ - package mage.cards.b; import java.util.UUID; @@ -8,9 +7,9 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.PutOnLibraryTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.abilities.keyword.DeathtouchAbility; @@ -18,13 +17,9 @@ import mage.abilities.keyword.FlyingAbility; import mage.cards.*; import mage.constants.*; import mage.counters.CounterType; -import mage.filter.FilterCard; -import mage.filter.common.FilterAttackingCreature; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.AbilityPredicate; -import mage.game.Game; -import mage.players.Player; -import mage.target.Target; import mage.target.common.TargetCardInYourGraveyard; import mage.target.common.TargetCreaturePermanent; @@ -43,13 +38,11 @@ public final class BowOfNylea extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT,CardType.ARTIFACT},"{1}{G}{G}"); addSuperType(SuperType.LEGENDARY); - // Attacking creatures you control have deathtouch. - GainAbilityControlledEffect gainEffect = new GainAbilityControlledEffect(DeathtouchAbility.getInstance(), Duration.WhileOnBattlefield, new FilterAttackingCreature("Attacking creatures"), false); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, gainEffect)); - + this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect(DeathtouchAbility.getInstance(), Duration.WhileOnBattlefield, StaticFilters.FILTER_ATTACKING_CREATURES, false))); + // {1}{G}, {T}: Choose one - Put a +1/+1 counter on target creature; - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, + Ability ability = new SimpleActivatedAbility( new AddCountersTargetEffect(CounterType.P1P1.createInstance()), new ManaCostsImpl("{1}{G}")); ability.addTarget(new TargetCreaturePermanent()); @@ -57,8 +50,7 @@ public final class BowOfNylea extends CardImpl { // or Bow of Nylea deals 2 damage to target creature with flying; Mode mode = new Mode(); mode.addEffect(new DamageTargetEffect(2)); - Target target = new TargetCreaturePermanent(filterFlying); - mode.addTarget(target); + mode.addTarget(new TargetCreaturePermanent(filterFlying)); ability.addMode(mode); // or you gain 3 life; mode = new Mode(); @@ -66,12 +58,11 @@ public final class BowOfNylea extends CardImpl { ability.addMode(mode); // or put up to four target cards from your graveyard on the bottom of your library in any order. mode = new Mode(); - mode.addEffect(new PutCardsFromGraveyardToLibraryEffect()); - mode.addTarget(new TargetCardInYourGraveyard(0,4, new FilterCard())); + mode.addEffect(new PutOnLibraryTargetEffect(false, "put up to four target cards from your graveyard on the bottom of your library in any order")); + mode.addTarget(new TargetCardInYourGraveyard(0, 4, StaticFilters.FILTER_CARDS_FROM_YOUR_GRAVEYARD)); ability.addMode(mode); this.addAbility(ability); - } private BowOfNylea(final BowOfNylea card) { @@ -83,36 +74,3 @@ public final class BowOfNylea extends CardImpl { return new BowOfNylea(this); } } - -class PutCardsFromGraveyardToLibraryEffect extends OneShotEffect { - - public PutCardsFromGraveyardToLibraryEffect() { - super(Outcome.Detriment); - this.staticText = "put up to four target cards from your graveyard on the bottom of your library in any order"; - } - - public PutCardsFromGraveyardToLibraryEffect(final PutCardsFromGraveyardToLibraryEffect effect) { - super(effect); - } - - @Override - public PutCardsFromGraveyardToLibraryEffect copy() { - return new PutCardsFromGraveyardToLibraryEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Cards cards = new CardsImpl(); - for (UUID cardId : this.getTargetPointer().getTargets(game, source)) { - Card card = controller.getGraveyard().get(cardId, game); - if (card != null) { - cards.add(card); - } - } - return controller.putCardsOnBottomOfLibrary(cards, game, source, true); - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/b/BrackwaterElemental.java b/Mage.Sets/src/mage/cards/b/BrackwaterElemental.java index 5f3f2be056c..99c585ef54f 100644 --- a/Mage.Sets/src/mage/cards/b/BrackwaterElemental.java +++ b/Mage.Sets/src/mage/cards/b/BrackwaterElemental.java @@ -69,7 +69,7 @@ class BrackwaterElementalSacrificeEffect extends OneShotEffect { Permanent sourcePermanent = game.getPermanent(source.getSourceId()); if (sourcePermanent != null) { SacrificeTargetEffect sacrificeEffect = new SacrificeTargetEffect("sacrifice {this}"); - sacrificeEffect.setTargetPointer(new FixedTarget(sourcePermanent.getId())); + sacrificeEffect.setTargetPointer(new FixedTarget(sourcePermanent.getId(), game)); game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(sacrificeEffect), source); } return false; diff --git a/Mage.Sets/src/mage/cards/b/Brainbite.java b/Mage.Sets/src/mage/cards/b/Brainbite.java index ca85112a55c..ce767dff899 100644 --- a/Mage.Sets/src/mage/cards/b/Brainbite.java +++ b/Mage.Sets/src/mage/cards/b/Brainbite.java @@ -21,7 +21,7 @@ public final class Brainbite extends CardImpl { // Target opponent reveals their hand. You choose a card from it. That player discards that card. this.getSpellAbility().addEffect(new DiscardCardYouChooseTargetEffect()); // Draw a card. - this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("
")); this.getSpellAbility().addTarget(new TargetOpponent()); } diff --git a/Mage.Sets/src/mage/cards/b/BrambleWurm.java b/Mage.Sets/src/mage/cards/b/BrambleWurm.java new file mode 100644 index 00000000000..0342c37f7ec --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BrambleWurm.java @@ -0,0 +1,57 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.ExileSourceFromGraveCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.keyword.ReachAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Zone; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BrambleWurm extends CardImpl { + + public BrambleWurm(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{6}{G}"); + + this.subtype.add(SubType.WURM); + this.power = new MageInt(7); + this.toughness = new MageInt(6); + + // Reach + this.addAbility(ReachAbility.getInstance()); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // When Bramble Wurm enters the battlefield, you gain 5 life. + this.addAbility(new EntersBattlefieldTriggeredAbility(new GainLifeEffect(5))); + + // {2}{G}, Exile Bramble Wurm from your graveyard: You gain 5 life. + Ability ability = new SimpleActivatedAbility( + Zone.GRAVEYARD, new GainLifeEffect(5), new ManaCostsImpl<>("{2}{G}") + ); + ability.addCost(new ExileSourceFromGraveCost()); + this.addAbility(ability); + } + + private BrambleWurm(final BrambleWurm card) { + super(card); + } + + @Override + public BrambleWurm copy() { + return new BrambleWurm(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BramblewoodParagon.java b/Mage.Sets/src/mage/cards/b/BramblewoodParagon.java index 1424fdf67a7..d4795d6df0b 100644 --- a/Mage.Sets/src/mage/cards/b/BramblewoodParagon.java +++ b/Mage.Sets/src/mage/cards/b/BramblewoodParagon.java @@ -12,7 +12,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.counters.CounterType; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.EntersTheBattlefieldEvent; import mage.game.events.GameEvent; @@ -24,12 +24,6 @@ import mage.game.permanent.Permanent; */ public final class BramblewoodParagon extends CardImpl { - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("Each creature you control with a +1/+1 counter on it"); - - static { - filter.add(CounterType.P1P1.getPredicate()); - } - public BramblewoodParagon(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); this.subtype.add(SubType.ELF, SubType.WARRIOR); @@ -45,7 +39,9 @@ public final class BramblewoodParagon extends CardImpl { new GainAbilityAllEffect( TrampleAbility.getInstance(), Duration.WhileOnBattlefield, - filter))); + StaticFilters.FILTER_EACH_CONTROLLED_CREATURE_P1P1) + ) + ); } diff --git a/Mage.Sets/src/mage/cards/b/BranchOfBoseiju.java b/Mage.Sets/src/mage/cards/b/BranchOfBoseiju.java new file mode 100644 index 00000000000..7f721738d42 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BranchOfBoseiju.java @@ -0,0 +1,47 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.common.LandsYouControlCount; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.keyword.ReachAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BranchOfBoseiju extends CardImpl { + + public BranchOfBoseiju(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, ""); + + this.subtype.add(SubType.PLANT); + this.power = new MageInt(0); + this.toughness = new MageInt(0); + this.color.setGreen(true); + this.nightCard = true; + + // Reach + this.addAbility(ReachAbility.getInstance()); + + // Branch of Boseiju gets +1/+1 for each land you control. + this.addAbility(new SimpleStaticAbility(new BoostSourceEffect( + LandsYouControlCount.instance, LandsYouControlCount.instance, Duration.WhileOnBattlefield + ).setText("{this} gets +1/+1 for each land you control"))); + } + + private BranchOfBoseiju(final BranchOfBoseiju card) { + super(card); + } + + @Override + public BranchOfBoseiju copy() { + return new BranchOfBoseiju(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BrandedBrawlers.java b/Mage.Sets/src/mage/cards/b/BrandedBrawlers.java index cdc3063fbd0..d7567fc858c 100644 --- a/Mage.Sets/src/mage/cards/b/BrandedBrawlers.java +++ b/Mage.Sets/src/mage/cards/b/BrandedBrawlers.java @@ -31,8 +31,6 @@ public final class BrandedBrawlers extends CardImpl { filter.add(TappedPredicate.UNTAPPED); } - static final private String rule = "{this} can't block if you control an untapped land"; - public BrandedBrawlers(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}"); this.subtype.add(SubType.HUMAN); @@ -64,7 +62,7 @@ class BrandedBrawlersCantBlockEffect extends RestrictionEffect { public BrandedBrawlersCantBlockEffect(FilterPermanent filter) { super(Duration.WhileOnBattlefield); this.filter = filter; - staticText = "{this} can't attack if you control " + filter.getMessage(); + staticText = "{this} can't block if you control " + filter.getMessage(); } public BrandedBrawlersCantBlockEffect(final BrandedBrawlersCantBlockEffect effect) { diff --git a/Mage.Sets/src/mage/cards/b/BrandedHowler.java b/Mage.Sets/src/mage/cards/b/BrandedHowler.java index 3b5fb571a4a..4aac4619373 100644 --- a/Mage.Sets/src/mage/cards/b/BrandedHowler.java +++ b/Mage.Sets/src/mage/cards/b/BrandedHowler.java @@ -2,18 +2,11 @@ package mage.cards.b; import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.WerewolfBackTriggeredAbility; -import mage.abilities.condition.common.TwoOrMoreSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.TransformSourceEffect; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; /** * @@ -30,7 +23,6 @@ public final class BrandedHowler extends CardImpl { // this card is the second face of double-faced card this.nightCard = true; - this.transformable = true; // At the beginning of each upkeep, if a player cast two or more spells last turn, transform Branded Howler. this.addAbility(new WerewolfBackTriggeredAbility()); diff --git a/Mage.Sets/src/mage/cards/b/BrashTaunter.java b/Mage.Sets/src/mage/cards/b/BrashTaunter.java index 874130ea7a0..ca83be0f181 100644 --- a/Mage.Sets/src/mage/cards/b/BrashTaunter.java +++ b/Mage.Sets/src/mage/cards/b/BrashTaunter.java @@ -31,7 +31,7 @@ import java.util.UUID; public final class BrashTaunter extends CardImpl { - private static final FilterPermanent filter = new FilterCreaturePermanent(); + private static final FilterPermanent filter = new FilterCreaturePermanent("another target creature"); static { filter.add(AnotherPredicate.instance); @@ -48,7 +48,7 @@ public final class BrashTaunter extends CardImpl { this.addAbility(IndestructibleAbility.getInstance()); // Whenever Brash Taunter is dealt damage, it deals that much damage to target opponent. - Ability ability = new DealtDamageToSourceTriggeredAbility(new BrashTaunterEffect(), false, false, true); + Ability ability = new DealtDamageToSourceTriggeredAbility(new BrashTaunterEffect(), false, false); ability.addTarget(new TargetOpponent()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/b/BreakingEntering.java b/Mage.Sets/src/mage/cards/b/BreakingEntering.java index 455669b5a7b..6911b38c1d1 100644 --- a/Mage.Sets/src/mage/cards/b/BreakingEntering.java +++ b/Mage.Sets/src/mage/cards/b/BreakingEntering.java @@ -77,7 +77,7 @@ class EnteringReturnFromGraveyardToBattlefieldEffect extends OneShotEffect { if (card != null) { if (controller.moveCards(card, Zone.BATTLEFIELD, source, game)) { ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(card.getId())); + effect.setTargetPointer(new FixedTarget(card.getId(), game)); game.addEffect(effect, source); } } diff --git a/Mage.Sets/src/mage/cards/b/BreakneckRider.java b/Mage.Sets/src/mage/cards/b/BreakneckRider.java index f0936aedf8c..fbc0c3322e3 100644 --- a/Mage.Sets/src/mage/cards/b/BreakneckRider.java +++ b/Mage.Sets/src/mage/cards/b/BreakneckRider.java @@ -21,7 +21,6 @@ public final class BreakneckRider extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(3); - this.transformable = true; this.secondSideCardClazz = mage.cards.n.NeckBreaker.class; // At the beginning of each upkeep, if no spells were cast last turn, transform Breakneck Rider. diff --git a/Mage.Sets/src/mage/cards/b/BreathOfTheSleepless.java b/Mage.Sets/src/mage/cards/b/BreathOfTheSleepless.java new file mode 100644 index 00000000000..2784d29afdf --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BreathOfTheSleepless.java @@ -0,0 +1,58 @@ +package mage.cards.b; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.condition.common.OpponentsTurnCondition; +import mage.abilities.decorator.ConditionalTriggeredAbility; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.effects.common.continuous.CastAsThoughItHadFlashAllEffect; +import mage.abilities.hint.common.OpponentsTurnHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BreathOfTheSleepless extends CardImpl { + + private static final FilterCard filter = new FilterCard("Spirit spells"); + + static { + filter.add(SubType.SPIRIT.getPredicate()); + } + + public BreathOfTheSleepless(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{U}"); + + // You may cast Spirit spells as though they had flash. + this.addAbility(new SimpleStaticAbility( + new CastAsThoughItHadFlashAllEffect(Duration.WhileOnBattlefield, filter) + )); + + // Whenever you cast a creature spell during an opponent's turn, tap up to one target creature. + Ability ability = new ConditionalTriggeredAbility(new SpellCastControllerTriggeredAbility( + new TapTargetEffect(), StaticFilters.FILTER_SPELL_A_CREATURE, false + ), OpponentsTurnCondition.instance, "Whenever you cast a creature spell " + + "during an opponent's turn, tap up to one target creature."); + ability.addTarget(new TargetCreaturePermanent(0, 1)); + this.addAbility(ability.addHint(OpponentsTurnHint.instance)); + } + + private BreathOfTheSleepless(final BreathOfTheSleepless card) { + super(card); + } + + @Override + public BreathOfTheSleepless copy() { + return new BreathOfTheSleepless(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BreathkeeperSeraph.java b/Mage.Sets/src/mage/cards/b/BreathkeeperSeraph.java new file mode 100644 index 00000000000..6742dd27c73 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BreathkeeperSeraph.java @@ -0,0 +1,57 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.common.delayed.AtTheBeginOfNextUpkeepDelayedTriggeredAbility; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect; +import mage.abilities.effects.common.continuous.GainAbilityPairedEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.SoulbondAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BreathkeeperSeraph extends CardImpl { + + public BreathkeeperSeraph(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{W}{W}"); + + this.subtype.add(SubType.ANGEL); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Soulbond + this.addAbility(new SoulbondAbility()); + + // As long as Breathkeeper Seraph is paired with another creature, each of those creatures has "When this creature dies, you may return it to the battlefield under its owner's control at the beginning of your next upkeep." + this.addAbility(new SimpleStaticAbility(new GainAbilityPairedEffect( + new DiesSourceTriggeredAbility( + new CreateDelayedTriggeredAbilityEffect(new AtTheBeginOfNextUpkeepDelayedTriggeredAbility( + new ReturnSourceFromGraveyardToBattlefieldEffect() + )).setText("return it to the battlefield under its owner's control at the beginning of your next upkeep"), true + ).setTriggerPhrase("When this creature dies, "), "As long as {this} is paired with " + + "another creature, each of those creatures has \"When this creature dies, you may return " + + "it to the battlefield under its owner's control at the beginning of your next upkeep.\"" + ))); + } + + private BreathkeeperSeraph(final BreathkeeperSeraph card) { + super(card); + } + + @Override + public BreathkeeperSeraph copy() { + return new BreathkeeperSeraph(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BreechesBrazenPlunderer.java b/Mage.Sets/src/mage/cards/b/BreechesBrazenPlunderer.java index 9738b9f5eb4..57ff7313728 100644 --- a/Mage.Sets/src/mage/cards/b/BreechesBrazenPlunderer.java +++ b/Mage.Sets/src/mage/cards/b/BreechesBrazenPlunderer.java @@ -36,7 +36,7 @@ public final class BreechesBrazenPlunderer extends CardImpl { this.toughness = new MageInt(3); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // Whenever one or more Pirates you control deal damage to your opponents, exile the top card of each of those opponents' libraries. You may play those cards this turn, and you may spend mana as though it were mana of any color to cast those spells. this.addAbility(new BreechesBrazenPlundererTriggeredAbility()); diff --git a/Mage.Sets/src/mage/cards/b/BriarberryCohort.java b/Mage.Sets/src/mage/cards/b/BriarberryCohort.java index 135b78470e5..edbe30cab86 100644 --- a/Mage.Sets/src/mage/cards/b/BriarberryCohort.java +++ b/Mage.Sets/src/mage/cards/b/BriarberryCohort.java @@ -35,7 +35,7 @@ public final class BriarberryCohort extends CardImpl { filter.add(AnotherPredicate.instance); } - private String rule = "{this} gets +1/+1 as long as you control another blue creature"; + private static final String rule = "{this} gets +1/+1 as long as you control another blue creature"; public BriarberryCohort(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}"); diff --git a/Mage.Sets/src/mage/cards/b/BriarknitKami.java b/Mage.Sets/src/mage/cards/b/BriarknitKami.java index 4b73eec21e6..fedf9683a7a 100644 --- a/Mage.Sets/src/mage/cards/b/BriarknitKami.java +++ b/Mage.Sets/src/mage/cards/b/BriarknitKami.java @@ -27,7 +27,7 @@ public final class BriarknitKami extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(3); // Whenever you cast a Spirit or Arcane spell, put a +1/+1 counter on target creature. - Ability ability = new SpellCastControllerTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance()), StaticFilters.SPIRIT_OR_ARCANE_CARD, false); + Ability ability = new SpellCastControllerTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance()), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/b/BridesGown.java b/Mage.Sets/src/mage/cards/b/BridesGown.java new file mode 100644 index 00000000000..f1ff3a5670e --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BridesGown.java @@ -0,0 +1,89 @@ +package mage.cards.b; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.continuous.BoostEquippedEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.abilities.keyword.EquipAbility; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterEquipmentPermanent; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.filter.predicate.mageobject.NamePredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BridesGown extends CardImpl { + + private static final FilterPermanent filter = new FilterEquipmentPermanent(); + + static { + filter.add(new NamePredicate("Groom's Finery")); + filter.add(BridesGownPredicate.instance); + } + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter, false); + private static final Hint hint = new ConditionHint( + condition, "An Equipment named Groom's Finery is attached to a creature you control" + ); + + public BridesGown(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}{W}"); + + this.subtype.add(SubType.EQUIPMENT); + + // Equipped creature gets +2/+0. It gets an additional +0/+2 and has first strike as long as an equipment named Groom's Finery is attached to a creature you control. + Ability ability = new SimpleStaticAbility(new BoostEquippedEffect(2, 0)); + ability.addEffect(new ConditionalContinuousEffect( + new BoostEquippedEffect(0, 2), + condition, "It gets an additional +0/+2" + )); + ability.addEffect(new ConditionalContinuousEffect( + new GainAbilityAttachedEffect( + FirstStrikeAbility.getInstance(), AttachmentType.EQUIPMENT + ), condition, "and has first strike as long as an Equipment " + + "named Groom's Finery is attached to a creature you control" + )); + this.addAbility(ability.addHint(hint)); + + // Equip {2} + this.addAbility(new EquipAbility(2)); + } + + private BridesGown(final BridesGown card) { + super(card); + } + + @Override + public BridesGown copy() { + return new BridesGown(this); + } +} + +enum BridesGownPredicate implements ObjectSourcePlayerPredicate { + instance; + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + Permanent permanent = game.getPermanent(input.getObject().getAttachedTo()); + return permanent != null + && permanent.isCreature(game) + && permanent.isControlledBy(input.getPlayerId()); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BrilliantRestoration.java b/Mage.Sets/src/mage/cards/b/BrilliantRestoration.java new file mode 100644 index 00000000000..3c1da7866b5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BrilliantRestoration.java @@ -0,0 +1,65 @@ +package mage.cards.b; + +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.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.common.FilterArtifactOrEnchantmentCard; +import mage.game.Game; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BrilliantRestoration extends CardImpl { + + public BrilliantRestoration(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{W}{W}{W}{W}"); + + // Return all artifact and enchantment cards from your graveyard to the battlefield. + this.getSpellAbility().addEffect(new BrilliantRestorationEffect()); + } + + private BrilliantRestoration(final BrilliantRestoration card) { + super(card); + } + + @Override + public BrilliantRestoration copy() { + return new BrilliantRestoration(this); + } +} + +class BrilliantRestorationEffect extends OneShotEffect { + + private static final FilterCard filter = new FilterArtifactOrEnchantmentCard(); + + BrilliantRestorationEffect() { + super(Outcome.Benefit); + staticText = "return all artifact and enchantment cards from your graveyard to the battlefield"; + } + + private BrilliantRestorationEffect(final BrilliantRestorationEffect effect) { + super(effect); + } + + @Override + public BrilliantRestorationEffect copy() { + return new BrilliantRestorationEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + return player.moveCards(player.getGraveyard().getCards(filter, game), Zone.BATTLEFIELD, source, game); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BrineComber.java b/Mage.Sets/src/mage/cards/b/BrineComber.java new file mode 100644 index 00000000000..cac6dbda703 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BrineComber.java @@ -0,0 +1,94 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.DisturbAbility; +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.GameEvent; +import mage.game.permanent.token.SpiritWhiteToken; +import mage.game.stack.Spell; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BrineComber extends CardImpl { + + public BrineComber(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{U}"); + + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + this.secondSideCardClazz = mage.cards.b.BrineboundGift.class; + + // Whenever Brine Comber enters the battlefield or becomes the target of an Aura spell, create a 1/1 white Spirit creature token with flying. + this.addAbility(new BrineComberTriggeredAbility()); + + // Disturb {W}{U} + this.addAbility(new DisturbAbility(this, "{W}{U}")); + } + + private BrineComber(final BrineComber card) { + super(card); + } + + @Override + public BrineComber copy() { + return new BrineComber(this); + } +} + +class BrineComberTriggeredAbility extends TriggeredAbilityImpl { + + BrineComberTriggeredAbility() { + super(Zone.ALL, new CreateTokenEffect(new SpiritWhiteToken())); + } + + private BrineComberTriggeredAbility(final BrineComberTriggeredAbility effect) { + super(effect); + } + + @Override + public BrineComberTriggeredAbility copy() { + return new BrineComberTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD + || event.getType() == GameEvent.EventType.TARGETED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + switch (event.getType()) { + case ENTERS_THE_BATTLEFIELD: + return event.getTargetId().equals(getSourceId()); + case TARGETED: + break; + default: + return false; + } + if (this.getSourcePermanentIfItStillExists(game) == null + || !event.getTargetId().equals(getSourceId())) { + return false; + } + Spell spell = game.getSpell(event.getSourceId()); + return spell != null && spell.hasSubtype(SubType.AURA, game); + } + + @Override + public String getRule() { + return "Whenever {this} enters the battlefield or becomes the target " + + "of an Aura spell, create a 1/1 white Spirit creature token with flying."; + } +} diff --git a/Mage.Sets/src/mage/cards/b/BrinebornCutthroat.java b/Mage.Sets/src/mage/cards/b/BrinebornCutthroat.java index 9324dd18897..cf9775b8397 100644 --- a/Mage.Sets/src/mage/cards/b/BrinebornCutthroat.java +++ b/Mage.Sets/src/mage/cards/b/BrinebornCutthroat.java @@ -2,10 +2,10 @@ package mage.cards.b; import mage.MageInt; import mage.abilities.common.SpellCastControllerTriggeredAbility; -import mage.abilities.condition.common.NotMyTurnCondition; +import mage.abilities.condition.common.OpponentsTurnCondition; import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.common.counter.AddCountersSourceEffect; -import mage.abilities.hint.common.NotMyTurnHint; +import mage.abilities.hint.common.OpponentsTurnHint; import mage.abilities.keyword.FlashAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -35,9 +35,9 @@ public final class BrinebornCutthroat extends CardImpl { this.addAbility(new ConditionalTriggeredAbility( new SpellCastControllerTriggeredAbility( new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false - ), NotMyTurnCondition.instance, "Whenever you cast a spell during an opponent's turn, " + + ), OpponentsTurnCondition.instance, "Whenever you cast a spell during an opponent's turn, " + "put a +1/+1 counter on {this}." - ).addHint(NotMyTurnHint.instance)); // TODO: replace to opponent's turn condition (team mode support in future) + ).addHint(OpponentsTurnHint.instance)); } private BrinebornCutthroat(final BrinebornCutthroat card) { diff --git a/Mage.Sets/src/mage/cards/b/BrineboundGift.java b/Mage.Sets/src/mage/cards/b/BrineboundGift.java new file mode 100644 index 00000000000..17e94aa84d9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BrineboundGift.java @@ -0,0 +1,107 @@ +package mage.cards.b; + +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.PutIntoGraveFromAnywhereSourceAbility; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.ExileSourceEffect; +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.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.SpiritWhiteToken; +import mage.game.stack.Spell; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BrineboundGift extends CardImpl { + + public BrineboundGift(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, ""); + + this.subtype.add(SubType.AURA); + this.color.setWhite(true); + this.color.setBlue(true); + this.nightCard = true; + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // Whenever Brinebound Gift enters the battlefield or enchanted creature becomes the target of an Aura spell, create a 1/1 white Spirit creature token with flying. + this.addAbility(new BrineboundGiftTriggeredAbility()); + + // If Brinebound Gift would be put into a graveyard from anywhere, exile it instead. + this.addAbility(new PutIntoGraveFromAnywhereSourceAbility(new ExileSourceEffect().setText("exile it instead"))); + } + + private BrineboundGift(final BrineboundGift card) { + super(card); + } + + @Override + public BrineboundGift copy() { + return new BrineboundGift(this); + } +} + +class BrineboundGiftTriggeredAbility extends TriggeredAbilityImpl { + + BrineboundGiftTriggeredAbility() { + super(Zone.ALL, new CreateTokenEffect(new SpiritWhiteToken())); + } + + private BrineboundGiftTriggeredAbility(final BrineboundGiftTriggeredAbility effect) { + super(effect); + } + + @Override + public BrineboundGiftTriggeredAbility copy() { + return new BrineboundGiftTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD + || event.getType() == GameEvent.EventType.TARGETED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + switch (event.getType()) { + case ENTERS_THE_BATTLEFIELD: + return event.getTargetId().equals(getSourceId()); + case TARGETED: + break; + default: + return false; + } + Permanent permanent = this.getSourcePermanentOrLKI(game); + if (permanent == null || !event.getTargetId().equals(permanent.getAttachedTo())) { + return false; + } + Spell spell = game.getSpell(event.getSourceId()); + return spell != null && spell.hasSubtype(SubType.AURA, game); + } + + @Override + public String getRule() { + return "Whenever {this} enters the battlefield or enchanted creature becomes the target " + + "of an Aura spell, create a 1/1 white Spirit creature token with flying."; + } +} diff --git a/Mage.Sets/src/mage/cards/b/BrokersAscendancy.java b/Mage.Sets/src/mage/cards/b/BrokersAscendancy.java new file mode 100644 index 00000000000..f571453c436 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BrokersAscendancy.java @@ -0,0 +1,43 @@ +package mage.cards.b; + +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.effects.common.counter.AddCountersAllEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.counters.CounterType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BrokersAscendancy extends CardImpl { + + public BrokersAscendancy(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{G}{U}{W}"); + + // At the beginning of your end step, put a +1/+1 counter on each creature you control and a loyalty counter on each planeswalker you control. + Ability ability = new BeginningOfEndStepTriggeredAbility(new AddCountersAllEffect( + CounterType.P1P1.createInstance(), + StaticFilters.FILTER_CONTROLLED_CREATURE + ), TargetController.YOU, false); + ability.addEffect(new AddCountersAllEffect( + CounterType.LOYALTY.createInstance(), + StaticFilters.FILTER_CONTROLLED_PERMANENT_PLANESWALKER + ).setText("and a loyalty counter on each planeswalker you control")); + this.addAbility(ability); + } + + private BrokersAscendancy(final BrokersAscendancy card) { + super(card); + } + + @Override + public BrokersAscendancy copy() { + return new BrokersAscendancy(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BronzeCudgels.java b/Mage.Sets/src/mage/cards/b/BronzeCudgels.java new file mode 100644 index 00000000000..c095a827a9d --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BronzeCudgels.java @@ -0,0 +1,82 @@ +package mage.cards.b; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.hint.common.AbilityResolutionCountHint; +import mage.abilities.keyword.EquipAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.targetpointer.FixedTarget; +import mage.watchers.common.AbilityResolvedWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BronzeCudgels extends CardImpl { + + public BronzeCudgels(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}"); + + this.subtype.add(SubType.EQUIPMENT); + + // {2}: Equipped creature gets +X/+0 until end of turn, where X is the number of times this ability has resolved this turn. + this.addAbility(new SimpleActivatedAbility( + new BronzeCudgelsEffect(), new GenericManaCost(2) + ).addHint(AbilityResolutionCountHint.instance), new AbilityResolvedWatcher()); + + // Equip {1} + this.addAbility(new EquipAbility(1)); + } + + private BronzeCudgels(final BronzeCudgels card) { + super(card); + } + + @Override + public BronzeCudgels copy() { + return new BronzeCudgels(this); + } +} + +class BronzeCudgelsEffect extends OneShotEffect { + + BronzeCudgelsEffect() { + super(Outcome.Benefit); + staticText = "until end of turn, equipped creature gets +X/+0, " + + "where X is the number of times this ability has resolved this turn."; + } + + private BronzeCudgelsEffect(final BronzeCudgelsEffect effect) { + super(effect); + } + + @Override + public BronzeCudgelsEffect copy() { + return new BronzeCudgelsEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = source.getSourcePermanentOrLKI(game); + if (permanent == null || game.getPermanent(permanent.getAttachedTo()) == null) { + return false; + } + int resolvedCount = AbilityResolvedWatcher.getResolutionCount(game, source); + if (resolvedCount < 1) { + return false; + } + game.addEffect(new BoostTargetEffect(resolvedCount, 0) + .setTargetPointer(new FixedTarget(permanent.getOwnerId(), game)), source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/b/BronzeplateBoar.java b/Mage.Sets/src/mage/cards/b/BronzeplateBoar.java new file mode 100644 index 00000000000..8895f8b47f7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BronzeplateBoar.java @@ -0,0 +1,53 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.continuous.BoostEquippedEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.ReconfigureAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BronzeplateBoar extends CardImpl { + + public BronzeplateBoar(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{2}{R}"); + + this.subtype.add(SubType.EQUIPMENT); + this.subtype.add(SubType.BOAR); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Equipped creature gets +3/+2 and has trample. + Ability ability = new SimpleStaticAbility(new BoostEquippedEffect(3, 2)); + ability.addEffect(new GainAbilityAttachedEffect( + TrampleAbility.getInstance(), AttachmentType.EQUIPMENT + ).setText("and has trample")); + this.addAbility(ability); + + // Reconfigure {5} + this.addAbility(new ReconfigureAbility("{5}")); + } + + private BronzeplateBoar(final BronzeplateBoar card) { + super(card); + } + + @Override + public BronzeplateBoar copy() { + return new BronzeplateBoar(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BroodhatchNantuko.java b/Mage.Sets/src/mage/cards/b/BroodhatchNantuko.java index 331da929d55..81ce22ee1c6 100644 --- a/Mage.Sets/src/mage/cards/b/BroodhatchNantuko.java +++ b/Mage.Sets/src/mage/cards/b/BroodhatchNantuko.java @@ -31,7 +31,7 @@ public final class BroodhatchNantuko extends CardImpl { this.toughness = new MageInt(1); // Whenever Broodhatch Nantuko is dealt damage, you may create that many 1/1 green Insect creature tokens. - this.addAbility(new DealtDamageToSourceTriggeredAbility(new BroodhatchNantukoDealDamageEffect(), true, false, true)); + this.addAbility(new DealtDamageToSourceTriggeredAbility(new BroodhatchNantukoDealDamageEffect(), true, false)); // Morph {2}{G} this.addAbility(new MorphAbility(this, new ManaCostsImpl("{2}{G}"))); } diff --git a/Mage.Sets/src/mage/cards/b/BroodingSaurian.java b/Mage.Sets/src/mage/cards/b/BroodingSaurian.java index 65ac3b858b4..aa7ecee1468 100644 --- a/Mage.Sets/src/mage/cards/b/BroodingSaurian.java +++ b/Mage.Sets/src/mage/cards/b/BroodingSaurian.java @@ -1,28 +1,29 @@ package mage.cards.b; -import java.util.Iterator; -import java.util.UUID; import mage.MageInt; -import mage.MageObjectReference; -import mage.abilities.Ability; import mage.abilities.common.BeginningOfEndStepTriggeredAbility; -import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.common.continuous.GainControlAllOwnedEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; import mage.filter.FilterPermanent; -import mage.filter.predicate.card.OwnerIdPredicate; import mage.filter.predicate.permanent.TokenPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class BroodingSaurian extends CardImpl { + private static final FilterPermanent filter = new FilterPermanent("nontoken permanents"); + + static { + filter.add(TokenPredicate.FALSE); + } + public BroodingSaurian(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{G}"); this.subtype.add(SubType.LIZARD); @@ -31,7 +32,9 @@ public final class BroodingSaurian extends CardImpl { this.toughness = new MageInt(4); // At the beginning of each end step, each player gains control of all nontoken permanents they own. - this.addAbility(new BeginningOfEndStepTriggeredAbility(new BroodingSaurianControlEffect(), TargetController.ANY, false)); + this.addAbility(new BeginningOfEndStepTriggeredAbility( + new GainControlAllOwnedEffect(filter), TargetController.ANY, false + )); } private BroodingSaurian(final BroodingSaurian card) { @@ -43,60 +46,3 @@ public final class BroodingSaurian extends CardImpl { return new BroodingSaurian(this); } } - -class BroodingSaurianControlEffect extends ContinuousEffectImpl { - - private static final FilterPermanent filter = new FilterPermanent(); - - static { - filter.add(TokenPredicate.FALSE); - } - - public BroodingSaurianControlEffect() { - super(Duration.EndOfGame, Layer.ControlChangingEffects_2, SubLayer.NA, Outcome.GainControl); - this.staticText = "each player gains control of all nontoken permanents they own"; - } - - public BroodingSaurianControlEffect(final BroodingSaurianControlEffect effect) { - super(effect); - } - - @Override - public BroodingSaurianControlEffect copy() { - return new BroodingSaurianControlEffect(this); - } - - @Override - public void init(Ability source, Game game) { - super.init(source, game); - // add all creatures in range - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - FilterPermanent playerFilter = filter.copy(); - playerFilter.add(new OwnerIdPredicate(playerId)); - for (Permanent permanent : game.getBattlefield().getActivePermanents(playerFilter, playerId, game)) { - affectedObjectList.add(new MageObjectReference(permanent, game)); - } - } - } - } - - @Override - public boolean apply(Game game, Ability source) { - for (Iterator it = affectedObjectList.iterator(); it.hasNext();) { - Permanent creature = it.next().getPermanent(game); - if (creature != null) { - if (!creature.isControlledBy(creature.getOwnerId())) { - creature.changeControllerId(creature.getOwnerId(), game, source); - } - } else { - it.remove(); - } - } - if (affectedObjectList.isEmpty()) { - this.discard(); - } - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/b/BrownOuphe.java b/Mage.Sets/src/mage/cards/b/BrownOuphe.java index 3fa7fdc17ef..b3f9d7ff462 100644 --- a/Mage.Sets/src/mage/cards/b/BrownOuphe.java +++ b/Mage.Sets/src/mage/cards/b/BrownOuphe.java @@ -26,7 +26,7 @@ public final class BrownOuphe extends CardImpl { private static final FilterStackObject filter = new FilterStackObject("activated ability from an artifact source"); static { - filter.add(new ArtifactSourcePredicate()); + filter.add(ArtifactSourcePredicate.instance); } public BrownOuphe(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/b/BrutalCathar.java b/Mage.Sets/src/mage/cards/b/BrutalCathar.java index 902e0277d02..66d67e83707 100644 --- a/Mage.Sets/src/mage/cards/b/BrutalCathar.java +++ b/Mage.Sets/src/mage/cards/b/BrutalCathar.java @@ -1,20 +1,16 @@ package mage.cards.b; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.Ability; +import mage.abilities.common.TransformsOrEntersTriggeredAbility; import mage.abilities.common.delayed.OnLeaveReturnExiledToBattlefieldAbility; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; import mage.abilities.effects.common.ExileUntilSourceLeavesEffect; import mage.abilities.keyword.DayboundAbility; -import mage.abilities.keyword.TransformAbility; 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.GameEvent; -import mage.game.permanent.Permanent; import mage.target.common.TargetOpponentsCreaturePermanent; import java.util.UUID; @@ -32,14 +28,17 @@ public final class BrutalCathar extends CardImpl { this.subtype.add(SubType.WEREWOLF); this.power = new MageInt(2); this.toughness = new MageInt(2); - this.transformable = true; this.secondSideCardClazz = mage.cards.m.MoonrageBrute.class; // When this creature enters the battlefield or transforms into Brutal Cathar, exile target creature an opponent controls until this creature leaves the battlefield. - this.addAbility(new BrutalCatharTriggeredAbility()); + Ability ability = new TransformsOrEntersTriggeredAbility( + new ExileUntilSourceLeavesEffect("creature an opponent controls"), false + ).setTriggerPhrase("Whenever this creature enters the battlefield or transforms into {this}, "); + ability.addTarget(new TargetOpponentsCreaturePermanent()); + ability.addEffect(new CreateDelayedTriggeredAbilityEffect(new OnLeaveReturnExiledToBattlefieldAbility())); + this.addAbility(ability); // Daybound - this.addAbility(new TransformAbility()); this.addAbility(new DayboundAbility()); } @@ -52,48 +51,3 @@ public final class BrutalCathar extends CardImpl { return new BrutalCathar(this); } } - -class BrutalCatharTriggeredAbility extends TriggeredAbilityImpl { - - public BrutalCatharTriggeredAbility() { - super(Zone.BATTLEFIELD, new ExileUntilSourceLeavesEffect("creature an opponent controls"), false); - this.addTarget(new TargetOpponentsCreaturePermanent()); - this.addEffect(new CreateDelayedTriggeredAbilityEffect(new OnLeaveReturnExiledToBattlefieldAbility())); - } - - public BrutalCatharTriggeredAbility(final BrutalCatharTriggeredAbility ability) { - super(ability); - } - - @Override - public BrutalCatharTriggeredAbility copy() { - return new BrutalCatharTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TRANSFORMED - || event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (!event.getTargetId().equals(this.getSourceId())) { - return false; - } - switch (event.getType()) { - case TRANSFORMED: - Permanent permanent = getSourcePermanentIfItStillExists(game); - return permanent != null && !permanent.isTransformed(); - case ENTERS_THE_BATTLEFIELD: - return true; - } - return false; - } - - @Override - public String getRule() { - return "When this creature enters the battlefield or transforms into {this}, " + - "exile target creature an opponent controls until this creature leaves the battlefield."; - } -} diff --git a/Mage.Sets/src/mage/cards/b/BrutalHordechief.java b/Mage.Sets/src/mage/cards/b/BrutalHordechief.java index 2cf83cfb40b..8b0eb470a61 100644 --- a/Mage.Sets/src/mage/cards/b/BrutalHordechief.java +++ b/Mage.Sets/src/mage/cards/b/BrutalHordechief.java @@ -1,17 +1,14 @@ - package mage.cards.b; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.LoseLifeTargetEffect; import mage.abilities.effects.common.combat.BlocksIfAbleAllEffect; +import mage.abilities.effects.common.combat.ChooseBlockersEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -19,12 +16,12 @@ import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.targetpointer.FixedTarget; -import mage.watchers.common.ChooseBlockersRedundancyWatcher; +import mage.watchers.common.ControlCombatRedundancyWatcher; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class BrutalHordechief extends CardImpl { @@ -36,7 +33,7 @@ public final class BrutalHordechief extends CardImpl { } public BrutalHordechief(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); this.subtype.add(SubType.ORC, SubType.WARRIOR); this.power = new MageInt(3); this.toughness = new MageInt(3); @@ -45,10 +42,11 @@ public final class BrutalHordechief extends CardImpl { this.addAbility(new BrutalHordechiefTriggeredAbility()); // {3}{R/W}{R/W}: Creatures your opponents control block this turn if able, and you choose how those creatures block. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BlocksIfAbleAllEffect(filter, Duration.EndOfTurn), new ManaCostsImpl("{3}{R/W}{R/W}")); - ability.addEffect(new BrutalHordechiefChooseBlockersEffect()); - ability.addWatcher(new ChooseBlockersRedundancyWatcher()); - ability.addEffect(new ChooseBlockersRedundancyWatcherIncrementEffect()); + Ability ability = new SimpleActivatedAbility( + new BlocksIfAbleAllEffect(filter, Duration.EndOfTurn), new ManaCostsImpl<>("{3}{R/W}{R/W}") + ); + ability.addEffect(new ChooseBlockersEffect(Duration.EndOfTurn).setText("and you choose how those creatures block")); + ability.addWatcher(new ControlCombatRedundancyWatcher()); this.addAbility(ability); } @@ -60,32 +58,6 @@ public final class BrutalHordechief extends CardImpl { public BrutalHordechief copy() { return new BrutalHordechief(this); } - - private class ChooseBlockersRedundancyWatcherIncrementEffect extends OneShotEffect { - - ChooseBlockersRedundancyWatcherIncrementEffect() { - super(Outcome.Neutral); - } - - ChooseBlockersRedundancyWatcherIncrementEffect(final ChooseBlockersRedundancyWatcherIncrementEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - ChooseBlockersRedundancyWatcher watcher = game.getState().getWatcher(ChooseBlockersRedundancyWatcher.class); - if (watcher != null) { - watcher.increment(); - return true; - } - return false; - } - - @Override - public ChooseBlockersRedundancyWatcherIncrementEffect copy() { - return new ChooseBlockersRedundancyWatcherIncrementEffect(this); - } - } } class BrutalHordechiefTriggeredAbility extends TriggeredAbilityImpl { @@ -112,9 +84,9 @@ class BrutalHordechiefTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { Permanent source = game.getPermanent(event.getSourceId()); - if (source != null && source.isControlledBy(controllerId)) { + if (source != null && source.isControlledBy(getControllerId())) { UUID defendingPlayerId = game.getCombat().getDefendingPlayerId(event.getSourceId(), game); - this.getEffects().get(0).setTargetPointer(new FixedTarget(defendingPlayerId)); + this.getEffects().setTargetPointer(new FixedTarget(defendingPlayerId)); return true; } return false; @@ -125,50 +97,3 @@ class BrutalHordechiefTriggeredAbility extends TriggeredAbilityImpl { return "Whenever a creature you control attacks, defending player loses 1 life and you gain 1 life."; } } - -class BrutalHordechiefChooseBlockersEffect extends ContinuousRuleModifyingEffectImpl { - - public BrutalHordechiefChooseBlockersEffect() { - super(Duration.EndOfTurn, Outcome.Benefit, false, false); - staticText = "You choose which creatures block this turn and how those creatures block"; - } - - public BrutalHordechiefChooseBlockersEffect(final BrutalHordechiefChooseBlockersEffect effect) { - super(effect); - } - - @Override - public BrutalHordechiefChooseBlockersEffect copy() { - return new BrutalHordechiefChooseBlockersEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - return false; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DECLARING_BLOCKERS; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - ChooseBlockersRedundancyWatcher watcher = game.getState().getWatcher(ChooseBlockersRedundancyWatcher.class); - if(watcher == null){ - return false; - } - watcher.decrement(); - if (watcher.copyCountApply > 0) { - game.informPlayers(source.getSourceObject(game).getIdName() + " didn't apply"); - return false; - } - watcher.copyCountApply = watcher.copyCount; - Player blockController = game.getPlayer(source.getControllerId()); - if (blockController != null) { - game.getCombat().selectBlockers(blockController, source, game); - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/b/BruteSuit.java b/Mage.Sets/src/mage/cards/b/BruteSuit.java new file mode 100644 index 00000000000..4ef9c8ec4ed --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BruteSuit.java @@ -0,0 +1,40 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.keyword.CrewAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BruteSuit extends CardImpl { + + public BruteSuit(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); + + this.subtype.add(SubType.VEHICLE); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // Crew 1 + this.addAbility(new CrewAbility(1)); + } + + private BruteSuit(final BruteSuit card) { + super(card); + } + + @Override + public BruteSuit copy() { + return new BruteSuit(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BubblingBeebles.java b/Mage.Sets/src/mage/cards/b/BubblingBeebles.java index 0395618541d..5767cfcf413 100644 --- a/Mage.Sets/src/mage/cards/b/BubblingBeebles.java +++ b/Mage.Sets/src/mage/cards/b/BubblingBeebles.java @@ -32,7 +32,7 @@ public final class BubblingBeebles extends CardImpl { // Bubbling Beebles can't be blocked as long as defending player controls an enchantment. Effect effect = new ConditionalRestrictionEffect( new CantBeBlockedSourceEffect(), - new DefendingPlayerControlsCondition(StaticFilters.FILTER_ENCHANTMENT_PERMANENT)); + new DefendingPlayerControlsCondition(StaticFilters.FILTER_PERMANENT_ENCHANTMENT)); effect.setText("{this} can't be blocked as long as defending player controls an enchantment"); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); } diff --git a/Mage.Sets/src/mage/cards/b/BudokaPupil.java b/Mage.Sets/src/mage/cards/b/BudokaPupil.java index 7f193a31944..127ef96c2f7 100644 --- a/Mage.Sets/src/mage/cards/b/BudokaPupil.java +++ b/Mage.Sets/src/mage/cards/b/BudokaPupil.java @@ -40,7 +40,7 @@ public final class BudokaPupil extends CardImpl { this.flipCardName = "Ichiga, Who Topples Oaks"; // Whenever you cast a Spirit or Arcane spell, you may put a ki counter on Budoka Pupil. - this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.KI.createInstance()), StaticFilters.SPIRIT_OR_ARCANE_CARD, true)); + this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.KI.createInstance()), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true)); // At the beginning of the end step, if there are two or more ki counters on Budoka Pupil, you may flip it. this.addAbility(new ConditionalInterveningIfTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/b/BuiltToLast.java b/Mage.Sets/src/mage/cards/b/BuiltToLast.java index 470831846d1..40ed0f96d73 100644 --- a/Mage.Sets/src/mage/cards/b/BuiltToLast.java +++ b/Mage.Sets/src/mage/cards/b/BuiltToLast.java @@ -28,7 +28,7 @@ public final class BuiltToLast extends CardImpl { this.getSpellAbility().addTarget(new TargetCreaturePermanent()); this.getSpellAbility().addEffect(new ConditionalContinuousEffect( new GainAbilityTargetEffect(IndestructibleAbility.getInstance(), Duration.EndOfTurn), new LockedInCondition(new TargetHasCardTypeCondition(CardType.ARTIFACT)), - "If its an artifact creature, it gains indestructible until end of turn")); + "If it's an artifact creature, it gains indestructible until end of turn")); } diff --git a/Mage.Sets/src/mage/cards/b/BuiltToSmash.java b/Mage.Sets/src/mage/cards/b/BuiltToSmash.java index 90664118ba8..2ed5b937989 100644 --- a/Mage.Sets/src/mage/cards/b/BuiltToSmash.java +++ b/Mage.Sets/src/mage/cards/b/BuiltToSmash.java @@ -28,7 +28,7 @@ public final class BuiltToSmash extends CardImpl { this.getSpellAbility().addTarget(new TargetAttackingCreature()); this.getSpellAbility().addEffect(new ConditionalContinuousEffect( new GainAbilityTargetEffect(TrampleAbility.getInstance(), Duration.EndOfTurn), new LockedInCondition(new TargetHasCardTypeCondition(CardType.ARTIFACT)), - "If its an artifact creature, it gains trample until end of turn")); + "If it's an artifact creature, it gains trample until end of turn")); } private BuiltToSmash(final BuiltToSmash card) { diff --git a/Mage.Sets/src/mage/cards/b/BullAurochs.java b/Mage.Sets/src/mage/cards/b/BullAurochs.java index 24392488007..4b54f87a1d7 100644 --- a/Mage.Sets/src/mage/cards/b/BullAurochs.java +++ b/Mage.Sets/src/mage/cards/b/BullAurochs.java @@ -1,9 +1,9 @@ - package mage.cards.b; 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; @@ -29,6 +29,8 @@ public final class BullAurochs extends CardImpl { filter.add(AnotherPredicate.instance); } + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter); + public BullAurochs(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{G}"); this.subtype.add(SubType.AUROCHS); @@ -37,9 +39,9 @@ public final class BullAurochs extends CardImpl { // Trample this.addAbility(TrampleAbility.getInstance()); + // Whenever Bull Aurochs attacks, it gets +1/+0 until end of turn for each other attacking Aurochs. - PermanentsOnBattlefieldCount value = new PermanentsOnBattlefieldCount(filter, 1); - this.addAbility(new AttacksTriggeredAbility(new BoostSourceEffect(value, StaticValue.get(0), Duration.EndOfTurn, true), false)); + this.addAbility(new AttacksTriggeredAbility(new BoostSourceEffect(xValue, StaticValue.get(0), Duration.EndOfTurn, true, "it"), false)); } private BullAurochs(final BullAurochs card) { diff --git a/Mage.Sets/src/mage/cards/b/BurlyBreaker.java b/Mage.Sets/src/mage/cards/b/BurlyBreaker.java index 4f327728240..da7065a2329 100644 --- a/Mage.Sets/src/mage/cards/b/BurlyBreaker.java +++ b/Mage.Sets/src/mage/cards/b/BurlyBreaker.java @@ -3,7 +3,6 @@ package mage.cards.b; import mage.MageInt; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.keyword.DayboundAbility; -import mage.abilities.keyword.TransformAbility; import mage.abilities.keyword.WardAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -24,14 +23,12 @@ public final class BurlyBreaker extends CardImpl { this.subtype.add(SubType.WEREWOLF); this.power = new MageInt(6); this.toughness = new MageInt(5); - this.transformable = true; this.secondSideCardClazz = mage.cards.d.DireStrainDemolisher.class; // Ward {1} this.addAbility(new WardAbility(new ManaCostsImpl<>("{1}"))); // Daybound - this.addAbility(new TransformAbility()); this.addAbility(new DayboundAbility()); } diff --git a/Mage.Sets/src/mage/cards/b/BurnAway.java b/Mage.Sets/src/mage/cards/b/BurnAway.java index b29f7a92799..09c29d803c6 100644 --- a/Mage.Sets/src/mage/cards/b/BurnAway.java +++ b/Mage.Sets/src/mage/cards/b/BurnAway.java @@ -78,6 +78,6 @@ class BurnAwayDelayedTriggeredAbility extends DelayedTriggeredAbility { @Override public String getRule() { - return "When that creature dies this turn, exile all cards from its controller's graveyard."; + return "When that creature dies this turn, exile its controller's graveyard."; } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/b/BurnFromWithin.java b/Mage.Sets/src/mage/cards/b/BurnFromWithin.java index 9c344c747ae..1a940c15833 100644 --- a/Mage.Sets/src/mage/cards/b/BurnFromWithin.java +++ b/Mage.Sets/src/mage/cards/b/BurnFromWithin.java @@ -72,7 +72,7 @@ class BurnFromWithinEffect extends OneShotEffect { int damageDealt = creature.damage(amount, source.getSourceId(), source, game, false, true); if (damageDealt > 0) { ContinuousEffect effect = new LoseAbilityTargetEffect(IndestructibleAbility.getInstance(), Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(creature.getId())); + effect.setTargetPointer(new FixedTarget(creature.getId(), game)); game.addEffect(effect, source); } return true; diff --git a/Mage.Sets/src/mage/cards/b/BurnTrail.java b/Mage.Sets/src/mage/cards/b/BurnTrail.java index f2620442ca4..9c070b730ae 100644 --- a/Mage.Sets/src/mage/cards/b/BurnTrail.java +++ b/Mage.Sets/src/mage/cards/b/BurnTrail.java @@ -23,7 +23,7 @@ public final class BurnTrail extends CardImpl { this.getSpellAbility().addTarget(new TargetAnyTarget()); // Conspire - this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.ONE)); + this.addAbility(new ConspireAbility(ConspireAbility.ConspireTargets.ONE)); } private BurnTrail(final BurnTrail card) { diff --git a/Mage.Sets/src/mage/cards/b/BurningCinderFuryOfCrimsonChaosFire.java b/Mage.Sets/src/mage/cards/b/BurningCinderFuryOfCrimsonChaosFire.java index b58eff8c56f..c25450038a9 100644 --- a/Mage.Sets/src/mage/cards/b/BurningCinderFuryOfCrimsonChaosFire.java +++ b/Mage.Sets/src/mage/cards/b/BurningCinderFuryOfCrimsonChaosFire.java @@ -86,7 +86,7 @@ class BurningCinderFuryOfCrimsonChaosFireAbility extends TriggeredAbilityImpl { @Override public String getTriggerPhrase() { - return "Whenever any player taps a permanent, " ; + return "Whenever any player taps a permanent, "; } } @@ -133,7 +133,7 @@ class BurningCinderFuryOfCrimsonChaosFireEffect extends OneShotEffect { if (chosenOpponent != null) { game.informPlayers(tappingPlayer.getLogName() + " chose " + chosenOpponent.getLogName() + " to gain control of " + permanentToControl.getLogName() + " at the beginning of the next end step"); ContinuousEffect effect = new BurningCinderFuryOfCrimsonChaosFireCreatureGainControlEffect(Duration.Custom, chosenOpponent.getId()); - effect.setTargetPointer(new FixedTarget(permanentToControl.getId())); + effect.setTargetPointer(new FixedTarget(permanentToControl.getId(), game)); game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect), source); return true; } @@ -200,7 +200,6 @@ class BurningCinderFuryOfCrimsonChaosFireWatcher extends Watcher { super(WatcherScope.GAME); } - @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.TAPPED) { diff --git a/Mage.Sets/src/mage/cards/b/BurningProphet.java b/Mage.Sets/src/mage/cards/b/BurningProphet.java index 1fd95238a17..6d7d53e2263 100644 --- a/Mage.Sets/src/mage/cards/b/BurningProphet.java +++ b/Mage.Sets/src/mage/cards/b/BurningProphet.java @@ -33,7 +33,7 @@ public final class BurningProphet extends CardImpl { ).setText("{this} gets +1/+0 until end of turn, then"), StaticFilters.FILTER_SPELL_A_NON_CREATURE, false ); - ability.addEffect(new ScryEffect(1)); + ability.addEffect(new ScryEffect(1, false)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/b/BurningWish.java b/Mage.Sets/src/mage/cards/b/BurningWish.java index 39f191083a5..e94879b4444 100644 --- a/Mage.Sets/src/mage/cards/b/BurningWish.java +++ b/Mage.Sets/src/mage/cards/b/BurningWish.java @@ -16,7 +16,7 @@ import mage.filter.FilterCard; */ public final class BurningWish extends CardImpl { - private static final FilterCard filter = new FilterCard("a sorcery card"); + private static final FilterCard filter = new FilterCard("sorcery card"); static { filter.add(CardType.SORCERY.getPredicate()); @@ -25,7 +25,7 @@ public final class BurningWish extends CardImpl { public BurningWish(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{R}"); - // You may choose a sorcery card you own from outside the game, reveal that card, and put it into your hand. + // You may reveal a sorcery card you own from outside the game and put it into your hand. this.getSpellAbility().addEffect(new WishEffect(filter)); this.getSpellAbility().addHint(OpenSideboardHint.instance); diff --git a/Mage.Sets/src/mage/cards/b/BurstOfStrength.java b/Mage.Sets/src/mage/cards/b/BurstOfStrength.java index 8ecad93fbab..8fd4b2c641a 100644 --- a/Mage.Sets/src/mage/cards/b/BurstOfStrength.java +++ b/Mage.Sets/src/mage/cards/b/BurstOfStrength.java @@ -22,7 +22,7 @@ public final class BurstOfStrength extends CardImpl { // Put a +1/+1 counter on target creature and untap it. this.getSpellAbility().addEffect(new AddCountersTargetEffect(CounterType.P1P1.createInstance(1))); - this.getSpellAbility().addEffect(new UntapTargetEffect()); + this.getSpellAbility().addEffect(new UntapTargetEffect().setText("and untap it")); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } diff --git a/Mage.Sets/src/mage/cards/b/BushmeatPoacher.java b/Mage.Sets/src/mage/cards/b/BushmeatPoacher.java index 7be8a1fb834..5219f808060 100644 --- a/Mage.Sets/src/mage/cards/b/BushmeatPoacher.java +++ b/Mage.Sets/src/mage/cards/b/BushmeatPoacher.java @@ -12,9 +12,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; -import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -27,12 +25,6 @@ import java.util.UUID; */ public final class BushmeatPoacher extends CardImpl { - private static final FilterControlledPermanent filter = new FilterControlledCreaturePermanent("another creature"); - - static { - filter.add(AnotherPredicate.instance); - } - public BushmeatPoacher(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); @@ -44,7 +36,7 @@ public final class BushmeatPoacher extends CardImpl { // {1}, {T}, Sacrifice another creature: You gain life equal to that creature's toughness. Draw a card. Ability ability = new SimpleActivatedAbility(new BushmeatPoacherEffect(), new GenericManaCost(1)); ability.addCost(new TapSourceCost()); - ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(filter))); + ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE))); this.addAbility(ability); } @@ -62,7 +54,7 @@ class BushmeatPoacherEffect extends OneShotEffect { BushmeatPoacherEffect() { super(Outcome.Benefit); - staticText = "you gain life equal to that creature's toughness. Draw a card"; + staticText = "you gain life equal to the sacrificed creature's toughness. Draw a card"; } private BushmeatPoacherEffect(final BushmeatPoacherEffect effect) { diff --git a/Mage.Sets/src/mage/cards/b/ByInvitationOnly.java b/Mage.Sets/src/mage/cards/b/ByInvitationOnly.java new file mode 100644 index 00000000000..92c6d1b6916 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/ByInvitationOnly.java @@ -0,0 +1,67 @@ +package mage.cards.b; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.SacrificeAllEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ByInvitationOnly extends CardImpl { + + public ByInvitationOnly(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{W}{W}"); + + // Choose a number between 0 and 13. Each player sacrifices that many creatures. + this.getSpellAbility().addEffect(new ByInvitationOnlyEffect()); + } + + private ByInvitationOnly(final ByInvitationOnly card) { + super(card); + } + + @Override + public ByInvitationOnly copy() { + return new ByInvitationOnly(this); + } +} + +class ByInvitationOnlyEffect extends OneShotEffect { + + ByInvitationOnlyEffect() { + super(Outcome.Benefit); + staticText = "choose a number between 0 and 13. Each player sacrifices that many creatures"; + } + + private ByInvitationOnlyEffect(final ByInvitationOnlyEffect effect) { + super(effect); + } + + @Override + public ByInvitationOnlyEffect copy() { + return new ByInvitationOnlyEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + int number = player.getAmount( + 0, 13, "Choose a number between 0 and 13", game + ); + return new SacrificeAllEffect( + number, StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT + ).apply(game, source); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CabalInquisitor.java b/Mage.Sets/src/mage/cards/c/CabalInquisitor.java index 0281d5cfcd1..92beb901840 100644 --- a/Mage.Sets/src/mage/cards/c/CabalInquisitor.java +++ b/Mage.Sets/src/mage/cards/c/CabalInquisitor.java @@ -17,7 +17,7 @@ import mage.abilities.effects.common.discard.DiscardTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.game.Game; import mage.target.TargetPlayer; import mage.target.common.TargetCardInYourGraveyard; @@ -40,7 +40,7 @@ public final class CabalInquisitor extends CardImpl { Ability ability = new ActivateAsSorceryConditionalActivatedAbility(Zone.BATTLEFIELD, new DiscardTargetEffect(1), new ManaCostsImpl("{1}{B}"), new CardsInControllerGraveyardCondition(7)); ability.addTarget(new TargetPlayer()); ability.addCost(new TapSourceCost()); - ability.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard(2, new FilterCard("cards from your graveyard")))); + ability.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard(2, StaticFilters.FILTER_CARDS_FROM_YOUR_GRAVEYARD))); ability.setAbilityWord(AbilityWord.THRESHOLD); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/c/CabalInterrogator.java b/Mage.Sets/src/mage/cards/c/CabalInterrogator.java index 8fff1f7921a..fb4ab265faf 100644 --- a/Mage.Sets/src/mage/cards/c/CabalInterrogator.java +++ b/Mage.Sets/src/mage/cards/c/CabalInterrogator.java @@ -1,4 +1,3 @@ - package mage.cards.c; import mage.MageInt; @@ -7,19 +6,14 @@ import mage.abilities.common.ActivateAsSorceryActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.dynamicvalue.common.ManacostVariableValue; -import mage.abilities.effects.OneShotEffect; -import mage.cards.*; +import mage.abilities.effects.common.discard.DiscardCardYouChooseTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.FilterCard; -import mage.game.Game; -import mage.players.Player; -import mage.target.TargetCard; +import mage.constants.TargetController; import mage.target.TargetPlayer; -import java.util.List; import java.util.UUID; /** @@ -36,8 +30,10 @@ public final class CabalInterrogator extends CardImpl { this.toughness = new MageInt(1); // {X}{B}, {tap}: Target player reveals X cards from their hand and you choose one of them. That player discards that card. - // Activate this ability only any time you could cast a sorcery. - Ability ability = new ActivateAsSorceryActivatedAbility(Zone.BATTLEFIELD, new CabalInterrogatorEffect(), new ManaCostsImpl("{X}{B}")); + // Activate only as a sorcery. + Ability ability = new ActivateAsSorceryActivatedAbility( + new DiscardCardYouChooseTargetEffect(TargetController.ANY, ManacostVariableValue.REGULAR), + new ManaCostsImpl("{X}{B}")); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetPlayer()); this.addAbility(ability); @@ -52,68 +48,3 @@ public final class CabalInterrogator extends CardImpl { return new CabalInterrogator(this); } } - -class CabalInterrogatorEffect extends OneShotEffect { - - public CabalInterrogatorEffect() { - super(Outcome.Discard); - this.staticText = "Target player reveals X cards from their hand and you choose one of them. That player discards that card"; - } - - public CabalInterrogatorEffect(final CabalInterrogatorEffect effect) { - super(effect); - } - - @Override - public CabalInterrogatorEffect copy() { - return new CabalInterrogatorEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - - Player targetPlayer = game.getPlayer(source.getFirstTarget()); - Player controller = game.getPlayer(source.getControllerId()); - if (targetPlayer == null || controller == null) { - return false; - } - - int amountToReveal = (ManacostVariableValue.REGULAR).calculate(game, source, this); - - Cards revealedCards = new CardsImpl(); - if (amountToReveal > 0 && targetPlayer.getHand().size() > amountToReveal) { - Cards cardsInHand = new CardsImpl(); - cardsInHand.addAll(targetPlayer.getHand()); - - TargetCard target = new TargetCard(amountToReveal, Zone.HAND, new FilterCard()); - - if (targetPlayer.choose(Outcome.Discard, cardsInHand, target, game)) { - List targets = target.getTargets(); - for (UUID targetId : targets) { - Card card = game.getCard(targetId); - if (card != null) { - revealedCards.add(card); - } - } - } - } else { - revealedCards.addAll(targetPlayer.getHand()); - } - - TargetCard targetInHand = new TargetCard(Zone.HAND, new FilterCard("card to discard")); - - if (!revealedCards.isEmpty()) { - targetPlayer.revealCards("Cabal Interrogator", revealedCards, game); - Card card = null; - if (revealedCards.size() > 1) { - controller.choose(Outcome.Discard, revealedCards, targetInHand, game); - card = revealedCards.get(targetInHand.getFirstTarget(), game); - } else { - card = revealedCards.getRandom(game); - } - - targetPlayer.discard(card, false, source, game); - } - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/c/CabalSurgeon.java b/Mage.Sets/src/mage/cards/c/CabalSurgeon.java index b242090ab0f..ddad3a2ae89 100644 --- a/Mage.Sets/src/mage/cards/c/CabalSurgeon.java +++ b/Mage.Sets/src/mage/cards/c/CabalSurgeon.java @@ -14,7 +14,6 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.FilterCard; import mage.filter.StaticFilters; import mage.target.common.TargetCardInYourGraveyard; @@ -35,7 +34,7 @@ public final class CabalSurgeon extends CardImpl { Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ReturnToHandTargetEffect(), new ManaCostsImpl("{2}{B}{B}")); ability.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); ability.addCost(new TapSourceCost()); - ability.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard(2, new FilterCard("cards from your graveyard")))); + ability.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard(2, StaticFilters.FILTER_CARDS_FROM_YOUR_GRAVEYARD))); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/c/CabalTherapist.java b/Mage.Sets/src/mage/cards/c/CabalTherapist.java index 24777da0288..53997fd42a9 100644 --- a/Mage.Sets/src/mage/cards/c/CabalTherapist.java +++ b/Mage.Sets/src/mage/cards/c/CabalTherapist.java @@ -40,7 +40,7 @@ public final class CabalTherapist extends CardImpl { this.toughness = new MageInt(1); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // At the beginning of your precombat main phase, you may sacrifice a creature. When you do, choose a nonland card name, then target player reveals their hand and discards all cards with that name. ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/c/CacklingCulprit.java b/Mage.Sets/src/mage/cards/c/CacklingCulprit.java new file mode 100644 index 00000000000..0d6ed4dc548 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CacklingCulprit.java @@ -0,0 +1,53 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.common.DiesThisOrAnotherCreatureTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.keyword.DeathtouchAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CacklingCulprit extends CardImpl { + + public CacklingCulprit(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ROGUE); + this.power = new MageInt(3); + this.toughness = new MageInt(5); + this.color.setBlack(true); + this.nightCard = true; + + // Whenever Cackling Culprit or another creature you control dies, you gain 1 life. + this.addAbility(new DiesThisOrAnotherCreatureTriggeredAbility( + new GainLifeEffect(1), false, StaticFilters.FILTER_CONTROLLED_CREATURE + )); + + // {1}{B}: Cackling Culprit gains deathtouch until end of turn. + this.addAbility(new SimpleActivatedAbility(new GainAbilitySourceEffect( + DeathtouchAbility.getInstance(), Duration.EndOfTurn + ), new ManaCostsImpl<>("{1}{B}"))); + } + + private CacklingCulprit(final CacklingCulprit card) { + super(card); + } + + @Override + public CacklingCulprit copy() { + return new CacklingCulprit(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CalculatingLich.java b/Mage.Sets/src/mage/cards/c/CalculatingLich.java index d003b57b959..1c242614ff5 100644 --- a/Mage.Sets/src/mage/cards/c/CalculatingLich.java +++ b/Mage.Sets/src/mage/cards/c/CalculatingLich.java @@ -30,7 +30,7 @@ public final class CalculatingLich extends CardImpl { this.toughness = new MageInt(5); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // Whenever a creature attacks one of your opponents, that player loses 1 life. this.addAbility(new CalculatingLichTriggeredAbility()); diff --git a/Mage.Sets/src/mage/cards/c/CalixDestinysHand.java b/Mage.Sets/src/mage/cards/c/CalixDestinysHand.java index 13460df08d4..544d46fbda1 100644 --- a/Mage.Sets/src/mage/cards/c/CalixDestinysHand.java +++ b/Mage.Sets/src/mage/cards/c/CalixDestinysHand.java @@ -4,7 +4,6 @@ import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.LookLibraryAndPickControllerEffect; @@ -55,7 +54,7 @@ public final class CalixDestinysHand extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.CALIX); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Look at the top four cards of your library. You may reveal an enchantment card from among them and put that card into your hand. Put the rest on the bottom of your library in a random order. this.addAbility(new LoyaltyAbility(new LookLibraryAndPickControllerEffect( diff --git a/Mage.Sets/src/mage/cards/c/CallForBlood.java b/Mage.Sets/src/mage/cards/c/CallForBlood.java index bf8bf7d9c30..3f2eafcd776 100644 --- a/Mage.Sets/src/mage/cards/c/CallForBlood.java +++ b/Mage.Sets/src/mage/cards/c/CallForBlood.java @@ -1,23 +1,17 @@ - package mage.cards.c; import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.costs.Cost; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.effects.Effect; +import mage.abilities.dynamicvalue.common.SacrificeCostCreaturesPower; +import mage.abilities.dynamicvalue.common.SignInversionDynamicValue; import mage.abilities.effects.common.continuous.BoostTargetEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.Zone; import static mage.filter.StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT; -import mage.game.Game; -import mage.game.permanent.Permanent; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; @@ -27,14 +21,15 @@ import mage.target.common.TargetCreaturePermanent; */ public final class CallForBlood extends CardImpl { + private static final DynamicValue xValue = new SignInversionDynamicValue(SacrificeCostCreaturesPower.instance, false); + public CallForBlood(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{4}{B}"); this.subtype.add(SubType.ARCANE); - // As an additional cost to cast Call for Blood, sacrifice a creature. + // As an additional cost to cast this spell, sacrifice a creature. this.getSpellAbility().addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(FILTER_CONTROLLED_CREATURE_SHORT_TEXT))); // Target creature gets -X/-X until end of turn, where X is the sacrificed creature's power. - DynamicValue xValue = new CallForBloodDynamicValue(); this.getSpellAbility().addEffect(new BoostTargetEffect(xValue, xValue, Duration.EndOfTurn, true)); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); @@ -49,37 +44,3 @@ public final class CallForBlood extends CardImpl { return new CallForBlood(this); } } - -class CallForBloodDynamicValue implements DynamicValue { - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - Card sourceCard = game.getCard(sourceAbility.getSourceId()); - if (sourceCard != null) { - for (Cost cost : sourceAbility.getCosts()) { - if (cost instanceof SacrificeTargetCost) { - Permanent p = (Permanent) game.getLastKnownInformation(((SacrificeTargetCost) cost).getPermanents().get(0).getId(), Zone.BATTLEFIELD); - if (p != null) { - return -1 * p.getPower().getValue(); - } - } - } - } - return 0; - } - - @Override - public CallForBloodDynamicValue copy() { - return this; - } - - @Override - public String getMessage() { - return ", where X is the sacrificed creature's power"; - } - - @Override - public String toString() { - return "-X"; - } -} diff --git a/Mage.Sets/src/mage/cards/c/CallToServe.java b/Mage.Sets/src/mage/cards/c/CallToServe.java index 870b33cdb55..0c6379ff33a 100644 --- a/Mage.Sets/src/mage/cards/c/CallToServe.java +++ b/Mage.Sets/src/mage/cards/c/CallToServe.java @@ -1,8 +1,6 @@ - package mage.cards.c; import java.util.UUID; -import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.Effect; @@ -15,9 +13,7 @@ import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ColorPredicate; +import mage.filter.StaticFilters; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; @@ -27,18 +23,12 @@ import mage.target.common.TargetCreaturePermanent; */ public final class CallToServe extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public CallToServe(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{W}"); this.subtype.add(SubType.AURA); // Enchant nonblack creature - TargetPermanent auraTarget = new TargetCreaturePermanent(filter); + TargetPermanent auraTarget = new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK); this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); Ability ability = new EnchantAbility(auraTarget.getTargetName()); diff --git a/Mage.Sets/src/mage/cards/c/CallousOppressor.java b/Mage.Sets/src/mage/cards/c/CallousOppressor.java index cce47a757b6..35cdfca8e54 100644 --- a/Mage.Sets/src/mage/cards/c/CallousOppressor.java +++ b/Mage.Sets/src/mage/cards/c/CallousOppressor.java @@ -51,7 +51,7 @@ public final class CallousOppressor extends CardImpl { // {T}: Gain control of target creature that isn't of the chosen type for as long as Callous Oppressor remains tapped. ConditionalContinuousEffect effect = new ConditionalContinuousEffect( new GainControlTargetEffect(Duration.OneUse), - SourceTappedCondition.instance, + SourceTappedCondition.TAPPED, "Gain control of target creature for as long as {this} remains tapped"); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent(new CallousOppressorFilter())); diff --git a/Mage.Sets/src/mage/cards/c/CallowJushi.java b/Mage.Sets/src/mage/cards/c/CallowJushi.java index 86c94abc1a8..2b7220c936c 100644 --- a/Mage.Sets/src/mage/cards/c/CallowJushi.java +++ b/Mage.Sets/src/mage/cards/c/CallowJushi.java @@ -43,7 +43,7 @@ public final class CallowJushi extends CardImpl { this.flipCardName = "Jaraku the Interloper"; // Whenever you cast a Spirit or Arcane spell, you may put a ki counter on Callow Jushi. - this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.KI.createInstance()), StaticFilters.SPIRIT_OR_ARCANE_CARD, true)); + this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.KI.createInstance()), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true)); // At the beginning of the end step, if there are two or more ki counters on Callow Jushi, you may flip it. this.addAbility(new ConditionalInterveningIfTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/c/CanopyCover.java b/Mage.Sets/src/mage/cards/c/CanopyCover.java index 58ad2aed98f..a07ba372567 100644 --- a/Mage.Sets/src/mage/cards/c/CanopyCover.java +++ b/Mage.Sets/src/mage/cards/c/CanopyCover.java @@ -2,19 +2,19 @@ package mage.cards.c; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.RestrictionEffect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.CantBeTargetedAttachedEffect; +import mage.abilities.effects.common.combat.CantBeBlockedByCreaturesAttachedEffect; import mage.abilities.keyword.EnchantAbility; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.ReachAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.FilterObject; +import mage.filter.common.FilterCreaturePermanent; import mage.filter.FilterStackObject; -import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.AbilityPredicate; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; @@ -25,7 +25,17 @@ import java.util.UUID; */ public final class CanopyCover extends CardImpl { - private static final FilterObject filter = new FilterStackObject("spells or abilities your opponents control"); + private static final FilterCreaturePermanent notFlyingorReachCreatures = new FilterCreaturePermanent("except by creatures with flying or reach"); + private static final FilterStackObject filter = new FilterStackObject("spells or abilities your opponents control"); + + static { + notFlyingorReachCreatures.add(Predicates.not( + Predicates.or( + new AbilityPredicate(FlyingAbility.class), + new AbilityPredicate(ReachAbility.class) + ) + )); + } public CanopyCover(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{G}"); @@ -39,10 +49,10 @@ public final class CanopyCover extends CardImpl { this.addAbility(ability); // Enchanted creature can't be blocked except by creatures with flying or reach. (!this is a static ability of the enchantment) - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CanopyCoverEffect())); + this.addAbility(new SimpleStaticAbility(new CantBeBlockedByCreaturesAttachedEffect(Duration.WhileOnBattlefield, notFlyingorReachCreatures, AttachmentType.AURA))); // Enchanted creature can't be the target of spells or abilities your opponents control. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantBeTargetedAttachedEffect(filter, Duration.WhileOnBattlefield, AttachmentType.AURA, TargetController.OPPONENT))); + this.addAbility(new SimpleStaticAbility(new CantBeTargetedAttachedEffect(filter, Duration.WhileOnBattlefield, AttachmentType.AURA, TargetController.OPPONENT))); } private CanopyCover(final CanopyCover card) { @@ -54,35 +64,3 @@ public final class CanopyCover extends CardImpl { return new CanopyCover(this); } } - -class CanopyCoverEffect extends RestrictionEffect { - - public CanopyCoverEffect() { - super(Duration.WhileOnBattlefield); - staticText = "Enchanted creature can't be blocked except by creatures with flying or reach"; - } - - public CanopyCoverEffect(final CanopyCoverEffect effect) { - super(effect); - } - - @Override - public boolean applies(Permanent permanent, Ability source, Game game) { - Permanent equipment = game.getPermanent(source.getSourceId()); - if (equipment != null && equipment.getAttachedTo() != null) { - Permanent equipped = game.getPermanent(equipment.getAttachedTo()); - return equipped != null && permanent.getId().equals(equipped.getId()); - } - return false; - } - - @Override - public boolean canBeBlocked(Permanent attacker, Permanent blocker, Ability source, Game game, boolean canUseChooseDialogs) { - return blocker.getAbilities().contains(FlyingAbility.getInstance()) || blocker.getAbilities().contains(ReachAbility.getInstance()); - } - - @Override - public CanopyCoverEffect copy() { - return new CanopyCoverEffect(this); - } -} diff --git a/Mage.Sets/src/mage/cards/c/CantStayAway.java b/Mage.Sets/src/mage/cards/c/CantStayAway.java index 813896ec60c..274a24fda00 100644 --- a/Mage.Sets/src/mage/cards/c/CantStayAway.java +++ b/Mage.Sets/src/mage/cards/c/CantStayAway.java @@ -77,7 +77,7 @@ class CantStayAwayEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); Card targetCard = game.getCard(source.getFirstTarget()); - if (controller == null || targetCard == null) { + if (controller == null || targetCard == null || game.getState().getZone(targetCard.getId()) != Zone.GRAVEYARD) { return false; } controller.moveCards(targetCard, Zone.BATTLEFIELD, source, game); diff --git a/Mage.Sets/src/mage/cards/c/CaptivatingCrew.java b/Mage.Sets/src/mage/cards/c/CaptivatingCrew.java index ee8fe2b4ad2..83e2fb1fc58 100644 --- a/Mage.Sets/src/mage/cards/c/CaptivatingCrew.java +++ b/Mage.Sets/src/mage/cards/c/CaptivatingCrew.java @@ -16,9 +16,8 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.TargetController; import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; /** @@ -27,12 +26,6 @@ import mage.target.common.TargetCreaturePermanent; */ public final class CaptivatingCrew extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public CaptivatingCrew(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); @@ -49,7 +42,7 @@ public final class CaptivatingCrew extends CardImpl { effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn); effect.setText("It gains haste until end of turn"); ability.addEffect(effect); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/c/CaptivatingGlance.java b/Mage.Sets/src/mage/cards/c/CaptivatingGlance.java index 47f67f62883..ee5e78824ea 100644 --- a/Mage.Sets/src/mage/cards/c/CaptivatingGlance.java +++ b/Mage.Sets/src/mage/cards/c/CaptivatingGlance.java @@ -84,13 +84,13 @@ class CaptivatingGlanceEffect extends OneShotEffect { if (enchantedCreature != null) { if (clashResult) { ContinuousEffect effect = new GainControlTargetEffect(Duration.Custom, false, controller.getId()); - effect.setTargetPointer(new FixedTarget(enchantedCreature.getId())); + effect.setTargetPointer(new FixedTarget(enchantedCreature.getId(), game)); game.addEffect(effect, source); } else { Player opponentWhomControllerClashedWith = game.getPlayer(targetPointer.getFirst(game, source)); if (opponentWhomControllerClashedWith != null) { ContinuousEffect effect = new GainControlTargetEffect(Duration.Custom, false, opponentWhomControllerClashedWith.getId()); - effect.setTargetPointer(new FixedTarget(enchantedCreature.getId())); + effect.setTargetPointer(new FixedTarget(enchantedCreature.getId(), game)); game.addEffect(effect, source); } } diff --git a/Mage.Sets/src/mage/cards/c/CaptiveAudience.java b/Mage.Sets/src/mage/cards/c/CaptiveAudience.java index 8481ae82a48..bde88a7f90a 100644 --- a/Mage.Sets/src/mage/cards/c/CaptiveAudience.java +++ b/Mage.Sets/src/mage/cards/c/CaptiveAudience.java @@ -4,24 +4,19 @@ import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.EntersBattlefieldAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.CreateTokenTargetEffect; +import mage.abilities.effects.common.EntersBattlefieldUnderControlOfOpponentOfChoiceEffect; import mage.abilities.effects.common.SetPlayerLifeSourceEffect; import mage.abilities.effects.common.discard.DiscardHandControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.Outcome; import mage.constants.TargetController; import mage.game.Game; import mage.game.permanent.token.ZombieToken; -import mage.players.Player; -import mage.target.targetpointer.FixedTarget; import java.util.UUID; -import mage.abilities.effects.common.EntersBattlefieldUnderControlOfOpponentOfChoiceEffect; - -import static mage.constants.Outcome.Benefit; /** * @author TheElk801 @@ -62,7 +57,7 @@ public final class CaptiveAudience extends CardImpl { class CaptiveAudienceCreateTokensEffect extends OneShotEffect { CaptiveAudienceCreateTokensEffect() { - super(Benefit); + super(Outcome.Benefit); staticText = "Each opponent creates five 2/2 black Zombie creature tokens."; } @@ -77,17 +72,9 @@ class CaptiveAudienceCreateTokensEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller == null) { - return false; - } - for (Player player : game.getPlayers().values()) { - if (player != null && controller.hasOpponent(player.getId(), game)) { - Effect effect = new CreateTokenTargetEffect(new ZombieToken(), 5); - effect.setTargetPointer(new FixedTarget(player.getId(), game)); - effect.apply(game, source); - } + for (UUID playerId : game.getOpponents(source.getControllerId())) { + new ZombieToken().putOntoBattlefield(5, game, source, playerId); } return true; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/c/CapturedByTheConsulate.java b/Mage.Sets/src/mage/cards/c/CapturedByTheConsulate.java index 74459fda0d1..eb2acc631d0 100644 --- a/Mage.Sets/src/mage/cards/c/CapturedByTheConsulate.java +++ b/Mage.Sets/src/mage/cards/c/CapturedByTheConsulate.java @@ -113,7 +113,7 @@ class CapturedByTheConsulateTriggeredAbility extends TriggeredAbilityImpl { @Override public String getTriggerPhrase() { - return "Whenever an opponent casts a spell, if it has a single target, " ; + return "Whenever an opponent casts a spell, if it has a single target, "; } @Override diff --git a/Mage.Sets/src/mage/cards/c/CarefulCultivation.java b/Mage.Sets/src/mage/cards/c/CarefulCultivation.java new file mode 100644 index 00000000000..31cfd3d91e5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CarefulCultivation.java @@ -0,0 +1,70 @@ +package mage.cards.c; + +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.AttachedToMatchesFilterCondition; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.ChannelAbility; +import mage.abilities.keyword.EnchantAbility; +import mage.abilities.keyword.ReachAbility; +import mage.abilities.mana.SimpleManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.permanent.token.HumanMonkToken; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CarefulCultivation extends CardImpl { + + private static final Condition condition = new AttachedToMatchesFilterCondition(StaticFilters.FILTER_PERMANENT_CREATURE); + + public CarefulCultivation(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}"); + + 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 +1/+3 and has reach and "{T}: Add {G}{G}." + Ability ability = new SimpleStaticAbility(new ConditionalContinuousEffect( + new BoostEnchantedEffect(1, 3), condition, + "as long as enchanted permanent is a creature, it gets +1/+3" + )); + ability.addEffect(new ConditionalContinuousEffect(new GainAbilityAttachedEffect( + ReachAbility.getInstance(), AttachmentType.AURA + ), condition, "and has reach")); + ability.addEffect(new ConditionalContinuousEffect(new GainAbilityAttachedEffect( + new SimpleManaAbility(Zone.BATTLEFIELD, Mana.GreenMana(2), new TapSourceCost()), AttachmentType.AURA + ), condition, "and \"{T}: Add {G}{G}.\"")); + this.addAbility(ability); + + // Channel — {1}{G}, Discard Careful Cultivation: Create a 1/1 green Human Monk creature token with "{T}: Add {G}." + this.addAbility(new ChannelAbility("{1}{G}", new CreateTokenEffect(new HumanMonkToken()))); + } + + private CarefulCultivation(final CarefulCultivation card) { + super(card); + } + + @Override + public CarefulCultivation copy() { + return new CarefulCultivation(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CarnifexDemon.java b/Mage.Sets/src/mage/cards/c/CarnifexDemon.java index 54c845eb5b1..e060e6fb8b4 100644 --- a/Mage.Sets/src/mage/cards/c/CarnifexDemon.java +++ b/Mage.Sets/src/mage/cards/c/CarnifexDemon.java @@ -43,7 +43,7 @@ public final class CarnifexDemon extends CardImpl { this.addAbility(new EntersBattlefieldAbility( new AddCountersSourceEffect(CounterType.M1M1.createInstance(2)), - "{this} enters the battlefield with two -1/-1 counters on it" + "with two -1/-1 counters on it" )); Ability ability = new SimpleActivatedAbility( diff --git a/Mage.Sets/src/mage/cards/c/CarnivalOfSouls.java b/Mage.Sets/src/mage/cards/c/CarnivalOfSouls.java index 0cfc7aee728..df31f1d6e26 100644 --- a/Mage.Sets/src/mage/cards/c/CarnivalOfSouls.java +++ b/Mage.Sets/src/mage/cards/c/CarnivalOfSouls.java @@ -13,7 +13,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SetTargetPointer; import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; /** * @@ -23,11 +23,10 @@ public final class CarnivalOfSouls extends CardImpl { public CarnivalOfSouls(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}"); - // Whenever a creature enters the battlefield, you lose 1 life and add {B}. Ability ability = new EntersBattlefieldAllTriggeredAbility(Zone.BATTLEFIELD, new LoseLifeSourceControllerEffect(1), - new FilterCreaturePermanent("a creature"), false, SetTargetPointer.PERMANENT, null, false); + StaticFilters.FILTER_PERMANENT_A_CREATURE, false, SetTargetPointer.PERMANENT, null, false); Effect effect = new AddManaToManaPoolSourceControllerEffect(Mana.BlackMana(1)); effect.setText("and add {B}."); ability.addEffect(effect); diff --git a/Mage.Sets/src/mage/cards/c/CarpetOfFlowers.java b/Mage.Sets/src/mage/cards/c/CarpetOfFlowers.java index bb5dcc74be4..782d533d055 100644 --- a/Mage.Sets/src/mage/cards/c/CarpetOfFlowers.java +++ b/Mage.Sets/src/mage/cards/c/CarpetOfFlowers.java @@ -120,7 +120,7 @@ class CarpetOfFlowersEffect extends ManaEffect { CarpetOfFlowersEffect() { super(); - staticText = "add X mana of any one color, where X is the number of Islands target opponent controls"; + staticText = "you may add X mana of any one color, where X is the number of Islands target opponent controls"; } CarpetOfFlowersEffect(final CarpetOfFlowersEffect effect) { diff --git a/Mage.Sets/src/mage/cards/c/CartographersSurvey.java b/Mage.Sets/src/mage/cards/c/CartographersSurvey.java new file mode 100644 index 00000000000..3e4989f9623 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CartographersSurvey.java @@ -0,0 +1,73 @@ +package mage.cards.c; + +import java.util.UUID; + +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.common.LookLibraryControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; + +/** + * + * @author weirddan455 + */ +public final class CartographersSurvey extends CardImpl { + + public CartographersSurvey(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{G}"); + + // Look at the top seven cards of your library. Put up to two land cards from among them onto the battlefield tapped. Put the rest on the bottom of your library in a random order. + this.getSpellAbility().addEffect(new CartographersSurveyEffect()); + } + + private CartographersSurvey(final CartographersSurvey card) { + super(card); + } + + @Override + public CartographersSurvey copy() { + return new CartographersSurvey(this); + } +} + +class CartographersSurveyEffect extends LookLibraryControllerEffect { + + public CartographersSurveyEffect() { + super(Outcome.PutLandInPlay, StaticValue.get(7), false, Zone.LIBRARY, false); + this.setBackInRandomOrder(true); + staticText = "Look at the top seven cards of your library. Put up to two land cards from among them onto the battlefield tapped. Put the rest on the bottom of your library in a random order"; + } + + private CartographersSurveyEffect(final CartographersSurveyEffect effect) { + super(effect); + } + + @Override + public CartographersSurveyEffect copy() { + return new CartographersSurveyEffect(this); + } + + @Override + protected void actionWithSelectedCards(Cards cards, Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + TargetCard target = new TargetCard(0, 2, Zone.LIBRARY, StaticFilters.FILTER_CARD_LANDS); + controller.choose(outcome, cards, target, game); + Cards pickedCards = new CardsImpl(target.getTargets()); + if (!pickedCards.isEmpty()) { + cards.removeAll(pickedCards); + controller.moveCards(pickedCards.getCards(game), Zone.BATTLEFIELD, source, game, true, false, false, null); + } + } + } +} diff --git a/Mage.Sets/src/mage/cards/c/CartoucheOfStrength.java b/Mage.Sets/src/mage/cards/c/CartoucheOfStrength.java index 1ce52f01ab8..d89bff5cb90 100644 --- a/Mage.Sets/src/mage/cards/c/CartoucheOfStrength.java +++ b/Mage.Sets/src/mage/cards/c/CartoucheOfStrength.java @@ -108,7 +108,8 @@ class FightEnchantedTargetEffect extends OneShotEffect { @Override public String getText(Mode mode) { - return "you may have enchanted creature fight target creature an opponent controls."; + return "you may have enchanted creature fight target creature an opponent controls. " + + "(Each deals damage equal to its power to the other.)"; } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/c/CastleRaptors.java b/Mage.Sets/src/mage/cards/c/CastleRaptors.java index b9e2ed10f61..2a80246e423 100644 --- a/Mage.Sets/src/mage/cards/c/CastleRaptors.java +++ b/Mage.Sets/src/mage/cards/c/CastleRaptors.java @@ -33,7 +33,7 @@ public final class CastleRaptors extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // As long as Castle Raptors is untapped, it gets +0/+2. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect( - new BoostSourceEffect(0, 2, Duration.WhileOnBattlefield), new InvertCondition(SourceTappedCondition.instance), + new BoostSourceEffect(0, 2, Duration.WhileOnBattlefield), SourceTappedCondition.UNTAPPED, "As long as {this} is untapped, it gets +0/+2."))); } diff --git a/Mage.Sets/src/mage/cards/c/CatapultCaptain.java b/Mage.Sets/src/mage/cards/c/CatapultCaptain.java new file mode 100644 index 00000000000..59660863339 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CatapultCaptain.java @@ -0,0 +1,53 @@ +package mage.cards.c; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.SacrificeCostCreaturesToughness; +import mage.abilities.effects.common.LoseLifeTargetEffect; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.target.common.TargetOpponent; + +/** + * + * @author weirddan455 + */ +public final class CatapultCaptain extends CardImpl { + + public CatapultCaptain(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); + + this.subtype.add(SubType.ZOMBIE); + this.color.setBlack(true); + this.power = new MageInt(2); + this.toughness = new MageInt(6); + + // Back half of Catapult Fodder + this.nightCard = true; + + // {2}{B}, {T}, Sacrifice another creature: Target opponent loses life equal to the sacrificed creature's toughness. + Ability ability = new SimpleActivatedAbility(new LoseLifeTargetEffect(SacrificeCostCreaturesToughness.instance) + .setText("Target opponent loses life equal to the sacrificed creature's toughness"), new ManaCostsImpl<>("{2}{B}")); + ability.addCost(new TapSourceCost()); + ability.addCost(new SacrificeTargetCost(StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE)); + ability.addTarget(new TargetOpponent()); + this.addAbility(ability); + } + + private CatapultCaptain(final CatapultCaptain card) { + super(card); + } + + @Override + public CatapultCaptain copy() { + return new CatapultCaptain(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CatapultFodder.java b/Mage.Sets/src/mage/cards/c/CatapultFodder.java new file mode 100644 index 00000000000..3e5a9c90a68 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CatapultFodder.java @@ -0,0 +1,73 @@ +package mage.cards.c; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfCombatTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.keyword.TransformAbility; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * + * @author weirddan455 + */ +public final class CatapultFodder extends CardImpl { + + public CatapultFodder(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.subtype.add(SubType.ZOMBIE); + this.power = new MageInt(1); + this.toughness = new MageInt(5); + this.secondSideCardClazz = mage.cards.c.CatapultCaptain.class; + + // At the beginning of combat on your turn, if you control three or more creatures that each have toughness greater than their power, transform Catapult Fodder. + this.addAbility(new TransformAbility()); + this.addAbility(new ConditionalInterveningIfTriggeredAbility( + new BeginningOfCombatTriggeredAbility(new TransformSourceEffect(), TargetController.YOU, false), + CatapultFodderCondition.instance, + "At the beginning of combat on your turn, if you control three or more creatures that each have toughness greater than their power, transform {this}" + )); + } + + private CatapultFodder(final CatapultFodder card) { + super(card); + } + + @Override + public CatapultFodder copy() { + return new CatapultFodder(this); + } +} + +enum CatapultFodderCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + int creatures = 0; + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(source.getControllerId())) { + if (permanent.isCreature(game) && permanent.getToughness().getValue() > permanent.getPower().getValue()) { + creatures++; + if (creatures >= 3) { + return true; + } + } + } + return false; + } + + @Override + public String toString() { + return "you control three or more creatures that each have toughness greater than their power"; + } +} diff --git a/Mage.Sets/src/mage/cards/c/CatharsCrusade.java b/Mage.Sets/src/mage/cards/c/CatharsCrusade.java index c3d53f870eb..d9cc61888ba 100644 --- a/Mage.Sets/src/mage/cards/c/CatharsCrusade.java +++ b/Mage.Sets/src/mage/cards/c/CatharsCrusade.java @@ -26,7 +26,7 @@ public final class CatharsCrusade extends CardImpl { this.addAbility(new EntersBattlefieldControlledTriggeredAbility( Zone.BATTLEFIELD, new AddCountersAllEffect(CounterType.P1P1.createInstance(), new FilterControlledCreaturePermanent()), - StaticFilters.FILTER_PERMANENT_CREATURE_A, + StaticFilters.FILTER_PERMANENT_A_CREATURE, false) ); } diff --git a/Mage.Sets/src/mage/cards/c/CatlikeCuriosity.java b/Mage.Sets/src/mage/cards/c/CatlikeCuriosity.java new file mode 100644 index 00000000000..9bd5e6aa4c7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CatlikeCuriosity.java @@ -0,0 +1,61 @@ +package mage.cards.c; + +import mage.abilities.Ability; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.common.PutIntoGraveFromAnywhereSourceAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.ExileSourceEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CatlikeCuriosity extends CardImpl { + + public CatlikeCuriosity(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, ""); + + this.subtype.add(SubType.AURA); + this.color.setBlue(true); + this.nightCard = true; + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // Enchanted creature has "Whenever this creature deals combat damage to a player, draw a card." + this.addAbility(new SimpleStaticAbility(new GainAbilityAttachedEffect( + new DealsCombatDamageToAPlayerTriggeredAbility( + new DrawCardSourceControllerEffect(1), false + ).setTriggerPhrase("Whenever this creature deals combat damage to a player, "), AttachmentType.AURA + ))); + + // If Catlike Curiosity would be put into a graveyard from anywhere, exile it instead. + this.addAbility(new PutIntoGraveFromAnywhereSourceAbility(new ExileSourceEffect().setText("exile it instead"))); + } + + private CatlikeCuriosity(final CatlikeCuriosity card) { + super(card); + } + + @Override + public CatlikeCuriosity copy() { + return new CatlikeCuriosity(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CaughtInTheBrights.java b/Mage.Sets/src/mage/cards/c/CaughtInTheBrights.java index 8a5e6d559d9..cb593ab1d96 100644 --- a/Mage.Sets/src/mage/cards/c/CaughtInTheBrights.java +++ b/Mage.Sets/src/mage/cards/c/CaughtInTheBrights.java @@ -44,7 +44,7 @@ public final class CaughtInTheBrights extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantAttackBlockAttachedEffect(AttachmentType.AURA))); // When a Vehicle you control attacks, exile enchanted creature. - this.addAbility(new AttacksAllTriggeredAbility(new ExileAttachedEffect(), false, filter, SetTargetPointer.NONE, false)); + this.addAbility(new AttacksAllTriggeredAbility(new ExileAttachedEffect(), false, filter, SetTargetPointer.NONE, false).setTriggerPhrase("When a Vehicle you control attacks, ")); } private CaughtInTheBrights(final CaughtInTheBrights card) { diff --git a/Mage.Sets/src/mage/cards/c/CaveIn.java b/Mage.Sets/src/mage/cards/c/CaveIn.java index 51d6cc315e9..ccccf8741c5 100644 --- a/Mage.Sets/src/mage/cards/c/CaveIn.java +++ b/Mage.Sets/src/mage/cards/c/CaveIn.java @@ -1,7 +1,5 @@ - package mage.cards.c; -import java.util.UUID; import mage.ObjectColor; import mage.abilities.costs.AlternativeCostSourceAbility; import mage.abilities.costs.common.ExileFromHandCost; @@ -10,27 +8,29 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.filter.common.FilterOwnedCard; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardIdPredicate; import mage.filter.predicate.mageobject.ColorPredicate; import mage.target.common.TargetCardInHand; +import java.util.UUID; + /** - * * @author emerald000 */ public final class CaveIn extends CardImpl { - public CaveIn(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{R}{R}"); + private static final FilterOwnedCard filter + = new FilterOwnedCard("a red card from your hand"); + static { + filter.add(new ColorPredicate(ObjectColor.RED)); + } + + public CaveIn(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{R}{R}"); // You may exile a red card from your hand rather than pay Cave-In's mana cost. - FilterOwnedCard filter = new FilterOwnedCard("a red card from your hand"); - filter.add(new ColorPredicate(ObjectColor.RED)); - filter.add(Predicates.not(new CardIdPredicate(this.getId()))); this.addAbility(new AlternativeCostSourceAbility(new ExileFromHandCost(new TargetCardInHand(filter)))); - + // Cave-In deals 2 damage to each creature and each player. this.getSpellAbility().addEffect(new DamageEverythingEffect(2)); } diff --git a/Mage.Sets/src/mage/cards/c/CecilyHauntedMage.java b/Mage.Sets/src/mage/cards/c/CecilyHauntedMage.java new file mode 100644 index 00000000000..8899e38c863 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CecilyHauntedMage.java @@ -0,0 +1,127 @@ +package mage.cards.c; + +import mage.ApprovingObject; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.MaximumHandSizeControllerEffect; +import mage.abilities.effects.common.cost.CastWithoutPayingManaCostEffect; +import mage.abilities.keyword.FriendsForeverAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.players.Player; +import mage.target.Target; +import mage.target.common.TargetCardInHand; +import org.apache.log4j.Logger; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CecilyHauntedMage extends CardImpl { + + public CecilyHauntedMage(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{B}{R}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(3); + this.toughness = new MageInt(5); + + // Your maximum hand size is eleven. + this.addAbility(new SimpleStaticAbility(new MaximumHandSizeControllerEffect( + 11, Duration.WhileOnBattlefield, MaximumHandSizeControllerEffect.HandSizeModification.SET + ))); + + // Whenever Eleven, the Mage attacks, you draw a card and you lose 1 life. Then if you have eleven or more cards in your hand, you may cast an instant or sorcery spell from your hand without paying its mana cost. + this.addAbility(new AttacksTriggeredAbility(new CecilyHauntedMageEffect())); + + // Friends forever + this.addAbility(FriendsForeverAbility.getInstance()); + } + + private CecilyHauntedMage(final CecilyHauntedMage card) { + super(card); + } + + @Override + public CecilyHauntedMage copy() { + return new CecilyHauntedMage(this); + } +} + +class CecilyHauntedMageEffect extends OneShotEffect { + + CecilyHauntedMageEffect() { + super(Outcome.Benefit); + staticText = "you draw a card and you lose 1 life. Then if you have eleven or more cards in your hand, " + + "you may cast an instant or sorcery spell from your hand without paying its mana cost"; + } + + private CecilyHauntedMageEffect(final CecilyHauntedMageEffect effect) { + super(effect); + } + + @Override + public CecilyHauntedMageEffect copy() { + return new CecilyHauntedMageEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + player.drawCards(1, source, game); + player.loseLife(1, game, source, false); + if (player.getHand().size() < 11) { + return true; + } + // TODO: change this to fit with changes made in https://github.com/magefree/mage/pull/8136 when merged + Target target = new TargetCardInHand(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY); + if (!target.canChoose( + source.getSourceId(), player.getId(), game + ) || !player.chooseUse( + Outcome.PlayForFree, "Cast an instant or sorcery spell " + + "from your hand without paying its mana cost?", source, game + )) { + return true; + } + Card cardToCast = null; + boolean cancel = false; + while (player.canRespond() + && !cancel) { + if (player.chooseTarget(Outcome.PlayForFree, target, source, game)) { + cardToCast = game.getCard(target.getFirstTarget()); + if (cardToCast != null) { + if (cardToCast.getSpellAbility() == null) { + Logger.getLogger(CastWithoutPayingManaCostEffect.class).fatal("Card: " + + cardToCast.getName() + " is no land and has no spell ability!"); + cancel = true; + } + if (cardToCast.getSpellAbility().canChooseTarget(game, player.getId())) { + cancel = true; + } + } + } else { + cancel = true; + } + } + if (cardToCast != null) { + game.getState().setValue("PlayFromNotOwnHandZone" + cardToCast.getId(), Boolean.TRUE); + player.cast(player.chooseAbilityForCast(cardToCast, game, true), + game, true, new ApprovingObject(source, game)); + game.getState().setValue("PlayFromNotOwnHandZone" + cardToCast.getId(), null); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/c/CelestialFlare.java b/Mage.Sets/src/mage/cards/c/CelestialFlare.java index 790dd67fe53..736181b3bc0 100644 --- a/Mage.Sets/src/mage/cards/c/CelestialFlare.java +++ b/Mage.Sets/src/mage/cards/c/CelestialFlare.java @@ -1,4 +1,3 @@ - package mage.cards.c; import java.util.UUID; @@ -15,7 +14,7 @@ import mage.target.TargetPlayer; */ public final class CelestialFlare extends CardImpl { - private static final FilterAttackingOrBlockingCreature filter = new FilterAttackingOrBlockingCreature("attacking or blocking creature"); + private static final FilterAttackingOrBlockingCreature filter = new FilterAttackingOrBlockingCreature("an attacking or blocking creature"); public CelestialFlare(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{W}{W}"); @@ -35,4 +34,4 @@ public final class CelestialFlare extends CardImpl { public CelestialFlare copy() { return new CelestialFlare(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/c/CelestialKirin.java b/Mage.Sets/src/mage/cards/c/CelestialKirin.java index 1340fae59e2..9bb18907613 100644 --- a/Mage.Sets/src/mage/cards/c/CelestialKirin.java +++ b/Mage.Sets/src/mage/cards/c/CelestialKirin.java @@ -35,7 +35,7 @@ public final class CelestialKirin extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); // Whenever you cast a Spirit or Arcane spell, destroy all permanents with that spell's converted mana cost. - this.addAbility(new SpellCastControllerTriggeredAbility(Zone.BATTLEFIELD, new CelestialKirinEffect(), StaticFilters.SPIRIT_OR_ARCANE_CARD, false, true)); + this.addAbility(new SpellCastControllerTriggeredAbility(Zone.BATTLEFIELD, new CelestialKirinEffect(), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false, true)); } private CelestialKirin(final CelestialKirin card) { diff --git a/Mage.Sets/src/mage/cards/c/CemeteryDesecrator.java b/Mage.Sets/src/mage/cards/c/CemeteryDesecrator.java new file mode 100644 index 00000000000..60b72184552 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CemeteryDesecrator.java @@ -0,0 +1,181 @@ +package mage.cards.c; + +import java.util.Map; +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.EntersBattlefieldOrDiesSourceTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.cards.Card; +import mage.constants.*; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.counters.Counter; +import mage.counters.Counters; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.common.TargetCardInGraveyard; +import mage.target.common.TargetOpponentsCreaturePermanent; + +/** + * + * @author weirddan455 + */ +public final class CemeteryDesecrator extends CardImpl { + + public CemeteryDesecrator(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}{B}"); + + this.subtype.add(SubType.ZOMBIE); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Menace + this.addAbility(new MenaceAbility(false)); + + // When Cemetery Desecrator enters the battlefield or dies, exile another card from a graveyard. When you do, choose one — + // • Remove X counters from target permanent, where X is the mana value of the exiled card. + // • Target creature an opponent controls gets -X/-X until end of turn, where X is the mana value of the exiled card. + this.addAbility(new EntersBattlefieldOrDiesSourceTriggeredAbility(new CemeteryDesecratorEffect(), false)); + } + + private CemeteryDesecrator(final CemeteryDesecrator card) { + super(card); + } + + @Override + public CemeteryDesecrator copy() { + return new CemeteryDesecrator(this); + } +} + +class CemeteryDesecratorEffect extends OneShotEffect { + + private static final FilterCard filter = new FilterCard("another card from a graveyard"); + private static final String triggerText = "choose one —
" + + "&bull Remove X counters from target permanent, where X is the mana value of the exiled card.
" + + "&bull Target creature an opponent controls gets -X/-X until end of turn, where X is the mana value of the exiled card."; + + static { + filter.add(AnotherPredicate.instance); + } + + public CemeteryDesecratorEffect() { + super(Outcome.Exile); + staticText = "exile another card from a graveyard. When you do, " + triggerText; + } + + private CemeteryDesecratorEffect(final CemeteryDesecratorEffect effect) { + super(effect); + } + + @Override + public CemeteryDesecratorEffect copy() { + return new CemeteryDesecratorEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + TargetCardInGraveyard target = new TargetCardInGraveyard(filter); + target.setNotTarget(true); + controller.choose(outcome, target, source.getSourceId(), game); + Card card = game.getCard(target.getFirstTarget()); + if (card != null) { + int manaValue = card.getManaValue(); + if (controller.moveCards(card, Zone.EXILED, source, game)) { + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility(new CemeteryDesecratorRemoveCountersEffect(manaValue), false, triggerText); + ability.addTarget(new TargetPermanent()); + Mode mode = new Mode(new BoostTargetEffect(-manaValue, -manaValue, Duration.EndOfTurn) + .setText("Target creature an opponent controls gets -X/-X until end of turn, where X is the mana value of the exiled card")); + mode.addTarget(new TargetOpponentsCreaturePermanent()); + ability.addMode(mode); + game.fireReflexiveTriggeredAbility(ability, source); + return true; + } + } + } + return false; + } +} + +class CemeteryDesecratorRemoveCountersEffect extends OneShotEffect { + + private final int xValue; + + public CemeteryDesecratorRemoveCountersEffect(int xValue) { + super(Outcome.UnboostCreature); + this.xValue = xValue; + staticText = "Remove X counters from target permanent, where X is the mana value of the exiled card"; + } + + private CemeteryDesecratorRemoveCountersEffect(final CemeteryDesecratorRemoveCountersEffect effect) { + super(effect); + this.xValue = effect.xValue; + } + + @Override + public CemeteryDesecratorRemoveCountersEffect copy() { + return new CemeteryDesecratorRemoveCountersEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (controller == null || permanent == null) { + return false; + } + if (xValue < 1) { + return false; + } + // Make copy of counters to avoid concurrent modification exception + Counters counters = permanent.getCounters(game).copy(); + int totalCounters = 0; + for (Counter counter : counters.values()) { + totalCounters += counter.getCount(); + } + if (totalCounters == 0) { + return false; + } + if (totalCounters <= xValue) { + for (Map.Entry entry : counters.entrySet()) { + permanent.removeCounters(entry.getKey(), entry.getValue().getCount(), source, game); + } + return true; + } + if (counters.size() == 1) { + String counterName = counters.keySet().iterator().next(); + permanent.removeCounters(counterName, xValue, source, game); + return true; + } + int remainingCounters = totalCounters; + int countersLeftToRemove = xValue; + for (Map.Entry entry : counters.entrySet()) { + String counterName = entry.getKey(); + int numCounters = entry.getValue().getCount(); + remainingCounters -= numCounters; + int min = Math.max(0, countersLeftToRemove - remainingCounters); + int max = Math.min(countersLeftToRemove, numCounters); + int toRemove = controller.getAmount(min, max, counterName + " counters to remove", game); + // Sanity check in case of GUI bugs/disconnects + toRemove = Math.max(toRemove, min); + toRemove = Math.min(toRemove, max); + permanent.removeCounters(counterName, toRemove, source, game); + countersLeftToRemove -= toRemove; + if (countersLeftToRemove <= 0) { + break; + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/c/CemeteryGatekeeper.java b/Mage.Sets/src/mage/cards/c/CemeteryGatekeeper.java new file mode 100644 index 00000000000..4bdcc579128 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CemeteryGatekeeper.java @@ -0,0 +1,169 @@ +package mage.cards.c; + +import java.util.List; +import java.util.UUID; +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.cards.Card; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.game.ExileZone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.stack.Spell; +import mage.players.Player; +import mage.target.common.TargetCardInGraveyard; +import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; + +/** + * + * @author weirddan455 + */ +public final class CemeteryGatekeeper extends CardImpl { + + public CemeteryGatekeeper(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); + + this.subtype.add(SubType.VAMPIRE); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // First strike + this.addAbility(FirstStrikeAbility.getInstance()); + + // When Cemetery Gatekeeper enters the battlefield, exile a card from a graveyard. + this.addAbility(new EntersBattlefieldTriggeredAbility(new CemeteryGatekeeperEffect())); + + // Whenever a player plays a land or casts a spell, if it shares a card type with the exiled card, Cemetery Gatekeeper deals 2 damage to that player. + this.addAbility(new CemeteryGatekeeperTriggeredAbility()); + } + + private CemeteryGatekeeper(final CemeteryGatekeeper card) { + super(card); + } + + @Override + public CemeteryGatekeeper copy() { + return new CemeteryGatekeeper(this); + } +} + +class CemeteryGatekeeperEffect extends OneShotEffect { + + public CemeteryGatekeeperEffect() { + super(Outcome.Exile); + staticText = "exile a card from a graveyard"; + } + + private CemeteryGatekeeperEffect(final CemeteryGatekeeperEffect effect) { + super(effect); + } + + @Override + public CemeteryGatekeeperEffect copy() { + return new CemeteryGatekeeperEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + TargetCardInGraveyard target = new TargetCardInGraveyard(); + target.setNotTarget(true); + controller.choose(outcome, target, source.getSourceId(), game); + Card card = game.getCard(target.getFirstTarget()); + if (card != null) { + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + MageObject sourceObject = source.getSourceObject(game); + String exileName = sourceObject == null ? null : sourceObject.getIdName(); + return controller.moveCardsToExile(card, source, game, true, exileId, exileName); + } + } + return false; + } +} + +class CemeteryGatekeeperTriggeredAbility extends TriggeredAbilityImpl { + + public CemeteryGatekeeperTriggeredAbility() { + super(Zone.BATTLEFIELD, new DamageTargetEffect(2, true, "that player")); + } + + private CemeteryGatekeeperTriggeredAbility(final CemeteryGatekeeperTriggeredAbility ability) { + super(ability); + } + + @Override + public CemeteryGatekeeperTriggeredAbility copy() { + return new CemeteryGatekeeperTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.LAND_PLAYED || event.getType() == GameEvent.EventType.SPELL_CAST; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Effect effect = getEffects().get(0); + effect.setTargetPointer(new FixedTarget(event.getPlayerId())); + effect.setValue("targetId", event.getTargetId()); + effect.setValue("eventType", event.getType()); + // sourceObjectZoneChangeCounter is not updated until after checkTrigger is called. Get ZCC from GameState instead. + effect.setValue("exileId", CardUtil.getExileZoneId(game, sourceId, game.getState().getZoneChangeCounter(sourceId))); + return true; + } + + private boolean checkCardTypes(List playedCardTypes, UUID exileId, Game game) { + ExileZone exileZone = game.getExile().getExileZone(exileId); + if (exileZone != null) { + for (UUID cardId : exileZone) { + Card card = game.getCard(cardId); + if (card != null) { + for (CardType exileCardType : card.getCardType(game)) { + if (playedCardTypes.contains(exileCardType)) { + return true; + } + } + } + } + } + return false; + } + + @Override + public boolean checkInterveningIfClause(Game game) { + Effect effect = getEffects().get(0); + UUID targetId = (UUID) effect.getValue("targetId"); + GameEvent.EventType eventType = (GameEvent.EventType) effect.getValue("eventType"); + UUID exileId = (UUID) effect.getValue("exileId"); + if (targetId != null && eventType != null && exileId != null) { + if (eventType == GameEvent.EventType.LAND_PLAYED) { + Permanent permanent = game.getPermanent(targetId); + return permanent != null && checkCardTypes(permanent.getCardType(game), exileId, game); + } else if (eventType == GameEvent.EventType.SPELL_CAST) { + Spell spell = game.getSpellOrLKIStack(targetId); + return spell != null && checkCardTypes(spell.getCardType(game), exileId, game); + } + } + return false; + } + + @Override + public String getTriggerPhrase() { + return "Whenever a player plays a land or casts a spell, if it shares a card type with the exiled card, "; + } +} diff --git a/Mage.Sets/src/mage/cards/c/CemeteryIlluminator.java b/Mage.Sets/src/mage/cards/c/CemeteryIlluminator.java new file mode 100644 index 00000000000..20d51cabb6a --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CemeteryIlluminator.java @@ -0,0 +1,185 @@ +package mage.cards.c; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +import mage.MageIdentifier; +import mage.MageInt; +import mage.MageObject; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldOrAttacksSourceTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.LookAtTopCardOfLibraryAnyTimeEffect; +import mage.cards.Card; +import mage.constants.*; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.game.ExileZone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCardInGraveyard; +import mage.util.CardUtil; +import mage.watchers.Watcher; + +/** + * + * @author weirddan455 + */ +public final class CemeteryIlluminator extends CardImpl { + + public CemeteryIlluminator(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{U}"); + + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever Cemetery Illuminator enters the battlefield or attacks, exile a card from a graveyard. + this.addAbility(new EntersBattlefieldOrAttacksSourceTriggeredAbility(new CemeteryIlluminatorExileEffect())); + + // You may look at the top card of your library any time. + this.addAbility(new SimpleStaticAbility(new LookAtTopCardOfLibraryAnyTimeEffect())); + + // Once each turn, you may cast a spell from the top of your library if it shares a card type with a card exiled with Cemetery Illuminator. + this.addAbility(new SimpleStaticAbility(new CemeteryIlluminatorPlayTopEffect()) + .setIdentifier(MageIdentifier.CemeteryIlluminatorWatcher), + new CemeteryIlluminatorWatcher()); + } + + private CemeteryIlluminator(final CemeteryIlluminator card) { + super(card); + } + + @Override + public CemeteryIlluminator copy() { + return new CemeteryIlluminator(this); + } +} + +class CemeteryIlluminatorExileEffect extends OneShotEffect { + + public CemeteryIlluminatorExileEffect() { + super(Outcome.Exile); + staticText = "exile a card from a graveyard"; + } + + private CemeteryIlluminatorExileEffect(final CemeteryIlluminatorExileEffect effect) { + super(effect); + } + + @Override + public CemeteryIlluminatorExileEffect copy() { + return new CemeteryIlluminatorExileEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + TargetCardInGraveyard target = new TargetCardInGraveyard(); + target.setNotTarget(true); + controller.choose(outcome, target, source.getSourceId(), game); + Card card = game.getCard(target.getFirstTarget()); + if (card != null) { + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + MageObject sourceObject = source.getSourceObject(game); + String exileName = sourceObject == null ? null : sourceObject.getIdName(); + return controller.moveCardsToExile(card, source, game, true, exileId, exileName); + } + } + return false; + } +} + +class CemeteryIlluminatorPlayTopEffect extends AsThoughEffectImpl { + + public CemeteryIlluminatorPlayTopEffect() { + super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.Benefit, true); + staticText = "Once each turn, you may cast a spell from the top of your library if it shares a card type with a card exiled with {this}"; + } + + private CemeteryIlluminatorPlayTopEffect(final CemeteryIlluminatorPlayTopEffect effect) { + super(effect); + } + + @Override + public CemeteryIlluminatorPlayTopEffect copy() { + return new CemeteryIlluminatorPlayTopEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + // Same checks as in PlayTheTopCardEffect + // Once per turn clause checked by Watcher same as Lurrus of the Dream Den + if (affectedControllerId.equals(source.getControllerId())) { + Player controller = game.getPlayer(source.getControllerId()); + CemeteryIlluminatorWatcher watcher = game.getState().getWatcher(CemeteryIlluminatorWatcher.class); + Permanent sourceObject = game.getPermanent(source.getSourceId()); + if (controller != null && watcher != null && sourceObject != null && !watcher.isAbilityUsed(new MageObjectReference(sourceObject, game))) { + Card card = game.getCard(objectId); + Card topCard = controller.getLibrary().getFromTop(game); + if (card != null && topCard != null && topCard.getId().equals(card.getMainCard().getId()) && !card.isLand(game) && !card.getManaCost().isEmpty()) { + // Check if it shares a card type with exiled cards + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), game.getState().getZoneChangeCounter(source.getSourceId())); + ExileZone exileZone = game.getExile().getExileZone(exileId); + if (exileZone != null) { + HashSet cardTypes = new HashSet<>(card.getCardType(game)); + for (UUID exileCardId : exileZone) { + Card exileCard = game.getCard(exileCardId); + if (exileCard != null) { + for (CardType exileType : exileCard.getCardType(game)) { + if (cardTypes.contains(exileType)) { + return true; + } + } + } + } + } + } + } + } + return false; + } +} + +class CemeteryIlluminatorWatcher extends Watcher { + + private final Set usedFrom = new HashSet<>(); + + public CemeteryIlluminatorWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.SPELL_CAST + && event.hasApprovingIdentifier(MageIdentifier.CemeteryIlluminatorWatcher)) { + usedFrom.add(event.getAdditionalReference().getApprovingMageObjectReference()); + } + } + + @Override + public void reset() { + super.reset(); + usedFrom.clear(); + } + + public boolean isAbilityUsed(MageObjectReference mor) { + return usedFrom.contains(mor); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CemeteryProtector.java b/Mage.Sets/src/mage/cards/c/CemeteryProtector.java new file mode 100644 index 00000000000..79cfa40752a --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CemeteryProtector.java @@ -0,0 +1,172 @@ +package mage.cards.c; + +import java.util.List; +import java.util.UUID; +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.cards.Card; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.abilities.keyword.FlashAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.game.ExileZone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.HumanToken; +import mage.game.stack.Spell; +import mage.players.Player; +import mage.target.common.TargetCardInGraveyard; +import mage.util.CardUtil; + +/** + * + * @author weirddan455 + */ +public final class CemeteryProtector extends CardImpl { + + public CemeteryProtector(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // When Cemetery Protector enters the battlefield, exile a card from a graveyard. + this.addAbility(new EntersBattlefieldTriggeredAbility(new CemeteryProtectorEffect())); + + // Whenever you play a land or cast a spell, if it shares a card type with the exiled card, create a 1/1 white Human creature token. + this.addAbility(new CemeteryProtectorTriggeredAbility()); + } + + private CemeteryProtector(final CemeteryProtector card) { + super(card); + } + + @Override + public CemeteryProtector copy() { + return new CemeteryProtector(this); + } +} + +class CemeteryProtectorEffect extends OneShotEffect { + + public CemeteryProtectorEffect() { + super(Outcome.Exile); + staticText = "exile a card from a graveyard"; + } + + private CemeteryProtectorEffect(final CemeteryProtectorEffect effect) { + super(effect); + } + + @Override + public CemeteryProtectorEffect copy() { + return new CemeteryProtectorEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + TargetCardInGraveyard target = new TargetCardInGraveyard(); + target.setNotTarget(true); + controller.choose(outcome, target, source.getSourceId(), game); + Card card = game.getCard(target.getFirstTarget()); + if (card != null) { + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + MageObject sourceObject = source.getSourceObject(game); + String exileName = sourceObject == null ? null : sourceObject.getIdName(); + return controller.moveCardsToExile(card, source, game, true, exileId, exileName); + } + } + return false; + } +} + +class CemeteryProtectorTriggeredAbility extends TriggeredAbilityImpl { + + public CemeteryProtectorTriggeredAbility() { + super(Zone.BATTLEFIELD, new CreateTokenEffect(new HumanToken())); + } + + private CemeteryProtectorTriggeredAbility(final CemeteryProtectorTriggeredAbility ability) { + super(ability); + } + + @Override + public CemeteryProtectorTriggeredAbility copy() { + return new CemeteryProtectorTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.LAND_PLAYED || event.getType() == GameEvent.EventType.SPELL_CAST; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (event.getPlayerId().equals(controllerId)) { + Effect effect = getEffects().get(0); + effect.setValue("targetId", event.getTargetId()); + effect.setValue("eventType", event.getType()); + // sourceObjectZoneChangeCounter is not updated until after checkTrigger is called. Get ZCC from GameState instead. + effect.setValue("exileId", CardUtil.getExileZoneId(game, sourceId, game.getState().getZoneChangeCounter(sourceId))); + return true; + } + return false; + } + + private boolean checkCardTypes(List playedCardTypes, UUID exileId, Game game) { + ExileZone exileZone = game.getExile().getExileZone(exileId); + if (exileZone != null) { + for (UUID cardId : exileZone) { + Card card = game.getCard(cardId); + if (card != null) { + for (CardType exileCardType : card.getCardType(game)) { + if (playedCardTypes.contains(exileCardType)) { + return true; + } + } + } + } + } + return false; + } + + @Override + public boolean checkInterveningIfClause(Game game) { + Effect effect = getEffects().get(0); + UUID targetId = (UUID) effect.getValue("targetId"); + GameEvent.EventType eventType = (GameEvent.EventType) effect.getValue("eventType"); + UUID exileId = (UUID) effect.getValue("exileId"); + if (targetId != null && eventType != null && exileId != null) { + if (eventType == GameEvent.EventType.LAND_PLAYED) { + Permanent permanent = game.getPermanent(targetId); + return permanent != null && checkCardTypes(permanent.getCardType(game), exileId, game); + } else if (eventType == GameEvent.EventType.SPELL_CAST) { + Spell spell = game.getSpellOrLKIStack(targetId); + return spell != null && checkCardTypes(spell.getCardType(game), exileId, game); + } + } + return false; + } + + @Override + public String getTriggerPhrase() { + return "Whenever you play a land or cast a spell, if it shares a card type with the exiled card, "; + } +} diff --git a/Mage.Sets/src/mage/cards/c/CemeteryProwler.java b/Mage.Sets/src/mage/cards/c/CemeteryProwler.java new file mode 100644 index 00000000000..d70f7aafbe4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CemeteryProwler.java @@ -0,0 +1,142 @@ +package mage.cards.c; + +import java.util.HashSet; +import java.util.UUID; +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.common.EntersBattlefieldOrAttacksSourceTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.cards.Card; +import mage.constants.*; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.game.ExileZone; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInGraveyard; +import mage.util.CardUtil; + +/** + * + * @author weirddan455 + */ +public final class CemeteryProwler extends CardImpl { + + public CemeteryProwler(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}{G}"); + + this.subtype.add(SubType.WOLF); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // Whenever Cemetery Prowler enters the battlefield or attacks, exile a card from a graveyard. + this.addAbility(new EntersBattlefieldOrAttacksSourceTriggeredAbility(new CemeteryProwlerExileEffect())); + + // Spells you cast cost {1} less to cast for each card type they share with cards exiled with Cemetery Prowler. + this.addAbility(new SimpleStaticAbility(new CemeteryProwlerCostReductionEffect())); + } + + private CemeteryProwler(final CemeteryProwler card) { + super(card); + } + + @Override + public CemeteryProwler copy() { + return new CemeteryProwler(this); + } +} + +class CemeteryProwlerExileEffect extends OneShotEffect { + + public CemeteryProwlerExileEffect() { + super(Outcome.Exile); + staticText = "exile a card from a graveyard"; + } + + private CemeteryProwlerExileEffect(final CemeteryProwlerExileEffect effect) { + super(effect); + } + + @Override + public CemeteryProwlerExileEffect copy() { + return new CemeteryProwlerExileEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + TargetCardInGraveyard target = new TargetCardInGraveyard(); + target.setNotTarget(true); + controller.choose(outcome, target, source.getSourceId(), game); + Card card = game.getCard(target.getFirstTarget()); + if (card != null) { + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + MageObject sourceObject = source.getSourceObject(game); + String exileName = sourceObject == null ? null : sourceObject.getIdName(); + return controller.moveCardsToExile(card, source, game, true, exileId, exileName); + } + } + return false; + } +} + +class CemeteryProwlerCostReductionEffect extends CostModificationEffectImpl { + + public CemeteryProwlerCostReductionEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.REDUCE_COST); + staticText = "Spells you cast cost {1} less to cast for each card type they share with cards exiled with {this}"; + } + + private CemeteryProwlerCostReductionEffect(final CemeteryProwlerCostReductionEffect effect) { + super(effect); + } + + @Override + public CemeteryProwlerCostReductionEffect copy() { + return new CemeteryProwlerCostReductionEffect(this); + } + + @Override + public boolean apply(Game game, Ability source, Ability abilityToModify) { + if (abilityToModify instanceof SpellAbility) { + // sourceObjectZoneChangeCounter is not working here. Getting it from GameState works. + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), game.getState().getZoneChangeCounter(source.getSourceId())); + ExileZone exileZone = game.getExile().getExileZone(exileId); + Card castCard = ((SpellAbility) abilityToModify).getCharacteristics(game); + if (exileZone != null && castCard != null) { + HashSet cardTypes = new HashSet<>(); + for (UUID cardId : exileZone) { + Card card = game.getCard(cardId); + if (card != null) { + cardTypes.addAll(card.getCardType(game)); + } + } + int sharedTypes = 0; + for (CardType type : castCard.getCardType(game)) { + if (cardTypes.contains(type)) { + sharedTypes++; + } + } + if (sharedTypes > 0) { + CardUtil.reduceCost(abilityToModify, sharedTypes); + return true; + } + } + } + return false; + } + + @Override + public boolean applies(Ability abilityToModify, Ability source, Game game) { + return abilityToModify instanceof SpellAbility && abilityToModify.isControlledBy(source.getControllerId()); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CemeteryPuca.java b/Mage.Sets/src/mage/cards/c/CemeteryPuca.java index ebd596616a0..2950b2e4c8f 100644 --- a/Mage.Sets/src/mage/cards/c/CemeteryPuca.java +++ b/Mage.Sets/src/mage/cards/c/CemeteryPuca.java @@ -15,6 +15,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Outcome; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; @@ -35,8 +36,11 @@ public final class CemeteryPuca extends CardImpl { this.toughness = new MageInt(2); // Whenever a creature dies, you may pay {1}. If you do, Cemetery Puca becomes a copy of that creature, except it has this ability. - this.addAbility(new DiesCreatureTriggeredAbility(new DoIfCostPaid(new CemeteryPucaEffect(), new ManaCostsImpl("{1}")), false, new FilterCreaturePermanent("a creature"), true)); - + this.addAbility(new DiesCreatureTriggeredAbility( + new DoIfCostPaid(new CemeteryPucaEffect(), new ManaCostsImpl("{1}")), + false, + StaticFilters.FILTER_PERMANENT_A_CREATURE, + true)); } private CemeteryPuca(final CemeteryPuca card) { @@ -72,8 +76,8 @@ class CemeteryPucaEffect extends OneShotEffect { Permanent copyFromCreature = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (copyFromCreature != null) { game.copyPermanent(Duration.WhileOnBattlefield, copyFromCreature, copyToCreature.getId(), source, new EmptyCopyApplier()); - ContinuousEffect effect = new GainAbilityTargetEffect(new DiesCreatureTriggeredAbility(new DoIfCostPaid(new CemeteryPucaEffect(), new ManaCostsImpl("{1}")), false, new FilterCreaturePermanent("a creature"), true), Duration.WhileOnBattlefield); - effect.setTargetPointer(new FixedTarget(copyToCreature.getId())); + ContinuousEffect effect = new GainAbilityTargetEffect(new DiesCreatureTriggeredAbility(new DoIfCostPaid(new CemeteryPucaEffect(), new ManaCostsImpl("{1}")), false, StaticFilters.FILTER_PERMANENT_A_CREATURE, true), Duration.WhileOnBattlefield); + effect.setTargetPointer(new FixedTarget(copyToCreature.getId(), game)); game.addEffect(effect, source); return true; } diff --git a/Mage.Sets/src/mage/cards/c/CennsHeir.java b/Mage.Sets/src/mage/cards/c/CennsHeir.java index 4c7973f4ec3..1da1a3345a9 100644 --- a/Mage.Sets/src/mage/cards/c/CennsHeir.java +++ b/Mage.Sets/src/mage/cards/c/CennsHeir.java @@ -1,9 +1,9 @@ - package mage.cards.c; 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.effects.common.continuous.BoostSourceEffect; import mage.cards.CardImpl; @@ -27,6 +27,8 @@ public final class CennsHeir extends CardImpl { filter.add(AnotherPredicate.instance); } + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter); + public CennsHeir(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{W}"); this.subtype.add(SubType.KITHKIN); @@ -35,8 +37,7 @@ public final class CennsHeir extends CardImpl { this.toughness = new MageInt(1); // Whenever Cenn's Heir attacks, it gets +1/+1 until end of turn for each other attacking Kithkin. - PermanentsOnBattlefieldCount count = new PermanentsOnBattlefieldCount(filter); - this.addAbility(new AttacksTriggeredAbility(new BoostSourceEffect(count, count, Duration.EndOfTurn, true), false)); + this.addAbility(new AttacksTriggeredAbility(new BoostSourceEffect(xValue, xValue, Duration.EndOfTurn, true, "it"), false)); } private CennsHeir(final CennsHeir card) { diff --git a/Mage.Sets/src/mage/cards/c/CennsTactician.java b/Mage.Sets/src/mage/cards/c/CennsTactician.java index 04ee6512e51..9f5c1305160 100644 --- a/Mage.Sets/src/mage/cards/c/CennsTactician.java +++ b/Mage.Sets/src/mage/cards/c/CennsTactician.java @@ -14,7 +14,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.counters.CounterType; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.target.common.TargetCreaturePermanent; @@ -25,10 +25,8 @@ import mage.target.common.TargetCreaturePermanent; public final class CennsTactician extends CardImpl { private static final FilterCreaturePermanent filterSoldier = new FilterCreaturePermanent("Soldier creature"); - private static final FilterControlledCreaturePermanent filterCounter = new FilterControlledCreaturePermanent("Each creature you control with a +1/+1 counter on it"); static { filterSoldier.add(SubType.SOLDIER.getPredicate()); - filterCounter.add(CounterType.P1P1.getPredicate()); } public CennsTactician(UUID ownerId, CardSetInfo setInfo) { @@ -45,7 +43,14 @@ public final class CennsTactician extends CardImpl { this.addAbility(ability); // Each creature you control with a +1/+1 counter on it can block an additional creature each combat. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CanBlockAdditionalCreatureAllEffect(1, filterCounter, Duration.WhileOnBattlefield))); + this.addAbility(new SimpleStaticAbility( + Zone.BATTLEFIELD, + new CanBlockAdditionalCreatureAllEffect( + 1, + StaticFilters.FILTER_EACH_CONTROLLED_CREATURE_P1P1, + Duration.WhileOnBattlefield) + ) + ); } private CennsTactician(final CennsTactician card) { diff --git a/Mage.Sets/src/mage/cards/c/CephalidSnitch.java b/Mage.Sets/src/mage/cards/c/CephalidSnitch.java index 7ac194a5eef..a2be468199e 100644 --- a/Mage.Sets/src/mage/cards/c/CephalidSnitch.java +++ b/Mage.Sets/src/mage/cards/c/CephalidSnitch.java @@ -1,4 +1,3 @@ - package mage.cards.c; import java.util.*; @@ -65,21 +64,9 @@ class CephalidSnitchEffect extends LoseAbilityTargetEffect{ return new CephalidSnitchEffect(this); } - public String filterNameAssembler(List unsortedColors) { - //Order colors properly by WUBRG (skipping black of course) and construct string to be displayed for ability. - List colors = new ArrayList<>(); - if(unsortedColors.contains(ObjectColor.WHITE)){ - colors.add(ObjectColor.WHITE); - } - if(unsortedColors.contains(ObjectColor.BLUE)){ - colors.add(ObjectColor.BLUE); - } - if(unsortedColors.contains(ObjectColor.RED)){ - colors.add(ObjectColor.RED); - } - if(unsortedColors.contains(ObjectColor.GREEN)){ - colors.add(ObjectColor.GREEN); - } + private static final ObjectColor NONBLACK = new ObjectColor("WURG"); + + private static String filterNameAssembler(List colors) { if (colors.size() == 1) { return colors.get(0).getDescription(); } else if (colors.size() == 2) { @@ -87,7 +74,7 @@ class CephalidSnitchEffect extends LoseAbilityTargetEffect{ } else if (colors.size() == 3) { return colors.get(0).getDescription() + ", from " + colors.get(1).getDescription() + " and from " + colors.get(2).getDescription(); } else if (colors.size() == 4) { - return colors.get(0).getDescription() + ", from " + colors.get(1).getDescription() + ", from " + colors.get(2).getDescription() + " and from " + colors.get(3).getDescription(); + return colors.get(0).getDescription() + ", from " + colors.get(1).getDescription() + ", from " + colors.get(2).getDescription() + " and from " + colors.get(3).getDescription(); } return ""; } @@ -96,31 +83,33 @@ class CephalidSnitchEffect extends LoseAbilityTargetEffect{ public boolean apply(Game game, Ability source) { Permanent targetCreature = game.getPermanent(targetPointer.getFirst(game, source)); if (targetCreature != null) { - - //Go through protection abilities and sort out any containing black, then record the colors other than black - for(ProtectionAbility a: targetCreature.getAbilities().getProtectionAbilities()) { - List objectColors = new ArrayList<>(); - if(a.getColors().contains(ObjectColor.BLACK)) - for (ObjectColor o : a.getColors()) { - if (!objectColors.contains(o) && !o.isBlack()) - objectColors.add(o); + List toRemove = new ArrayList<>(); + //Go through protection abilities and sort out any containing black + for (ProtectionAbility a: targetCreature.getAbilities().getProtectionAbilities()) { + ObjectColor colors = a.getFromColor(); + if (colors.isBlack()) { + List nonBlackColors = colors.intersection(NONBLACK).getColors(); + if (!nonBlackColors.isEmpty()) { + //If the ability is protection from multiple colors, construct a new filter excluding black + FilterCard filter = new FilterCard(filterNameAssembler(nonBlackColors)); + if (nonBlackColors.size() == 1) + filter.add(new ColorPredicate(nonBlackColors.get(0))); + else if (nonBlackColors.size() == 2) + filter.add(Predicates.or(new ColorPredicate(nonBlackColors.get(0)), new ColorPredicate(nonBlackColors.get(1)))); + else if (nonBlackColors.size() == 3) + filter.add(Predicates.or(new ColorPredicate(nonBlackColors.get(0)), new ColorPredicate(nonBlackColors.get(1)), new ColorPredicate(nonBlackColors.get(2)))); + else if (nonBlackColors.size() == 4) + filter.add(Predicates.or(new ColorPredicate(nonBlackColors.get(0)), new ColorPredicate(nonBlackColors.get(1)), new ColorPredicate(nonBlackColors.get(2)), new ColorPredicate(nonBlackColors.get(3)))); + a.setFilter(filter); + } else { + //if the ability is just protection from black, remove it from the creature + toRemove.add(a); } - //Construct a card filter excluding black - if(!objectColors.isEmpty()) { - FilterCard filter = new FilterCard(filterNameAssembler(objectColors)); - if (objectColors.size() == 1) - filter.add(new ColorPredicate(objectColors.get(0))); - else if (objectColors.size() == 2) - filter.add(Predicates.or(new ColorPredicate(objectColors.get(0)), new ColorPredicate(objectColors.get(1)))); - else if (objectColors.size() == 3) - filter.add(Predicates.or(new ColorPredicate(objectColors.get(0)), new ColorPredicate(objectColors.get(1)), new ColorPredicate(objectColors.get(2)))); - else if (objectColors.size() == 4) - filter.add(Predicates.or(new ColorPredicate(objectColors.get(0)), new ColorPredicate(objectColors.get(1)), new ColorPredicate(objectColors.get(2)), new ColorPredicate(objectColors.get(3)))); - a.setFilter(filter); } } + targetCreature.removeAbilities(toRemove, source.getSourceId(), game); return true; } return false; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/c/CeremonialKnife.java b/Mage.Sets/src/mage/cards/c/CeremonialKnife.java new file mode 100644 index 00000000000..97480d54566 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CeremonialKnife.java @@ -0,0 +1,50 @@ +package mage.cards.c; + +import mage.abilities.Ability; +import mage.abilities.common.DealsCombatDamageTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.continuous.BoostEquippedEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.EquipAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.BloodToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CeremonialKnife extends CardImpl { + + public CeremonialKnife(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}"); + + this.subtype.add(SubType.EQUIPMENT); + + // Equipped creature gets +1/+0 and has "Whenever this creature deals combat damage, create a Blood token." + Ability ability = new SimpleStaticAbility(new BoostEquippedEffect(1, 0)); + ability.addEffect(new GainAbilityAttachedEffect( + new DealsCombatDamageTriggeredAbility( + new CreateTokenEffect(new BloodToken()), false + ).setTriggerPhrase("Whenever this creature deals combat damage, "), AttachmentType.AURA + ).setText("and has \"Whenever this creature deals combat damage, create a Blood token.\"")); + this.addAbility(ability); + + // Equip {2} + this.addAbility(new EquipAbility(2)); + } + + private CeremonialKnife(final CeremonialKnife card) { + super(card); + } + + @Override + public CeremonialKnife copy() { + return new CeremonialKnife(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CeruleanWisps.java b/Mage.Sets/src/mage/cards/c/CeruleanWisps.java index 802ef4b869d..3cc5469f800 100644 --- a/Mage.Sets/src/mage/cards/c/CeruleanWisps.java +++ b/Mage.Sets/src/mage/cards/c/CeruleanWisps.java @@ -25,7 +25,7 @@ public final class CeruleanWisps extends CardImpl { // Target creature becomes blue until end of turn. Untap that creature. this.getSpellAbility().addTarget(new TargetCreaturePermanent()); this.getSpellAbility().addEffect(new BecomesColorTargetEffect(ObjectColor.BLUE, Duration.EndOfTurn)); - this.getSpellAbility().addEffect(new UntapTargetEffect()); + this.getSpellAbility().addEffect(new UntapTargetEffect().setText("Untap that creature")); // Draw a card. this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); diff --git a/Mage.Sets/src/mage/cards/c/ChainOfAcid.java b/Mage.Sets/src/mage/cards/c/ChainOfAcid.java index 30383dc46d1..d71b8c3b7cb 100644 --- a/Mage.Sets/src/mage/cards/c/ChainOfAcid.java +++ b/Mage.Sets/src/mage/cards/c/ChainOfAcid.java @@ -75,7 +75,7 @@ class ChainOfAcidEffect extends OneShotEffect { Player affectedPlayer = game.getPlayer(permanent.getControllerId()); if (affectedPlayer != null) { Effect effect = new DestroyTargetEffect(); - effect.setTargetPointer(new FixedTarget(targetId)); + effect.setTargetPointer(new FixedTarget(targetId, game)); effect.apply(game, source); if (affectedPlayer.chooseUse(Outcome.Copy, "Copy the spell?", source, game)) { Spell spell = game.getStack().getSpell(source.getSourceId()); diff --git a/Mage.Sets/src/mage/cards/c/ChainOfSmog.java b/Mage.Sets/src/mage/cards/c/ChainOfSmog.java index f30f27f4540..afc499a90aa 100644 --- a/Mage.Sets/src/mage/cards/c/ChainOfSmog.java +++ b/Mage.Sets/src/mage/cards/c/ChainOfSmog.java @@ -64,7 +64,7 @@ class ChainOfSmogEffect extends OneShotEffect { Player affectedPlayer = game.getPlayer(targetId); if (affectedPlayer != null) { Effect effect = new DiscardTargetEffect(2); - effect.setTargetPointer(new FixedTarget(targetId)); + effect.setTargetPointer(new FixedTarget(targetId, game)); effect.apply(game, source); if (affectedPlayer.chooseUse(Outcome.Copy, "Copy the spell?", source, game)) { Spell spell = game.getStack().getSpell(source.getSourceId()); diff --git a/Mage.Sets/src/mage/cards/c/ChainStasis.java b/Mage.Sets/src/mage/cards/c/ChainStasis.java index a16edebb9a7..baeceb3c13b 100644 --- a/Mage.Sets/src/mage/cards/c/ChainStasis.java +++ b/Mage.Sets/src/mage/cards/c/ChainStasis.java @@ -70,7 +70,7 @@ class ChainStasisEffect extends OneShotEffect { Permanent permanent = game.getPermanent(source.getFirstTarget()); if (permanent != null) { Effect effect = new MayTapOrUntapTargetEffect(); - effect.setTargetPointer(new FixedTarget(source.getFirstTarget())); + effect.setTargetPointer(new FixedTarget(source.getFirstTarget(), game)); effect.apply(game, source); Player player = game.getPlayer(permanent.getControllerId()); if (player == null) { diff --git a/Mage.Sets/src/mage/cards/c/ChainedToTheRocks.java b/Mage.Sets/src/mage/cards/c/ChainedToTheRocks.java index b91e0b464e8..8d5f55731be 100644 --- a/Mage.Sets/src/mage/cards/c/ChainedToTheRocks.java +++ b/Mage.Sets/src/mage/cards/c/ChainedToTheRocks.java @@ -15,9 +15,8 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.TargetController; +import mage.filter.StaticFilters; import mage.filter.common.FilterControlledLandPermanent; -import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; @@ -62,11 +61,9 @@ import mage.util.CardUtil; public final class ChainedToTheRocks extends CardImpl { private static final FilterControlledLandPermanent filter = new FilterControlledLandPermanent("Mountain you control"); - private static final FilterCreaturePermanent filterTarget = new FilterCreaturePermanent("creature an opponent controls"); static { filter.add(SubType.MOUNTAIN.getPredicate()); - filterTarget.add(TargetController.OPPONENT.getControllerPredicate()); } public ChainedToTheRocks(UUID ownerId, CardSetInfo setInfo) { @@ -82,7 +79,7 @@ public final class ChainedToTheRocks extends CardImpl { // When Chained to the Rocks enters the battlefield, exile target creature an opponent controls until Chained to the Rocks leaves the battlefield. (That creature returns under its owner's control.) ability = new EntersBattlefieldTriggeredAbility(new ChainedToTheRocksEffect()); - ability.addTarget(new TargetCreaturePermanent(filterTarget)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); ability.addEffect(new CreateDelayedTriggeredAbilityEffect(new OnLeaveReturnExiledToBattlefieldAbility())); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/c/ChainerNightmareAdept.java b/Mage.Sets/src/mage/cards/c/ChainerNightmareAdept.java index ee2627a4821..e478aca4173 100644 --- a/Mage.Sets/src/mage/cards/c/ChainerNightmareAdept.java +++ b/Mage.Sets/src/mage/cards/c/ChainerNightmareAdept.java @@ -18,6 +18,7 @@ import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.Game; import mage.game.events.GameEvent; +import mage.util.CardUtil; import mage.watchers.Watcher; import mage.watchers.common.CastFromHandWatcher; @@ -147,7 +148,7 @@ class ChainerNightmareAdeptWatcher extends Watcher { source.getSourceId(), source.getSourceObjectZoneChangeCounter(), game ); morMap.computeIfAbsent(mor, m -> new HashMap<>()) - .compute(source.getControllerId(), (u, i) -> i == null ? 1 : Integer.sum(i, 1)); + .compute(source.getControllerId(), CardUtil::setOrIncrementValue); } } diff --git a/Mage.Sets/src/mage/cards/c/ChainersTorment.java b/Mage.Sets/src/mage/cards/c/ChainersTorment.java index b66ac36a6e7..995043bd96e 100644 --- a/Mage.Sets/src/mage/cards/c/ChainersTorment.java +++ b/Mage.Sets/src/mage/cards/c/ChainersTorment.java @@ -28,7 +28,7 @@ public final class ChainersTorment extends CardImpl { this.subtype.add(SubType.SAGA); // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) - SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); + SagaAbility sagaAbility = new SagaAbility(this); // I, II — Chainer's Torment deals 2 damage to each opponent and you gain 2 life. Effects effects = new Effects(); diff --git a/Mage.Sets/src/mage/cards/c/ChainflailCentipede.java b/Mage.Sets/src/mage/cards/c/ChainflailCentipede.java new file mode 100644 index 00000000000..f3c01f8212f --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ChainflailCentipede.java @@ -0,0 +1,93 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +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.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ChainflailCentipede extends CardImpl { + + public ChainflailCentipede(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{2}{B}"); + + this.subtype.add(SubType.EQUIPMENT); + this.subtype.add(SubType.INSECT); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Whenever Chainflail Centipede or equipped creature attacks, it gets +2/+0 until end of turn. + this.addAbility(new ChainflailCentipedeTriggeredAbility()); + + // Reconfigure {2} + this.addAbility(new ReconfigureAbility("{2}")); + } + + private ChainflailCentipede(final ChainflailCentipede card) { + super(card); + } + + @Override + public ChainflailCentipede copy() { + return new ChainflailCentipede(this); + } +} + +class ChainflailCentipedeTriggeredAbility extends TriggeredAbilityImpl { + + ChainflailCentipedeTriggeredAbility() { + super(Zone.BATTLEFIELD, new BoostTargetEffect(2, 0)); + } + + private ChainflailCentipedeTriggeredAbility(final ChainflailCentipedeTriggeredAbility ability) { + super(ability); + } + + @Override + public ChainflailCentipedeTriggeredAbility copy() { + return new ChainflailCentipedeTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DECLARED_ATTACKERS; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + UUID attacker; + if (!game.getCombat().getAttackers().contains(getSourceId())) { + Permanent permanent = getSourcePermanentOrLKI(game); + if (permanent != null && game.getCombat().getAttackers().contains(permanent.getAttachedTo())) { + attacker = permanent.getAttachedTo(); + } else { + attacker = null; + } + } else { + attacker = getSourceId(); + } + if (attacker == null) { + return false; + } + getEffects().setTargetPointer(new FixedTarget(attacker, game)); + return true; + } + + @Override + public String getRule() { + return "Whenever {this} or equipped creature attacks, it gets +2/+0 until end of turn."; + } +} diff --git a/Mage.Sets/src/mage/cards/c/ChaliceOfLife.java b/Mage.Sets/src/mage/cards/c/ChaliceOfLife.java index 50e16626431..b9a4a4768a8 100644 --- a/Mage.Sets/src/mage/cards/c/ChaliceOfLife.java +++ b/Mage.Sets/src/mage/cards/c/ChaliceOfLife.java @@ -25,7 +25,6 @@ public final class ChaliceOfLife extends CardImpl { public ChaliceOfLife(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}"); - this.transformable = true; this.secondSideCardClazz = ChaliceOfDeath.class; this.addAbility(new TransformAbility()); @@ -66,8 +65,7 @@ class ChaliceOfLifeEffect extends OneShotEffect { // if you have at least 10 life more than your starting life total, transform Chalice of Life. if (player.getLife() >= game.getStartingLife() + 10) { - permanent.transform(game); - game.informPlayers(permanent.getName() + " transforms into " + permanent.getSecondCardFace().getName()); + permanent.transform(source, game); } } } diff --git a/Mage.Sets/src/mage/cards/c/ChampionOfDusk.java b/Mage.Sets/src/mage/cards/c/ChampionOfDusk.java index 7a39738d1fa..2deaf20a988 100644 --- a/Mage.Sets/src/mage/cards/c/ChampionOfDusk.java +++ b/Mage.Sets/src/mage/cards/c/ChampionOfDusk.java @@ -1,4 +1,3 @@ - package mage.cards.c; import mage.MageInt; @@ -8,6 +7,8 @@ import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.LoseLifeSourceControllerEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -17,7 +18,6 @@ import mage.filter.common.FilterControlledPermanent; import java.util.UUID; /** - * * @author L_J */ public final class ChampionOfDusk extends CardImpl { @@ -28,6 +28,9 @@ public final class ChampionOfDusk extends CardImpl { filter.add(SubType.VAMPIRE.getPredicate()); } + private static final DynamicValue xCount = new PermanentsOnBattlefieldCount(filter); + private static final Hint hint = new ValueHint("Vampires you control", xCount); + public ChampionOfDusk(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{B}"); this.subtype.add(SubType.VAMPIRE); @@ -36,10 +39,12 @@ public final class ChampionOfDusk extends CardImpl { this.toughness = new MageInt(4); // When Champion of Dusk enters the battlefield, you draw X cards and you lose X life, where X is the number of Vampires you control. - DynamicValue xCount = new PermanentsOnBattlefieldCount(filter); - Ability ability = new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(xCount)); - ability.addEffect(new LoseLifeSourceControllerEffect(xCount)); - this.addAbility(ability); + Ability ability = new EntersBattlefieldTriggeredAbility( + new DrawCardSourceControllerEffect(xCount).setText("you draw X cards") + ); + ability.addEffect(new LoseLifeSourceControllerEffect(xCount) + .setText("and you lose X life, where X is the number of Vampires you control")); + this.addAbility(ability.addHint(hint)); } private ChampionOfDusk(final ChampionOfDusk card) { diff --git a/Mage.Sets/src/mage/cards/c/ChampionOfStraySouls.java b/Mage.Sets/src/mage/cards/c/ChampionOfStraySouls.java index be43b08456e..e6a66d2b748 100644 --- a/Mage.Sets/src/mage/cards/c/ChampionOfStraySouls.java +++ b/Mage.Sets/src/mage/cards/c/ChampionOfStraySouls.java @@ -17,7 +17,6 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.common.FilterCreatureCard; import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.target.common.TargetCardInYourGraveyard; @@ -84,7 +83,7 @@ enum ChampionOfStraySoulsAdjuster implements TargetAdjuster { if (effect instanceof ReturnFromGraveyardToBattlefieldTargetEffect) { int xValue = GetXValue.instance.calculate(game, ability, null); ability.getTargets().clear(); - ability.addTarget(new TargetCardInYourGraveyard(xValue, xValue, new FilterCreatureCard("creature cards from your graveyard"))); + ability.addTarget(new TargetCardInYourGraveyard(xValue, xValue, StaticFilters.FILTER_CARD_CREATURES_YOUR_GRAVEYARD)); } } } diff --git a/Mage.Sets/src/mage/cards/c/ChandraAblaze.java b/Mage.Sets/src/mage/cards/c/ChandraAblaze.java index 2e665564dff..9dbc678a2da 100644 --- a/Mage.Sets/src/mage/cards/c/ChandraAblaze.java +++ b/Mage.Sets/src/mage/cards/c/ChandraAblaze.java @@ -3,7 +3,6 @@ package mage.cards.c; import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DrawCardAllEffect; @@ -39,7 +38,7 @@ public final class ChandraAblaze extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.CHANDRA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +1: Discard a card. If a red card is discarded this way, Chandra Ablaze deals 4 damage to any target. LoyaltyAbility ability = new LoyaltyAbility(new ChandraAblazeEffect1(), 1); diff --git a/Mage.Sets/src/mage/cards/c/ChandraAcolyteOfFlame.java b/Mage.Sets/src/mage/cards/c/ChandraAcolyteOfFlame.java index acce249973c..2ceeb02b127 100644 --- a/Mage.Sets/src/mage/cards/c/ChandraAcolyteOfFlame.java +++ b/Mage.Sets/src/mage/cards/c/ChandraAcolyteOfFlame.java @@ -3,7 +3,6 @@ package mage.cards.c; import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.effects.*; import mage.abilities.effects.common.InfoEffect; @@ -55,7 +54,7 @@ public final class ChandraAcolyteOfFlame extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.CHANDRA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // 0: Put a loyalty counter on each red planeswalker you control. this.addAbility(new LoyaltyAbility(new AddCountersAllEffect(CounterType.LOYALTY.createInstance(), filter), 0)); diff --git a/Mage.Sets/src/mage/cards/c/ChandraAwakenedInferno.java b/Mage.Sets/src/mage/cards/c/ChandraAwakenedInferno.java index a9781b1009a..d188189f6f1 100644 --- a/Mage.Sets/src/mage/cards/c/ChandraAwakenedInferno.java +++ b/Mage.Sets/src/mage/cards/c/ChandraAwakenedInferno.java @@ -3,7 +3,6 @@ package mage.cards.c; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.common.CantBeCounteredSourceAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.dynamicvalue.common.GetXLoyaltyValue; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageAllEffect; @@ -40,7 +39,7 @@ public final class ChandraAwakenedInferno extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.CHANDRA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(6)); + this.setStartingLoyalty(6); // This spell can't be countered. this.addAbility(new CantBeCounteredSourceAbility()); diff --git a/Mage.Sets/src/mage/cards/c/ChandraBoldPyromancer.java b/Mage.Sets/src/mage/cards/c/ChandraBoldPyromancer.java index 3687ef58e82..c48a05742d8 100644 --- a/Mage.Sets/src/mage/cards/c/ChandraBoldPyromancer.java +++ b/Mage.Sets/src/mage/cards/c/ChandraBoldPyromancer.java @@ -5,7 +5,6 @@ import java.util.UUID; import mage.Mana; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.Effects; import mage.abilities.effects.mana.BasicManaEffect; import mage.abilities.effects.common.DamageAllControlledTargetEffect; @@ -30,7 +29,7 @@ public final class ChandraBoldPyromancer extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.CHANDRA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +1: Add {R}{R}. Chandra, Bold Pyromancer deals 2 damage to target player. Ability ability = new LoyaltyAbility(new BasicManaEffect(Mana.RedMana(2)), +1); diff --git a/Mage.Sets/src/mage/cards/c/ChandraDressedToKill.java b/Mage.Sets/src/mage/cards/c/ChandraDressedToKill.java new file mode 100644 index 00000000000..be5a74be307 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ChandraDressedToKill.java @@ -0,0 +1,174 @@ +package mage.cards.c; + +import java.util.Set; +import java.util.UUID; + +import mage.MageObject; +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.LoyaltyAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.GetEmblemEffect; +import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; +import mage.abilities.effects.mana.AddManaToManaPoolSourceControllerEffect; +import mage.cards.Card; +import mage.constants.*; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.filter.common.FilterPlayerOrPlaneswalker; +import mage.game.Game; +import mage.game.command.emblems.ChandraDressedToKillEmblem; +import mage.players.Player; +import mage.target.common.TargetPlayerOrPlaneswalker; +import mage.target.targetpointer.FixedTarget; +import mage.target.targetpointer.FixedTargets; +import mage.util.CardUtil; + +/** + * + * @author weirddan455 + */ +public final class ChandraDressedToKill extends CardImpl { + + private static final FilterPlayerOrPlaneswalker filter = new FilterPlayerOrPlaneswalker(); + + public ChandraDressedToKill(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{1}{R}{R}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.CHANDRA); + this.setStartingLoyalty(3); + + // +1: Add {R}. Chandra, Dressed to Kill deals 1 damage to up to one target player or planeswalker. + Ability ability = new LoyaltyAbility(new AddManaToManaPoolSourceControllerEffect(new Mana(ManaType.RED, 1)), 1); + ability.addEffect(new DamageTargetEffect(1)); + ability.addTarget(new TargetPlayerOrPlaneswalker(0, 1, filter, false)); + this.addAbility(ability); + + // +1: Exile the top card of your library. If it's red, you may cast it this turn. + this.addAbility(new LoyaltyAbility(new ChandraDressedToKillExile1Effect(), 1)); + + // −7: Exile the top five cards of your library. You may cast red spells from among them this turn. + // You get an emblem with "Whenever you cast a red spell, this emblem deals X damage to any target, where X is the amount of mana spent to cast that spell." + ability = new LoyaltyAbility(new ChandraDressedToKillExile5Effect(), -7); + ability.addEffect(new GetEmblemEffect(new ChandraDressedToKillEmblem())); + this.addAbility(ability); + } + + private ChandraDressedToKill(final ChandraDressedToKill card) { + super(card); + } + + @Override + public ChandraDressedToKill copy() { + return new ChandraDressedToKill(this); + } +} + +// +1 and -7 need to be different abilities. +// The +1 ability checks if the card is red as it resolves. The -7 checks if it's red at the time you would cast it. +// Ex. Playing a Painter's Servant on red after the ability resolves. +// Ex. MDFCs like Mila, Crafty Companion. Back half cannot be played from the +1 but it can from the -7. +class ChandraDressedToKillExile1Effect extends OneShotEffect { + + public ChandraDressedToKillExile1Effect() { + super(Outcome.Benefit); + staticText = "Exile the top card of your library. If it's red, you may cast it this turn"; + } + + private ChandraDressedToKillExile1Effect(final ChandraDressedToKillExile1Effect effect) { + super(effect); + } + + @Override + public ChandraDressedToKillExile1Effect copy() { + return new ChandraDressedToKillExile1Effect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + Card card = controller.getLibrary().getFromTop(game); + if (card == null) { + return false; + } + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + MageObject sourceObject = source.getSourceObject(game); + String exileName = sourceObject == null ? null : sourceObject.getIdName(); + controller.moveCardsToExile(card, source, game, true, exileId, exileName); + if (game.getState().getZone(card.getId()) == Zone.EXILED && card.getColor(game).isRed()) { + game.addEffect(new PlayFromNotOwnHandZoneTargetEffect(Zone.EXILED, TargetController.YOU, Duration.EndOfTurn, false, true) + .setTargetPointer(new FixedTarget(card, game)), source); + } + return true; + } +} + +class ChandraDressedToKillExile5Effect extends OneShotEffect { + + public ChandraDressedToKillExile5Effect() { + super(Outcome.Benefit); + staticText = "Exile the top five cards of your library. You may cast red spells from among them this turn"; + } + + private ChandraDressedToKillExile5Effect(final ChandraDressedToKillExile5Effect effect) { + super(effect); + } + + @Override + public ChandraDressedToKillExile5Effect copy() { + return new ChandraDressedToKillExile5Effect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + Set cards = controller.getLibrary().getTopCards(game, 5); + if (cards.isEmpty()) { + return false; + } + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + MageObject sourceObject = source.getSourceObject(game); + String exileName = sourceObject == null ? null : sourceObject.getIdName(); + controller.moveCardsToExile(cards, source, game, true, exileId, exileName); + cards.removeIf(card -> !Zone.EXILED.equals(game.getState().getZone(card.getId()))); + if (!cards.isEmpty()) { + game.addEffect(new ChandraDressedToKillPlayEffect() + .setTargetPointer(new FixedTargets(cards, game)), source); + } + return true; + } +} + +// Only used for the -7 ability (see comment at top) +class ChandraDressedToKillPlayEffect extends PlayFromNotOwnHandZoneTargetEffect { + + public ChandraDressedToKillPlayEffect() { + super(Zone.EXILED, TargetController.YOU, Duration.EndOfTurn, false, true); + } + + private ChandraDressedToKillPlayEffect(final ChandraDressedToKillPlayEffect effect) { + super(effect); + } + + @Override + public ChandraDressedToKillPlayEffect copy() { + return new ChandraDressedToKillPlayEffect(this); + } + + @Override + public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) { + if (!super.applies(objectId, affectedAbility, source, game, playerId)) { + return false; + } + Card card = game.getCard(objectId); + return card != null && card.getColor(game).isRed(); + } +} diff --git a/Mage.Sets/src/mage/cards/c/ChandraFireArtisan.java b/Mage.Sets/src/mage/cards/c/ChandraFireArtisan.java index bd3011a153a..85cb7459a98 100644 --- a/Mage.Sets/src/mage/cards/c/ChandraFireArtisan.java +++ b/Mage.Sets/src/mage/cards/c/ChandraFireArtisan.java @@ -4,7 +4,6 @@ import mage.MageObject; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageTargetEffect; @@ -29,7 +28,7 @@ public final class ChandraFireArtisan extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.CHANDRA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // Whenever one or more loyalty counters are removed from Chandra, Fire Artisan, she deals that much damage to target opponent or planeswalker. this.addAbility(new ChandraFireArtisanTriggeredAbility()); diff --git a/Mage.Sets/src/mage/cards/c/ChandraFireOfKaladesh.java b/Mage.Sets/src/mage/cards/c/ChandraFireOfKaladesh.java index 217cd782ff0..0b04f718128 100644 --- a/Mage.Sets/src/mage/cards/c/ChandraFireOfKaladesh.java +++ b/Mage.Sets/src/mage/cards/c/ChandraFireOfKaladesh.java @@ -5,7 +5,7 @@ import java.util.UUID; import mage.MageInt; import mage.ObjectColor; import mage.abilities.Ability; -import mage.abilities.Gender; +import mage.abilities.Pronoun; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.condition.common.SourceDealtDamageCondition; @@ -45,7 +45,6 @@ public final class ChandraFireOfKaladesh extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); - this.transformable = true; this.secondSideCardClazz = ChandraRoaringFlame.class; // Whenever you cast a red spell, untap Chandra, Fire of Kaladesh. @@ -54,7 +53,7 @@ public final class ChandraFireOfKaladesh extends CardImpl { // {T}: Chandra, Fire of Kaladesh deals 1 damage to target player. If Chandra has dealt 3 or more damage this turn, exile her, then return her to the battlefield transformed under her owner's control. this.addAbility(new TransformAbility()); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(1), new TapSourceCost()); - ability.addEffect(new ConditionalOneShotEffect(new ExileAndReturnTransformedSourceEffect(Gender.FEMALE), new SourceDealtDamageCondition(3))); + ability.addEffect(new ConditionalOneShotEffect(new ExileAndReturnTransformedSourceEffect(Pronoun.SHE), new SourceDealtDamageCondition(3))); ability.addTarget(new TargetPlayerOrPlaneswalker()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/c/ChandraFlamecaller.java b/Mage.Sets/src/mage/cards/c/ChandraFlamecaller.java index 7a27babd504..97e7fa43803 100644 --- a/Mage.Sets/src/mage/cards/c/ChandraFlamecaller.java +++ b/Mage.Sets/src/mage/cards/c/ChandraFlamecaller.java @@ -2,7 +2,6 @@ package mage.cards.c; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.dynamicvalue.common.GetXLoyaltyValue; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; @@ -30,7 +29,7 @@ public final class ChandraFlamecaller extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.CHANDRA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Create two 3/1 red Elemental creature tokens with haste. Exile them at the beginning of the next end step. this.addAbility(new LoyaltyAbility(new ChandraElementalEffect(), 1)); diff --git a/Mage.Sets/src/mage/cards/c/ChandraFlamesCatalyst.java b/Mage.Sets/src/mage/cards/c/ChandraFlamesCatalyst.java index 9c78e245a3e..91763674b5d 100644 --- a/Mage.Sets/src/mage/cards/c/ChandraFlamesCatalyst.java +++ b/Mage.Sets/src/mage/cards/c/ChandraFlamesCatalyst.java @@ -3,7 +3,6 @@ package mage.cards.c; import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.CastCardFromGraveyardThenExileItEffect; import mage.abilities.effects.Effect; import mage.abilities.effects.common.DamagePlayersEffect; @@ -39,7 +38,7 @@ public final class ChandraFlamesCatalyst extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.CHANDRA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +1: Chandra, Flame's Catalyst deals 3 damage to each opponent. this.addAbility(new LoyaltyAbility(new DamagePlayersEffect(3, TargetController.OPPONENT), 1)); diff --git a/Mage.Sets/src/mage/cards/c/ChandraFlamesFury.java b/Mage.Sets/src/mage/cards/c/ChandraFlamesFury.java index 454b0153f82..8ed4fa1f39e 100644 --- a/Mage.Sets/src/mage/cards/c/ChandraFlamesFury.java +++ b/Mage.Sets/src/mage/cards/c/ChandraFlamesFury.java @@ -2,7 +2,6 @@ package mage.cards.c; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageAllControlledTargetEffect; import mage.abilities.effects.common.DamageTargetEffect; @@ -32,7 +31,7 @@ public final class ChandraFlamesFury extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.CHANDRA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Chandra, Flame's Fury deals 2 damage to any target. Ability ability = new LoyaltyAbility(new DamageTargetEffect(2), 1); diff --git a/Mage.Sets/src/mage/cards/c/ChandraGremlinWrangler.java b/Mage.Sets/src/mage/cards/c/ChandraGremlinWrangler.java index 643baa27a87..d4cf5ff5619 100644 --- a/Mage.Sets/src/mage/cards/c/ChandraGremlinWrangler.java +++ b/Mage.Sets/src/mage/cards/c/ChandraGremlinWrangler.java @@ -2,7 +2,6 @@ package mage.cards.c; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.common.CreateTokenEffect; @@ -37,7 +36,7 @@ public final class ChandraGremlinWrangler extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.CHANDRA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +1: Create a 2/2 red Gremlin creature token. this.addAbility(new LoyaltyAbility(new CreateTokenEffect(new GremlinToken()), 1)); diff --git a/Mage.Sets/src/mage/cards/c/ChandraHeartOfFire.java b/Mage.Sets/src/mage/cards/c/ChandraHeartOfFire.java index 1356bb3de48..1a0f5432850 100644 --- a/Mage.Sets/src/mage/cards/c/ChandraHeartOfFire.java +++ b/Mage.Sets/src/mage/cards/c/ChandraHeartOfFire.java @@ -4,7 +4,6 @@ import mage.Mana; import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageTargetEffect; @@ -27,6 +26,7 @@ import mage.target.common.TargetAnyTarget; import mage.target.common.TargetCardInLibrary; import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetpointer.FixedTargets; +import mage.util.CardUtil; import java.util.HashSet; import java.util.Set; @@ -42,7 +42,7 @@ public final class ChandraHeartOfFire extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.CHANDRA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +1: Discard your hand, then exile the top three cards of your library. Until end of turn, you may play cards exiled this way. Ability ability = new LoyaltyAbility(new DiscardHandControllerEffect(), 1); @@ -104,7 +104,6 @@ class ChandraHeartOfFireUltimateEffect extends OneShotEffect { if (target.canChoose(source.getSourceId(), controller.getId(), game) && target.choose(Outcome.AIDontUseIt, controller.getId(), source.getSourceId(), game)) { Set cards = new CardsImpl(target.getTargets()).getCards(game); - controller.moveCards(cards, Zone.EXILED, source, game); exiledCards.addAll(cards); } @@ -113,9 +112,11 @@ class ChandraHeartOfFireUltimateEffect extends OneShotEffect { if (target.canChoose(source.getSourceId(), controller.getId(), game) && target.choose(Outcome.AIDontUseIt, controller.getId(), source.getSourceId(), game)) { Set cards = new CardsImpl(target.getTargets()).getCards(game); - controller.moveCards(cards, Zone.EXILED, source, game); exiledCards.addAll(cards); } + + // exile cards all at once and set the exile name to the source card + controller.moveCardsToExile(exiledCards, source, game, true, CardUtil.getExileZoneId(game, source), CardUtil.getSourceName(game, source)); controller.shuffleLibrary(source, game); exiledCards.removeIf(card -> !Zone.EXILED.equals(game.getState().getZone(card.getId()))); diff --git a/Mage.Sets/src/mage/cards/c/ChandraNalaar.java b/Mage.Sets/src/mage/cards/c/ChandraNalaar.java index 09c6b329535..f4ef053a684 100644 --- a/Mage.Sets/src/mage/cards/c/ChandraNalaar.java +++ b/Mage.Sets/src/mage/cards/c/ChandraNalaar.java @@ -2,7 +2,6 @@ package mage.cards.c; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.dynamicvalue.common.GetXLoyaltyValue; import mage.abilities.effects.Effects; import mage.abilities.effects.common.DamageAllControlledTargetEffect; @@ -28,7 +27,7 @@ public final class ChandraNalaar extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.CHANDRA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(6)); + this.setStartingLoyalty(6); // +1: Chandra Nalaar deals 1 damage to target player or planeswalker. LoyaltyAbility ability1 = new LoyaltyAbility(new DamageTargetEffect(1), 1); diff --git a/Mage.Sets/src/mage/cards/c/ChandraNovicePyromancer.java b/Mage.Sets/src/mage/cards/c/ChandraNovicePyromancer.java index e262ee17f94..cfa960e489b 100644 --- a/Mage.Sets/src/mage/cards/c/ChandraNovicePyromancer.java +++ b/Mage.Sets/src/mage/cards/c/ChandraNovicePyromancer.java @@ -3,7 +3,6 @@ package mage.cards.c; import mage.Mana; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.continuous.BoostControlledEffect; import mage.abilities.effects.mana.BasicManaEffect; @@ -31,7 +30,7 @@ public final class ChandraNovicePyromancer extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.CHANDRA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +1: Elementals you control get +2/+0 until end of turn. this.addAbility(new LoyaltyAbility( diff --git a/Mage.Sets/src/mage/cards/c/ChandraPyrogenius.java b/Mage.Sets/src/mage/cards/c/ChandraPyrogenius.java index 3c4249d7b1b..37977180130 100644 --- a/Mage.Sets/src/mage/cards/c/ChandraPyrogenius.java +++ b/Mage.Sets/src/mage/cards/c/ChandraPyrogenius.java @@ -3,7 +3,6 @@ package mage.cards.c; import java.util.UUID; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.Effects; import mage.abilities.effects.common.DamageAllControlledTargetEffect; @@ -31,7 +30,7 @@ public final class ChandraPyrogenius extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.CHANDRA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +2: Chandra, Pyrogenius deals 2 damage to each opponent. this.addAbility(new LoyaltyAbility(new DamagePlayersEffect(Outcome.Damage, StaticValue.get(2), TargetController.OPPONENT), 2)); diff --git a/Mage.Sets/src/mage/cards/c/ChandraPyromaster.java b/Mage.Sets/src/mage/cards/c/ChandraPyromaster.java index 48f06046a40..3eb7d4bcc1c 100644 --- a/Mage.Sets/src/mage/cards/c/ChandraPyromaster.java +++ b/Mage.Sets/src/mage/cards/c/ChandraPyromaster.java @@ -7,7 +7,6 @@ import mage.ApprovingObject; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; @@ -36,7 +35,7 @@ public final class ChandraPyromaster extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.CHANDRA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Chandra, Pyromaster deals 1 damage to target player and 1 damage to // up to one target creature that player controls. That creature can't block this turn. @@ -94,7 +93,7 @@ class ChandraPyromasterEffect1 extends OneShotEffect { if (creature != null) { creature.damage(1, source.getSourceId(), source, game, false, true); ContinuousEffect effect = new CantBlockTargetEffect(Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(creature.getId())); + effect.setTargetPointer(new FixedTarget(creature.getId(), game)); game.addEffect(effect, source); } return true; diff --git a/Mage.Sets/src/mage/cards/c/ChandraRoaringFlame.java b/Mage.Sets/src/mage/cards/c/ChandraRoaringFlame.java index 09ebc62e4d4..ce08c704fe1 100644 --- a/Mage.Sets/src/mage/cards/c/ChandraRoaringFlame.java +++ b/Mage.Sets/src/mage/cards/c/ChandraRoaringFlame.java @@ -2,7 +2,6 @@ package mage.cards.c; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageTargetEffect; import mage.cards.CardImpl; @@ -33,9 +32,8 @@ public final class ChandraRoaringFlame extends CardImpl { this.color.setRed(true); this.nightCard = true; - this.transformable = true; - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Chandra, Roaring Flame deals 2 damage to target player. LoyaltyAbility loyaltyAbility = new LoyaltyAbility(new DamageTargetEffect(2), 1); diff --git a/Mage.Sets/src/mage/cards/c/ChandraTheFirebrand.java b/Mage.Sets/src/mage/cards/c/ChandraTheFirebrand.java index 2f18156678f..aa85c501d14 100644 --- a/Mage.Sets/src/mage/cards/c/ChandraTheFirebrand.java +++ b/Mage.Sets/src/mage/cards/c/ChandraTheFirebrand.java @@ -4,7 +4,6 @@ package mage.cards.c; import java.util.UUID; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.CopyTargetSpellEffect; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; @@ -32,7 +31,7 @@ public final class ChandraTheFirebrand extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.CHANDRA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +1: Chandra, the Firebrand deals 1 damage to any target. LoyaltyAbility ability1 = new LoyaltyAbility(new DamageTargetEffect(1), 1); diff --git a/Mage.Sets/src/mage/cards/c/ChandraTorchOfDefiance.java b/Mage.Sets/src/mage/cards/c/ChandraTorchOfDefiance.java index 0f921f1fd25..651e30a7e38 100644 --- a/Mage.Sets/src/mage/cards/c/ChandraTorchOfDefiance.java +++ b/Mage.Sets/src/mage/cards/c/ChandraTorchOfDefiance.java @@ -6,7 +6,6 @@ import mage.MageObject; import mage.Mana; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamagePlayersEffect; @@ -38,7 +37,7 @@ public final class ChandraTorchOfDefiance extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.CHANDRA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Exile the top card of your library. You may cast that card. If you don't, Chandra, Torch of Defiance deals 2 damage to each opponent. LoyaltyAbility ability = new LoyaltyAbility(new ChandraTorchOfDefianceEffect(), 1); diff --git a/Mage.Sets/src/mage/cards/c/ChandrasPyrohelix.java b/Mage.Sets/src/mage/cards/c/ChandrasPyrohelix.java index 15227bdcc19..e760da10870 100644 --- a/Mage.Sets/src/mage/cards/c/ChandrasPyrohelix.java +++ b/Mage.Sets/src/mage/cards/c/ChandrasPyrohelix.java @@ -1,7 +1,6 @@ package mage.cards.c; import java.util.UUID; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.DamageMultiEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -18,9 +17,7 @@ public final class ChandrasPyrohelix extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{R}"); // Chandra's Pyrohelix deals 2 damage divided as you choose among one or two targets. - Effect effect = new DamageMultiEffect(2); - effect.setText("{this} deals 2 damage divided as you choose among one or two targets"); - this.getSpellAbility().addEffect(effect); + this.getSpellAbility().addEffect(new DamageMultiEffect(2)); this.getSpellAbility().addTarget(new TargetAnyTargetAmount(2)); } diff --git a/Mage.Sets/src/mage/cards/c/ChangeOfFortune.java b/Mage.Sets/src/mage/cards/c/ChangeOfFortune.java new file mode 100644 index 00000000000..a248efc5eed --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ChangeOfFortune.java @@ -0,0 +1,63 @@ +package mage.cards.c; + +import java.util.UUID; + +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.discard.DiscardHandControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.game.Game; +import mage.watchers.common.DiscardedCardWatcher; + +/** + * + * @author weirddan455 + */ +public final class ChangeOfFortune extends CardImpl { + + public ChangeOfFortune(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{R}"); + + // Discard your hand, then draw a card for each card you've discarded this turn. + this.getSpellAbility().addEffect(new DiscardHandControllerEffect()); + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(ChangeOfFortuneValue.instance).concatBy(", then")); + this.getSpellAbility().addWatcher(new DiscardedCardWatcher()); + } + + private ChangeOfFortune(final ChangeOfFortune card) { + super(card); + } + + @Override + public ChangeOfFortune copy() { + return new ChangeOfFortune(this); + } +} + +enum ChangeOfFortuneValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + return DiscardedCardWatcher.getDiscarded(sourceAbility.getControllerId(), game); + } + + @Override + public ChangeOfFortuneValue copy() { + return instance; + } + + @Override + public String getMessage() { + return "card you've discarded this turn"; + } + + @Override + public String toString() { + return "1"; + } +} diff --git a/Mage.Sets/src/mage/cards/c/ChanneledForce.java b/Mage.Sets/src/mage/cards/c/ChanneledForce.java index 7e933d29193..698172bc82b 100644 --- a/Mage.Sets/src/mage/cards/c/ChanneledForce.java +++ b/Mage.Sets/src/mage/cards/c/ChanneledForce.java @@ -25,7 +25,7 @@ public final class ChanneledForce extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{U}{R}"); // As an additional cost to cast this spell, discard X cards. - this.getSpellAbility().addCost(new DiscardXTargetCost(StaticFilters.FILTER_CARD_CARDS, true)); + this.getSpellAbility().addCost(new DiscardXTargetCost(StaticFilters.FILTER_CARD_CARDS)); // Target player draws X cards. Channeled Force deals X damage to up to one target creature or planeswalker. this.getSpellAbility().addEffect(new ChanneledForceEffect()); diff --git a/Mage.Sets/src/mage/cards/c/ChapelShieldgeist.java b/Mage.Sets/src/mage/cards/c/ChapelShieldgeist.java index 397c975f90c..d5ac684e100 100644 --- a/Mage.Sets/src/mage/cards/c/ChapelShieldgeist.java +++ b/Mage.Sets/src/mage/cards/c/ChapelShieldgeist.java @@ -31,7 +31,6 @@ public final class ChapelShieldgeist extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(1); this.color.setWhite(true); - this.transformable = true; this.nightCard = true; // Flying diff --git a/Mage.Sets/src/mage/cards/c/ChaplainOfAlms.java b/Mage.Sets/src/mage/cards/c/ChaplainOfAlms.java index 73105c7bf2f..a6dd19da76b 100644 --- a/Mage.Sets/src/mage/cards/c/ChaplainOfAlms.java +++ b/Mage.Sets/src/mage/cards/c/ChaplainOfAlms.java @@ -4,7 +4,6 @@ import mage.MageInt; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.keyword.DisturbAbility; import mage.abilities.keyword.FirstStrikeAbility; -import mage.abilities.keyword.TransformAbility; import mage.abilities.keyword.WardAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -25,7 +24,6 @@ public final class ChaplainOfAlms extends CardImpl { this.subtype.add(SubType.CLERIC); this.power = new MageInt(1); this.toughness = new MageInt(1); - this.transformable = true; this.secondSideCardClazz = mage.cards.c.ChapelShieldgeist.class; // First strike @@ -35,8 +33,7 @@ public final class ChaplainOfAlms extends CardImpl { this.addAbility(new WardAbility(new ManaCostsImpl<>("{1}"))); // Disturb {3}{W} - this.addAbility(new TransformAbility()); - this.addAbility(new DisturbAbility(new ManaCostsImpl<>("{3}{W}"))); + this.addAbility(new DisturbAbility(this, "{3}{W}")); } private ChaplainOfAlms(final ChaplainOfAlms card) { diff --git a/Mage.Sets/src/mage/cards/c/CharmbreakerDevils.java b/Mage.Sets/src/mage/cards/c/CharmbreakerDevils.java index aece380a165..4d4e65e231a 100644 --- a/Mage.Sets/src/mage/cards/c/CharmbreakerDevils.java +++ b/Mage.Sets/src/mage/cards/c/CharmbreakerDevils.java @@ -77,7 +77,7 @@ class CharmbreakerDevilsEffect extends OneShotEffect { TargetCard target = new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_INSTANT_AND_SORCERY); target.setRandom(true); target.setNotTarget(true); - player.chooseTarget(outcome, target, source, game); + target.chooseTarget(outcome, player.getId(), source, game); Card card = game.getCard(target.getFirstTarget()); return card != null && player.moveCards(card, Zone.HAND, source, game); } diff --git a/Mage.Sets/src/mage/cards/c/CharnelTroll.java b/Mage.Sets/src/mage/cards/c/CharnelTroll.java index 17f5ee7d9a0..0405b460c97 100644 --- a/Mage.Sets/src/mage/cards/c/CharnelTroll.java +++ b/Mage.Sets/src/mage/cards/c/CharnelTroll.java @@ -49,7 +49,7 @@ public final class CharnelTroll extends CardImpl { )), false ).setText("exile a creature card from your graveyard. " + "If you do, put a +1/+1 counter on {this}." - + " Otherwise sacrifice it."), + + " Otherwise, sacrifice it."), TargetController.YOU, false )); diff --git a/Mage.Sets/src/mage/cards/c/ChemistersTrick.java b/Mage.Sets/src/mage/cards/c/ChemistersTrick.java index fcbaac6c6c8..812b25bfdac 100644 --- a/Mage.Sets/src/mage/cards/c/ChemistersTrick.java +++ b/Mage.Sets/src/mage/cards/c/ChemistersTrick.java @@ -66,7 +66,7 @@ class ChemistersTrickEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { for (Permanent creature : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL, source.getControllerId(), source.getSourceId(), game)) { AttacksIfAbleTargetEffect effect = new AttacksIfAbleTargetEffect(Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(creature.getId())); + effect.setTargetPointer(new FixedTarget(creature.getId(), game)); game.addEffect(effect, source); } return true; diff --git a/Mage.Sets/src/mage/cards/c/ChildOfThePack.java b/Mage.Sets/src/mage/cards/c/ChildOfThePack.java new file mode 100644 index 00000000000..ca2629315df --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ChildOfThePack.java @@ -0,0 +1,47 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.DayboundAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.WolfToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ChildOfThePack extends CardImpl { + + public ChildOfThePack(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{G}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WEREWOLF); + this.power = new MageInt(2); + this.toughness = new MageInt(5); + this.secondSideCardClazz = mage.cards.s.SavagePackmate.class; + + // {2}{R}{G}: Create a 2/2 green Wolf creature token. + this.addAbility(new SimpleActivatedAbility( + new CreateTokenEffect(new WolfToken()), new ManaCostsImpl<>("{2}{R}{G}") + )); + + // Daybound + this.addAbility(new DayboundAbility()); + } + + private ChildOfThePack(final ChildOfThePack card) { + super(card); + } + + @Override + public ChildOfThePack copy() { + return new ChildOfThePack(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/ChillOfTheGrave.java b/Mage.Sets/src/mage/cards/c/ChillOfTheGrave.java new file mode 100644 index 00000000000..e7a232205db --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ChillOfTheGrave.java @@ -0,0 +1,57 @@ +package mage.cards.c; + +import java.util.UUID; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.effects.common.DontUntapInControllersNextUntapStepTargetEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; +import mage.abilities.hint.ConditionHint; +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.target.common.TargetCreaturePermanent; + +/** + * + * @author weirddan455 + */ +public final class ChillOfTheGrave extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent(SubType.ZOMBIE, "you control a Zombie"); + + public ChillOfTheGrave(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{U}"); + + // This spell costs {1} less to cast if you control a Zombie. + Condition condition = new PermanentsOnTheBattlefieldCondition(filter); + Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(1, condition)); + ability.setRuleAtTheTop(true); + ability.addHint(new ConditionHint(condition)); + this.addAbility(ability); + + // Tap target creature. It doesn't untap during its controller's next untap step. + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + this.getSpellAbility().addEffect(new TapTargetEffect()); + this.getSpellAbility().addEffect(new DontUntapInControllersNextUntapStepTargetEffect("It")); + + // Draw a card. + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("
")); + } + + private ChillOfTheGrave(final ChillOfTheGrave card) { + super(card); + } + + @Override + public ChillOfTheGrave copy() { + return new ChillOfTheGrave(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/ChillingChronicle.java b/Mage.Sets/src/mage/cards/c/ChillingChronicle.java index 27b55de43e1..12036ef2743 100644 --- a/Mage.Sets/src/mage/cards/c/ChillingChronicle.java +++ b/Mage.Sets/src/mage/cards/c/ChillingChronicle.java @@ -22,13 +22,12 @@ public final class ChillingChronicle extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, ""); this.color.setBlue(true); - this.transformable = true; this.nightCard = true; // {1}, {T}: Tap target nonland permanent. Transform Chilling Chronicle. Ability ability = new SimpleActivatedAbility(new TapTargetEffect(), new GenericManaCost(1)); ability.addCost(new TapSourceCost()); - ability.addEffect(new TransformSourceEffect(false)); + ability.addEffect(new TransformSourceEffect()); ability.addTarget(new TargetNonlandPermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/c/ChimeOfNight.java b/Mage.Sets/src/mage/cards/c/ChimeOfNight.java index fc46c59e4b2..78da0f9f66f 100644 --- a/Mage.Sets/src/mage/cards/c/ChimeOfNight.java +++ b/Mage.Sets/src/mage/cards/c/ChimeOfNight.java @@ -1,6 +1,5 @@ package mage.cards.c; -import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.PutIntoGraveFromBattlefieldSourceTriggeredAbility; import mage.abilities.effects.common.AttachEffect; @@ -11,9 +10,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ColorPredicate; +import mage.filter.StaticFilters; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; @@ -24,12 +21,6 @@ import java.util.UUID; */ public final class ChimeOfNight extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public ChimeOfNight(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}"); @@ -44,7 +35,7 @@ public final class ChimeOfNight extends CardImpl { // When Chime of Night is put into a graveyard from the battlefield, destroy target nonblack creature. ability = new PutIntoGraveFromBattlefieldSourceTriggeredAbility(new DestroyTargetEffect()); - ability.addTarget(new TargetPermanent(filter)); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/c/ChishiroTheShatteredBlade.java b/Mage.Sets/src/mage/cards/c/ChishiroTheShatteredBlade.java new file mode 100644 index 00000000000..6f7b759adc6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ChishiroTheShatteredBlade.java @@ -0,0 +1,67 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.counter.AddCountersAllEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.TargetController; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.ModifiedPredicate; +import mage.game.permanent.token.SpiritRedToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ChishiroTheShatteredBlade extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent("an Aura or Equipment"); + private static final FilterPermanent filter2 = new FilterControlledCreaturePermanent("modified creature you control"); + + static { + filter.add(Predicates.or( + SubType.AURA.getPredicate(), + SubType.EQUIPMENT.getPredicate() + )); + filter2.add(ModifiedPredicate.instance); + } + + public ChishiroTheShatteredBlade(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.SNAKE); + this.subtype.add(SubType.SAMURAI); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Whenever an Aura or Equipment enters the battlefield under your control, create a 2/2 red Spirit creature token. + this.addAbility(new EntersBattlefieldControlledTriggeredAbility( + new CreateTokenEffect(new SpiritRedToken()), filter + )); + + // At the beginning of your end step, put a +1/+1 counter on each modified creature you control. + this.addAbility(new BeginningOfEndStepTriggeredAbility(new AddCountersAllEffect( + CounterType.P1P1.createInstance(), filter2 + ), TargetController.YOU, false)); + } + + private ChishiroTheShatteredBlade(final ChishiroTheShatteredBlade card) { + super(card); + } + + @Override + public ChishiroTheShatteredBlade copy() { + return new ChishiroTheShatteredBlade(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/ChitinousCloak.java b/Mage.Sets/src/mage/cards/c/ChitinousCloak.java index a5a77ca0aa0..05a1ceceb2d 100644 --- a/Mage.Sets/src/mage/cards/c/ChitinousCloak.java +++ b/Mage.Sets/src/mage/cards/c/ChitinousCloak.java @@ -31,7 +31,7 @@ public final class ChitinousCloak extends CardImpl { // Equipped creature gets +2/+2 and has menace. Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(2, 2)); Effect effect = new GainAbilityAttachedEffect(new MenaceAbility(), AttachmentType.EQUIPMENT); - effect.setText("and has menace"); + effect.setText("and has menace. (It can't be blocked except by two or more creatures.)"); ability.addEffect(effect); this.addAbility(ability); // Equip {3} diff --git a/Mage.Sets/src/mage/cards/c/ChorusOfTheTides.java b/Mage.Sets/src/mage/cards/c/ChorusOfTheTides.java index 82cae80069f..2488a17894b 100644 --- a/Mage.Sets/src/mage/cards/c/ChorusOfTheTides.java +++ b/Mage.Sets/src/mage/cards/c/ChorusOfTheTides.java @@ -26,8 +26,9 @@ public final class ChorusOfTheTides extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); + // Heroic — Whenever you cast a spell that targets Chorus of the Tides, scry 1. - this.addAbility(new HeroicAbility(new ScryEffect(1))); + this.addAbility(new HeroicAbility(new ScryEffect(1, false))); } private ChorusOfTheTides(final ChorusOfTheTides card) { diff --git a/Mage.Sets/src/mage/cards/c/ChosenOfMarkov.java b/Mage.Sets/src/mage/cards/c/ChosenOfMarkov.java index 43a0143df8b..b52ded12bbe 100644 --- a/Mage.Sets/src/mage/cards/c/ChosenOfMarkov.java +++ b/Mage.Sets/src/mage/cards/c/ChosenOfMarkov.java @@ -37,12 +37,11 @@ public final class ChosenOfMarkov extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); - this.transformable = true; this.secondSideCardClazz = mage.cards.m.MarkovsServant.class; // {tap}, Tap an untapped Vampire you control: Transform Chosen of Markov. this.addAbility(new TransformAbility()); - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new TransformSourceEffect(true), new TapSourceCost()); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new TransformSourceEffect(), new TapSourceCost()); ability.addCost(new TapTargetCost(new TargetControlledPermanent(filter))); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/c/ChromeReplicator.java b/Mage.Sets/src/mage/cards/c/ChromeReplicator.java index cc05f6e7bd8..3bfecae7b0c 100644 --- a/Mage.Sets/src/mage/cards/c/ChromeReplicator.java +++ b/Mage.Sets/src/mage/cards/c/ChromeReplicator.java @@ -17,6 +17,7 @@ import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.Game; import mage.game.permanent.token.Construct4Token; +import mage.util.CardUtil; import java.util.HashMap; import java.util.Map; @@ -75,6 +76,6 @@ enum ChromeReplicatorCondition implements Condition { .map(MageObject::getName) .filter(Objects::nonNull) .filter(s -> !s.isEmpty()) - .anyMatch(s -> nameMap.compute(s, (x, i) -> i == null ? 1 : Integer.sum(i, 1)) >= 2); + .anyMatch(s -> nameMap.compute(s, CardUtil::setOrIncrementValue) >= 2); } } diff --git a/Mage.Sets/src/mage/cards/c/ChromeshellCrab.java b/Mage.Sets/src/mage/cards/c/ChromeshellCrab.java index e9122ac6ee9..9a59d61658c 100644 --- a/Mage.Sets/src/mage/cards/c/ChromeshellCrab.java +++ b/Mage.Sets/src/mage/cards/c/ChromeshellCrab.java @@ -14,8 +14,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; @@ -26,12 +25,6 @@ import mage.target.common.TargetCreaturePermanent; public final class ChromeshellCrab extends CardImpl { private static final String rule = "you may exchange control of target creature you control and target creature an opponent controls"; - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } public ChromeshellCrab(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{U}"); @@ -48,7 +41,7 @@ public final class ChromeshellCrab extends CardImpl { effect.setText("exchange control of target creature you control and target creature an opponent controls"); Ability ability = new TurnedFaceUpSourceTriggeredAbility(effect, false, true); ability.addTarget(new TargetControlledCreaturePermanent()); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/c/ChroniclerOfHeroes.java b/Mage.Sets/src/mage/cards/c/ChroniclerOfHeroes.java index cae6e3a3cb3..1654feceefa 100644 --- a/Mage.Sets/src/mage/cards/c/ChroniclerOfHeroes.java +++ b/Mage.Sets/src/mage/cards/c/ChroniclerOfHeroes.java @@ -11,9 +11,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Outcome; -import mage.constants.TargetController; -import mage.counters.CounterType; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; @@ -49,12 +47,6 @@ public final class ChroniclerOfHeroes extends CardImpl { class ChroniclerOfHeroesEffect extends OneShotEffect { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature with a +1/+1 counter on it"); - static { - filter.add(TargetController.YOU.getControllerPredicate()); - filter.add(CounterType.P1P1.getPredicate()); - } - public ChroniclerOfHeroesEffect() { super(Outcome.DrawCard); this.staticText = "draw a card if you control a creature with a +1/+1 counter on it"; @@ -73,7 +65,7 @@ class ChroniclerOfHeroesEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - if (new PermanentsOnTheBattlefieldCondition(filter).apply(game, source)) { + if (new PermanentsOnTheBattlefieldCondition(StaticFilters.FILTER_A_CREATURE_P1P1).apply(game, source)) { controller.drawCards(1, source, game); } return true; diff --git a/Mage.Sets/src/mage/cards/c/ChunLiCountlessKicks.java b/Mage.Sets/src/mage/cards/c/ChunLiCountlessKicks.java new file mode 100644 index 00000000000..542de6e0e0e --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ChunLiCountlessKicks.java @@ -0,0 +1,156 @@ +package mage.cards.c; + +import mage.ApprovingObject; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.dynamicvalue.common.MultikickerCount; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.MultikickerAbility; +import mage.cards.*; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.FilterCard; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInYourGraveyard; +import mage.target.targetadjustment.TargetAdjuster; +import org.apache.log4j.Logger; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ChunLiCountlessKicks extends CardImpl { + + public ChunLiCountlessKicks(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Multikicker {W/U} + this.addAbility(new MultikickerAbility("{W/U}")); + + // When Chun-Li enters the battlefield, exile up to X target instant cards from your graveyard, where X is the number of times Chun-Li was kicked. Put a kick counter on each of them. + this.addAbility(new EntersBattlefieldTriggeredAbility(new ChunLiCountlessKicksExileEffect()) + .setTargetAdjuster(ChunLiCountlessKicksAdjuster.instance)); + + // Lightning Kick—Whenever Chun-Li attacks, copy each exiled card you own with a kick counter on it. You may cast the copies. + this.addAbility(new AttacksTriggeredAbility(new ChunLiCountlessKicksCastEffect()).withFlavorWord("Lightning Kick")); + } + + private ChunLiCountlessKicks(final ChunLiCountlessKicks card) { + super(card); + } + + @Override + public ChunLiCountlessKicks copy() { + return new ChunLiCountlessKicks(this); + } +} + +enum ChunLiCountlessKicksAdjuster implements TargetAdjuster { + instance; + private static final FilterCard filter = new FilterCard("instant cards from your graveyard"); + + static { + filter.add(CardType.INSTANT.getPredicate()); + } + + @Override + public void adjustTargets(Ability ability, Game game) { + int count = MultikickerCount.instance.calculate(game, ability, null); + ability.getTargets().clear(); + ability.addTarget(new TargetCardInYourGraveyard(0, count, filter)); + } +} + +class ChunLiCountlessKicksExileEffect extends OneShotEffect { + + ChunLiCountlessKicksExileEffect() { + super(Outcome.Benefit); + staticText = "exile up to X target instant cards from your graveyard, " + + "where X is the number of times {this} was kicked. Put a kick counter on each of them"; + } + + private ChunLiCountlessKicksExileEffect(final ChunLiCountlessKicksExileEffect effect) { + super(effect); + } + + @Override + public ChunLiCountlessKicksExileEffect copy() { + return new ChunLiCountlessKicksExileEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Cards cards = new CardsImpl(getTargetPointer().getTargets(game, source)); + if (player == null || cards.isEmpty()) { + return false; + } + player.moveCards(cards, Zone.EXILED, source, game); + cards.getCards(game).forEach(card -> card.addCounters(CounterType.KICK.createInstance(), source, game)); + return true; + } +} + +class ChunLiCountlessKicksCastEffect extends OneShotEffect { + + ChunLiCountlessKicksCastEffect() { + super(Outcome.Benefit); + staticText = "copy each exiled card you own with a kick counter on it. You may cast the copies"; + } + + private ChunLiCountlessKicksCastEffect(final ChunLiCountlessKicksCastEffect effect) { + super(effect); + } + + @Override + public ChunLiCountlessKicksCastEffect copy() { + return new ChunLiCountlessKicksCastEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Cards cards = new CardsImpl(game.getExile().getAllCards(game, source.getControllerId())); + cards.removeIf(uuid -> !game.getCard(uuid).getCounters(game).containsKey(CounterType.KICK)); + if (cards.isEmpty()) { + return false; + } + Cards copies = new CardsImpl(); + for (Card card : cards.getCards(game)) { + Card copiedCard = game.copyCard(card, source, source.getControllerId()); + game.getExile().add(source.getSourceId(), "", copiedCard); + game.getState().setZone(copiedCard.getId(), Zone.EXILED); + copies.add(copiedCard); + } + for (Card copiedCard : copies.getCards(game)) { + if (!player.chooseUse(outcome, "Cast the copied card?", source, game)) { + continue; + } + if (copiedCard.getSpellAbility() != null) { + game.getState().setValue("PlayFromNotOwnHandZone" + copiedCard.getId(), Boolean.TRUE); + player.cast( + player.chooseAbilityForCast(copiedCard, game, true), + game, true, new ApprovingObject(source, game) + ); + game.getState().setValue("PlayFromNotOwnHandZone" + copiedCard.getId(), null); + } else { + Logger.getLogger(ChunLiCountlessKicksCastEffect.class).error("Chun Li, Countless Kicks: " + + "spell ability == null " + copiedCard.getName()); + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/c/CinderheartGiant.java b/Mage.Sets/src/mage/cards/c/CinderheartGiant.java index 262149ea57d..4dc474cf9fe 100644 --- a/Mage.Sets/src/mage/cards/c/CinderheartGiant.java +++ b/Mage.Sets/src/mage/cards/c/CinderheartGiant.java @@ -77,7 +77,7 @@ class CinderheartGiantEffect extends OneShotEffect { TargetPermanent target = new TargetOpponentsCreaturePermanent(); target.setNotTarget(true); target.setRandom(true); - player.chooseTarget(outcome, target, source, game); + target.chooseTarget(outcome, player.getId(), source, game); Permanent permanent = game.getPermanent(target.getFirstTarget()); return permanent != null && permanent.damage(7, source.getSourceId(), source, game) > 0; } diff --git a/Mage.Sets/src/mage/cards/c/CipherboundSpirit.java b/Mage.Sets/src/mage/cards/c/CipherboundSpirit.java new file mode 100644 index 00000000000..6046bb3bbfb --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CipherboundSpirit.java @@ -0,0 +1,50 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.common.CanBlockOnlyFlyingAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DrawDiscardControllerEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CipherboundSpirit extends CardImpl { + + public CipherboundSpirit(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); + + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + this.color.setBlue(true); + this.nightCard = true; + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Cipherbound Spirit can block only creatures with flying. + this.addAbility(new CanBlockOnlyFlyingAbility()); + + // {3}{U}: Draw two cards, then discard a card. + this.addAbility(new SimpleActivatedAbility( + new DrawDiscardControllerEffect(2, 1), new ManaCostsImpl<>("{3}{U}") + )); + } + + private CipherboundSpirit(final CipherboundSpirit card) { + super(card); + } + + @Override + public CipherboundSpirit copy() { + return new CipherboundSpirit(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CircleOfConfinement.java b/Mage.Sets/src/mage/cards/c/CircleOfConfinement.java new file mode 100644 index 00000000000..47ce8354751 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CircleOfConfinement.java @@ -0,0 +1,110 @@ +package mage.cards.c; + +import java.util.UUID; + +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.delayed.OnLeaveReturnExiledToBattlefieldAbility; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.common.ExileUntilSourceLeavesEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterOpponentsCreaturePermanent; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.game.ExileZone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.stack.Spell; +import mage.target.TargetPermanent; +import mage.util.CardUtil; + +/** + * + * @author weirddan455 + */ +public final class CircleOfConfinement extends CardImpl { + + private static final FilterPermanent filter + = new FilterOpponentsCreaturePermanent("creature an opponent controls with mana value 3 or less"); + + static { + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); + } + + public CircleOfConfinement(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); + + // When Circle of Confinement enters the battlefield, exile target creature an opponent controls with mana value 3 or less until Circle of Confinement leaves the battlefield. + Ability ability = new EntersBattlefieldTriggeredAbility( + new ExileUntilSourceLeavesEffect("") + .setText("exile target creature an opponent controls with mana value 3 " + + "or less until {this} leaves the battlefield") + ); + ability.addTarget(new TargetPermanent(filter)); + ability.addEffect(new CreateDelayedTriggeredAbilityEffect(new OnLeaveReturnExiledToBattlefieldAbility())); + this.addAbility(ability); + + // Whenever an opponent casts a Vampire spell with the same name as a card exiled with Circle of Confinement, you gain 2 life. + this.addAbility(new CircleOfConfinementTriggeredAbility()); + } + + private CircleOfConfinement(final CircleOfConfinement card) { + super(card); + } + + @Override + public CircleOfConfinement copy() { + return new CircleOfConfinement(this); + } +} + +class CircleOfConfinementTriggeredAbility extends TriggeredAbilityImpl { + + public CircleOfConfinementTriggeredAbility() { + super(Zone.BATTLEFIELD, new GainLifeEffect(2)); + } + + private CircleOfConfinementTriggeredAbility(final CircleOfConfinementTriggeredAbility ability) { + super(ability); + } + + @Override + public CircleOfConfinementTriggeredAbility copy() { + return new CircleOfConfinementTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.SPELL_CAST; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (game.getOpponents(controllerId).contains(event.getPlayerId())) { + Spell spell = game.getSpell(event.getTargetId()); + if (spell != null && spell.hasSubtype(SubType.VAMPIRE, game)) { + ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, sourceId, game.getState().getZoneChangeCounter(sourceId))); + if (exileZone != null) { + for (UUID cardId : exileZone) { + if (CardUtil.haveSameNames(spell, game.getCard(cardId))) { + return true; + } + } + } + } + } + return false; + } + + @Override + public String getTriggerPhrase() { + return "Whenever an opponent casts a Vampire spell with the same name as a card exiled with {this}, "; + } +} diff --git a/Mage.Sets/src/mage/cards/c/CircleOfFlame.java b/Mage.Sets/src/mage/cards/c/CircleOfFlame.java index fa9e9fa0901..28ff68ac006 100644 --- a/Mage.Sets/src/mage/cards/c/CircleOfFlame.java +++ b/Mage.Sets/src/mage/cards/c/CircleOfFlame.java @@ -80,7 +80,7 @@ class CircleOfFlameTriggeredAbility extends TriggeredAbilityImpl { } if (youOrYourPlaneswalker) { for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(attacker.getId())); + effect.setTargetPointer(new FixedTarget(attacker.getId(), game)); } return true; } diff --git a/Mage.Sets/src/mage/cards/c/CircuitMender.java b/Mage.Sets/src/mage/cards/c/CircuitMender.java new file mode 100644 index 00000000000..635d9ba337a --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CircuitMender.java @@ -0,0 +1,42 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.LeavesBattlefieldTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CircuitMender extends CardImpl { + + public CircuitMender(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}"); + + this.subtype.add(SubType.INSECT); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // When Circuit Mender enters the battlefield, you gain 2 life. + this.addAbility(new EntersBattlefieldTriggeredAbility(new GainLifeEffect(2))); + + // When Circuit Mender leaves the battlefield, draw a card. + this.addAbility(new LeavesBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(1), false)); + } + + private CircuitMender(final CircuitMender card) { + super(card); + } + + @Override + public CircuitMender copy() { + return new CircuitMender(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CitystalkerConnoisseur.java b/Mage.Sets/src/mage/cards/c/CitystalkerConnoisseur.java new file mode 100644 index 00000000000..746a87e1ac8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CitystalkerConnoisseur.java @@ -0,0 +1,101 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.DeathtouchAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.game.Game; +import mage.game.permanent.token.BloodToken; +import mage.players.Player; +import mage.target.common.TargetCardInHand; +import mage.target.common.TargetOpponent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CitystalkerConnoisseur extends CardImpl { + + public CitystalkerConnoisseur(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); + + this.subtype.add(SubType.VAMPIRE); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Deathtouch + this.addAbility(DeathtouchAbility.getInstance()); + + // When Citystalker Connoisseur enters the battlefield, target opponent discards a card with the greatest mana value among cards in their hand. Create a Blood token. + Ability ability = new EntersBattlefieldTriggeredAbility(new CitystalkerConnoisseurEffect()); + ability.addEffect(new CreateTokenEffect(new BloodToken())); + ability.addTarget(new TargetOpponent()); + this.addAbility(ability); + } + + private CitystalkerConnoisseur(final CitystalkerConnoisseur card) { + super(card); + } + + @Override + public CitystalkerConnoisseur copy() { + return new CitystalkerConnoisseur(this); + } +} + +class CitystalkerConnoisseurEffect extends OneShotEffect { + + CitystalkerConnoisseurEffect() { + super(Outcome.Discard); + staticText = "target opponent discards a card with the greatest mana value among cards in their hand"; + } + + private CitystalkerConnoisseurEffect(final CitystalkerConnoisseurEffect effect) { + super(effect); + } + + @Override + public CitystalkerConnoisseurEffect copy() { + return new CitystalkerConnoisseurEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getFirstTarget()); + if (player == null || player.getHand().isEmpty()) { + return false; + } + if (player.getHand().size() == 1) { + player.discard(player.getHand(), false, source, game); + return true; + } + int maxValue = player + .getHand() + .getCards(game) + .stream() + .mapToInt(MageObject::getManaValue) + .max() + .orElse(0); + Cards cards = new CardsImpl(player.getHand()); + cards.removeIf(uuid -> game.getCard(uuid).getManaValue() < maxValue); + if (cards.size() == 1) { + player.discard(cards, false, source, game); + return true; + } + TargetCardInHand target = new TargetCardInHand(); + player.choose(outcome, cards, target, game); + player.discard(cards.get(target.getFirstTarget(), game), false, source, game); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/c/CivilizedScholar.java b/Mage.Sets/src/mage/cards/c/CivilizedScholar.java index 2810cb556df..be6d388e56a 100644 --- a/Mage.Sets/src/mage/cards/c/CivilizedScholar.java +++ b/Mage.Sets/src/mage/cards/c/CivilizedScholar.java @@ -1,4 +1,3 @@ - package mage.cards.c; import java.util.UUID; @@ -12,7 +11,6 @@ import mage.abilities.keyword.TransformAbility; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.cards.h.HomicidalBruteWatcher; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Outcome; @@ -31,7 +29,6 @@ public final class CivilizedScholar extends CardImpl { this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.ADVISOR); - this.transformable = true; this.secondSideCardClazz = mage.cards.h.HomicidalBrute.class; this.power = new MageInt(0); @@ -39,7 +36,7 @@ public final class CivilizedScholar extends CardImpl { // {tap}: Draw a card, then discard a card. If a creature card is discarded this way, untap Civilized Scholar, then transform it. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new CivilizedScholarEffect(), new TapSourceCost())); - this.addAbility(new TransformAbility(), new HomicidalBruteWatcher()); + this.addAbility(new TransformAbility()); } private CivilizedScholar(final CivilizedScholar card) { @@ -80,7 +77,7 @@ class CivilizedScholarEffect extends OneShotEffect { Permanent permanent = game.getPermanent(source.getSourceId()); if (permanent != null) { permanent.untap(game); - permanent.transform(game); + permanent.transform(source, game); } } return true; diff --git a/Mage.Sets/src/mage/cards/c/ClackbridgeTroll.java b/Mage.Sets/src/mage/cards/c/ClackbridgeTroll.java index d92918efb05..0388d41c922 100644 --- a/Mage.Sets/src/mage/cards/c/ClackbridgeTroll.java +++ b/Mage.Sets/src/mage/cards/c/ClackbridgeTroll.java @@ -14,6 +14,7 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.TargetController; +import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterControlledPermanent; import mage.game.Game; import mage.game.permanent.Permanent; @@ -65,6 +66,8 @@ public final class ClackbridgeTroll extends CardImpl { class ClackbridgeTrollEffect extends OneShotEffect { + private static final FilterControlledPermanent filter = new FilterControlledCreaturePermanent("creature to sacrifice"); + ClackbridgeTrollEffect() { super(Outcome.Benefit); staticText = "any opponent may sacrifice a creature. If a player does, " + @@ -83,19 +86,12 @@ class ClackbridgeTrollEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Permanent sourcePerm = game.getPermanent(source.getSourceId()); - if (controller == null) { - return false; - } boolean flag = false; for (UUID opponentId : game.getOpponents(controller.getId())) { Player opponent = game.getPlayer(opponentId); if (opponent == null) { continue; } - FilterControlledPermanent filter = new FilterControlledPermanent("creature to sacrifice"); - filter.add(CardType.CREATURE.getPredicate()); - filter.add(TargetController.YOU.getControllerPredicate()); TargetControlledPermanent target = new TargetControlledPermanent(filter); target.setNotTarget(true); if (!target.canChoose(source.getSourceId(), opponent.getId(), game) @@ -110,6 +106,7 @@ class ClackbridgeTrollEffect extends OneShotEffect { flag = true; } if (flag) { + Permanent sourcePerm = source.getSourcePermanentIfItStillExists(game); if (sourcePerm != null) { sourcePerm.tap(source, game); } diff --git a/Mage.Sets/src/mage/cards/c/ClarionUltimatum.java b/Mage.Sets/src/mage/cards/c/ClarionUltimatum.java index 40b21884879..53c5deacce1 100644 --- a/Mage.Sets/src/mage/cards/c/ClarionUltimatum.java +++ b/Mage.Sets/src/mage/cards/c/ClarionUltimatum.java @@ -16,6 +16,7 @@ import mage.players.Player; import mage.target.TargetPermanent; import mage.target.common.TargetCardInLibrary; import mage.target.common.TargetControlledPermanent; +import mage.util.CardUtil; import java.util.*; import java.util.stream.Collectors; @@ -115,7 +116,7 @@ class ClarionUltimatumTarget extends TargetCardInLibrary { } private void populateNameMap(Set names) { - names.stream().forEach(name -> this.nameMap.compute(name, (s, i) -> i == null ? 1 : Integer.sum(i, 1))); + names.stream().forEach(name -> this.nameMap.compute(name, CardUtil::setOrIncrementValue)); } @Override @@ -133,7 +134,7 @@ class ClarionUltimatumTarget extends TargetCardInLibrary { .map(game::getCard) .filter(Objects::nonNull) .map(MageObject::getName) - .forEach(name -> alreadyChosen.compute(name, (s, i) -> i == null ? 1 : Integer.sum(i, 1))); + .forEach(name -> alreadyChosen.compute(name, CardUtil::setOrIncrementValue)); return nameMap.getOrDefault(card.getName(), 0) > alreadyChosen.getOrDefault(card.getName(), 0); } diff --git a/Mage.Sets/src/mage/cards/c/ClashOfTitans.java b/Mage.Sets/src/mage/cards/c/ClashOfTitans.java index 616d399b27f..c9405028f82 100644 --- a/Mage.Sets/src/mage/cards/c/ClashOfTitans.java +++ b/Mage.Sets/src/mage/cards/c/ClashOfTitans.java @@ -4,8 +4,7 @@ import mage.abilities.effects.common.FightTargetsEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.other.AnotherTargetPredicate; +import mage.filter.StaticFilters; import mage.target.Target; import mage.target.common.TargetCreaturePermanent; @@ -16,12 +15,6 @@ import java.util.UUID; */ public final class ClashOfTitans extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); - - static { - filter.add(new AnotherTargetPredicate(2)); - } - public ClashOfTitans(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{R}{R}"); @@ -31,7 +24,7 @@ public final class ClashOfTitans extends CardImpl { target.setTargetTag(1); this.getSpellAbility().addTarget(target); - target = new TargetCreaturePermanent(filter); + target = new TargetCreaturePermanent(StaticFilters.FILTER_ANOTHER_CREATURE_TARGET_2); target.setTargetTag(2); this.getSpellAbility().addTarget(target); } diff --git a/Mage.Sets/src/mage/cards/c/ClawingTorment.java b/Mage.Sets/src/mage/cards/c/ClawingTorment.java new file mode 100644 index 00000000000..abaf3b95e2a --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ClawingTorment.java @@ -0,0 +1,69 @@ +package mage.cards.c; + +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.AttachedToMatchesFilterCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.decorator.ConditionalRestrictionEffect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.LoseLifeSourceControllerEffect; +import mage.abilities.effects.common.combat.CantBlockAttachedEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ClawingTorment extends CardImpl { + + private static final Condition condition = new AttachedToMatchesFilterCondition(StaticFilters.FILTER_PERMANENT_CREATURE); + + public ClawingTorment(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{B}"); + + this.subtype.add(SubType.AURA); + + // Enchant artifact 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 -1/-1 and can't block. + Ability ability = new SimpleStaticAbility(new ConditionalContinuousEffect( + new BoostEnchantedEffect(-1, -1), condition, + "as long as enchanted permanent is a creature, it gets -1/-1" + )); + ability.addEffect(new ConditionalRestrictionEffect( + new CantBlockAttachedEffect(AttachmentType.AURA), condition, "and can't block" + )); + this.addAbility(ability); + + // Enchanted permanent has "At the beginning of your upkeep, you lose 1 life." + this.addAbility(new SimpleStaticAbility(new GainAbilityAttachedEffect( + new BeginningOfUpkeepTriggeredAbility( + new LoseLifeSourceControllerEffect(1), TargetController.YOU, false + ), AttachmentType.AURA, Duration.WhileOnBattlefield, + "enchanted permanent has \"At the beginning of your upkeep, you lose 1 life.\"" + ))); + } + + private ClawingTorment(final ClawingTorment card) { + super(card); + } + + @Override + public ClawingTorment copy() { + return new ClawingTorment(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CleansingMeditation.java b/Mage.Sets/src/mage/cards/c/CleansingMeditation.java index 43613b82d1d..1c7a6a19ea6 100644 --- a/Mage.Sets/src/mage/cards/c/CleansingMeditation.java +++ b/Mage.Sets/src/mage/cards/c/CleansingMeditation.java @@ -72,7 +72,7 @@ class CleansingMeditationEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); - for (Permanent permanent : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_ENCHANTMENT_PERMANENT, source.getControllerId(), source.getSourceId(), game)) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_ENCHANTMENT, source.getControllerId(), source.getSourceId(), game)) { if (permanent != null && permanent.destroy(source, game, false)) { if (threshold && controller != null && permanent.isOwnedBy(controller.getId())) { cardsToBattlefield.add(permanent); diff --git a/Mage.Sets/src/mage/cards/c/CleavingSliver.java b/Mage.Sets/src/mage/cards/c/CleavingSliver.java index b3da1f12439..276865cf0c5 100644 --- a/Mage.Sets/src/mage/cards/c/CleavingSliver.java +++ b/Mage.Sets/src/mage/cards/c/CleavingSliver.java @@ -27,7 +27,7 @@ public final class CleavingSliver extends CardImpl { // Sliver creatures you control get +2/+0. this.addAbility(new SimpleStaticAbility(new BoostControlledEffect( 2, 0, Duration.WhileOnBattlefield, - StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS + StaticFilters.FILTER_PERMANENT_SLIVERS ))); } diff --git a/Mage.Sets/src/mage/cards/c/CleverDistraction.java b/Mage.Sets/src/mage/cards/c/CleverDistraction.java new file mode 100644 index 00000000000..aca1c3e7953 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CleverDistraction.java @@ -0,0 +1,70 @@ +package mage.cards.c; + +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.common.PutIntoGraveFromAnywhereSourceAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.ExileSourceEffect; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.DefendingPlayerControlsPredicate; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CleverDistraction extends CardImpl { + + private static final FilterPermanent filter + = new FilterCreaturePermanent("creature defending player controls"); + + static { + filter.add(DefendingPlayerControlsPredicate.instance); + } + + public CleverDistraction(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, ""); + + this.subtype.add(SubType.AURA); + this.color.setWhite(true); + this.nightCard = true; + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // Enchanted creature has "Whenever this creature attacks, tap target creature defending player controls." + ability = new AttacksTriggeredAbility(new TapTargetEffect()) + .setTriggerPhrase("Whenever this creature attacks, "); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(new SimpleStaticAbility(new GainAbilityAttachedEffect(ability, AttachmentType.AURA))); + + // If Clever Distracting would be put into a graveyard from anywhere, exile it instead. + this.addAbility(new PutIntoGraveFromAnywhereSourceAbility(new ExileSourceEffect().setText("exile it instead"))); + } + + private CleverDistraction(final CleverDistraction card) { + super(card); + } + + @Override + public CleverDistraction copy() { + return new CleverDistraction(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/ClingingMists.java b/Mage.Sets/src/mage/cards/c/ClingingMists.java index 6392c7f2276..7506823282f 100644 --- a/Mage.Sets/src/mage/cards/c/ClingingMists.java +++ b/Mage.Sets/src/mage/cards/c/ClingingMists.java @@ -12,7 +12,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; -import mage.filter.common.FilterAttackingCreature; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTargets; @@ -51,8 +51,6 @@ public final class ClingingMists extends CardImpl { class ClingingMistsEffect extends OneShotEffect { - private static final FilterAttackingCreature filter = new FilterAttackingCreature("attacking creatures"); - public ClingingMistsEffect() { super(Outcome.Tap); staticText = "tap all attacking creatures. Those creatures don't untap during their controller's next untap step"; @@ -65,7 +63,7 @@ class ClingingMistsEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { List doNotUntapNextUntapStep = new ArrayList<>(); - for (Permanent creature : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { + for (Permanent creature : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_ATTACKING_CREATURES, source.getControllerId(), source.getSourceId(), game)) { creature.tap(source, game); doNotUntapNextUntapStep.add(creature); } @@ -81,5 +79,4 @@ class ClingingMistsEffect extends OneShotEffect { public ClingingMistsEffect copy() { return new ClingingMistsEffect(this); } - } diff --git a/Mage.Sets/src/mage/cards/c/CloakAndDagger.java b/Mage.Sets/src/mage/cards/c/CloakAndDagger.java index 738beb52aa3..8901bc61d92 100644 --- a/Mage.Sets/src/mage/cards/c/CloakAndDagger.java +++ b/Mage.Sets/src/mage/cards/c/CloakAndDagger.java @@ -1,4 +1,3 @@ - package mage.cards.c; import java.util.UUID; @@ -23,11 +22,7 @@ import mage.filter.common.FilterCreaturePermanent; */ public final class CloakAndDagger extends CardImpl { - private static final FilterPermanent filter = new FilterCreaturePermanent("a Rogue creature"); - - static { - filter.add(SubType.ROGUE.getPredicate()); - } + private static final FilterPermanent filter = new FilterCreaturePermanent(SubType.ROGUE, "a Rogue creature"); public CloakAndDagger(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.TRIBAL, CardType.ARTIFACT}, "{2}"); diff --git a/Mage.Sets/src/mage/cards/c/CloakedCadet.java b/Mage.Sets/src/mage/cards/c/CloakedCadet.java new file mode 100644 index 00000000000..da63a120065 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CloakedCadet.java @@ -0,0 +1,83 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.keyword.TrainingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CloakedCadet extends CardImpl { + + public CloakedCadet(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.RANGER); + this.power = new MageInt(2); + this.toughness = new MageInt(4); + + // Training + this.addAbility(new TrainingAbility()); + + // Whenever one or more +1/+1 counters are put on one or more Humans you control, draw a card. This ability triggers only once each turn. + this.addAbility(new CloakedCadetTriggeredAbility()); + } + + private CloakedCadet(final CloakedCadet card) { + super(card); + } + + @Override + public CloakedCadet copy() { + return new CloakedCadet(this); + } +} + +class CloakedCadetTriggeredAbility extends TriggeredAbilityImpl { + + CloakedCadetTriggeredAbility() { + super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1)); + this.setTriggersOnce(true); + } + + private CloakedCadetTriggeredAbility(final CloakedCadetTriggeredAbility ability) { + super(ability); + } + + @Override + public CloakedCadetTriggeredAbility copy() { + return new CloakedCadetTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.COUNTER_ADDED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent permanent = game.getPermanent(event.getTargetId()); + return permanent != null + && isControlledBy(permanent.getControllerId()) + && permanent.hasSubtype(SubType.HUMAN, game) + && event.getData().equals(CounterType.P1P1.getName()); + } + + @Override + public String getTriggerPhrase() { + return "Whenever one or more +1/+1 counters are put on one or more Humans you control, "; + } +} diff --git a/Mage.Sets/src/mage/cards/c/Clockspinning.java b/Mage.Sets/src/mage/cards/c/Clockspinning.java index 5dcbb3b8adb..cd1771f9f8b 100644 --- a/Mage.Sets/src/mage/cards/c/Clockspinning.java +++ b/Mage.Sets/src/mage/cards/c/Clockspinning.java @@ -136,14 +136,14 @@ class ClockspinningAddOrRemoveCounterEffect extends OneShotEffect { if (player != null && permanent != null) { if (player.chooseUse(Outcome.Neutral, "Remove a counter?", source, game)) { RemoveCounterTargetEffect effect = new RemoveCounterTargetEffect(); - effect.setTargetPointer(new FixedTarget(source.getFirstTarget())); + effect.setTargetPointer(new FixedTarget(source.getFirstTarget(), game)); effect.apply(game, source); } else { Counter counter = selectCounterType(game, source, permanent); if (counter != null) { AddCountersTargetEffect effect = new AddCountersTargetEffect(counter); - effect.setTargetPointer(new FixedTarget(source.getFirstTarget())); + effect.setTargetPointer(new FixedTarget(source.getFirstTarget(), game)); effect.apply(game, source); } } @@ -155,13 +155,13 @@ class ClockspinningAddOrRemoveCounterEffect extends OneShotEffect { if (player.chooseUse(Outcome.Neutral, "Remove a counter?", source, game)) { Counter counter = selectCounterType(game, source, card); RemoveCounterTargetEffect effect = new RemoveCounterTargetEffect(counter); - effect.setTargetPointer(new FixedTarget(source.getFirstTarget())); + effect.setTargetPointer(new FixedTarget(source.getFirstTarget(), game)); effect.apply(game, source); } else { Counter counter = selectCounterType(game, source, card); if (counter != null) { AddCountersTargetEffect effect = new AddCountersTargetEffect(counter); - effect.setTargetPointer(new FixedTarget(source.getFirstTarget())); + effect.setTargetPointer(new FixedTarget(source.getFirstTarget(), game)); effect.apply(game, source); } } diff --git a/Mage.Sets/src/mage/cards/c/CloisteredYouth.java b/Mage.Sets/src/mage/cards/c/CloisteredYouth.java index 09cecfb9115..d98ba02068c 100644 --- a/Mage.Sets/src/mage/cards/c/CloisteredYouth.java +++ b/Mage.Sets/src/mage/cards/c/CloisteredYouth.java @@ -25,12 +25,11 @@ public final class CloisteredYouth extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); - this.transformable = true; this.secondSideCardClazz = mage.cards.u.UnholyFiend.class; // At the beginning of your upkeep, you may transform Cloistered Youth. this.addAbility(new TransformAbility()); - this.addAbility(new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(true), TargetController.YOU, true)); + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(), TargetController.YOU, true)); } private CloisteredYouth(final CloisteredYouth card) { diff --git a/Mage.Sets/src/mage/cards/c/CloneShell.java b/Mage.Sets/src/mage/cards/c/CloneShell.java index 6ce2cfdaa2d..95bf49d9189 100644 --- a/Mage.Sets/src/mage/cards/c/CloneShell.java +++ b/Mage.Sets/src/mage/cards/c/CloneShell.java @@ -69,7 +69,7 @@ class CloneShellEffect extends OneShotEffect { Cards cards = new CardsImpl(controller.getLibrary().getTopCards(game, 4)); if (!cards.isEmpty()) { TargetCard target1 = new TargetCard(Zone.LIBRARY, filter1); - if (controller.choose(Outcome.Detriment, cards, target1, game)) { + if (controller.choose(Outcome.Benefit, cards, target1, game)) { Card card = cards.get(target1.getFirstTarget(), game); if (card != null) { cards.remove(card); @@ -112,12 +112,15 @@ class CloneShellDiesEffect extends OneShotEffect { Permanent permanent = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); if (permanent != null) { List imprinted = permanent.getImprinted(); - if (imprinted != null && !imprinted.isEmpty()) { - Card imprintedCard = game.getCard(imprinted.get(0)); - if (imprintedCard != null) { - imprintedCard.setFaceDown(false, game); - if (imprintedCard.isCreature(game)) { - controller.moveCards(imprintedCard, Zone.BATTLEFIELD, source, game); + if (imprinted != null + && !imprinted.isEmpty()) { + for (UUID imprintedId : imprinted) { + Card imprintedCard = game.getCard(imprintedId); + if (imprintedCard != null) { + imprintedCard.setFaceDown(false, game); + if (imprintedCard.isCreature(game)) { + controller.moveCards(imprintedCard, Zone.BATTLEFIELD, source, game); + } } } } diff --git a/Mage.Sets/src/mage/cards/c/CloseQuarters.java b/Mage.Sets/src/mage/cards/c/CloseQuarters.java index eed5485b4ca..5f0b974987a 100644 --- a/Mage.Sets/src/mage/cards/c/CloseQuarters.java +++ b/Mage.Sets/src/mage/cards/c/CloseQuarters.java @@ -17,7 +17,7 @@ import mage.target.common.TargetAnyTarget; * @author fireshoes */ public final class CloseQuarters extends CardImpl { - + static final private FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature you control"); static { diff --git a/Mage.Sets/src/mage/cards/c/ClotSliver.java b/Mage.Sets/src/mage/cards/c/ClotSliver.java index be39f61fb12..d0c769b9383 100644 --- a/Mage.Sets/src/mage/cards/c/ClotSliver.java +++ b/Mage.Sets/src/mage/cards/c/ClotSliver.java @@ -1,4 +1,3 @@ - package mage.cards.c; import mage.MageInt; @@ -12,7 +11,6 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.Zone; import mage.filter.FilterPermanent; import java.util.UUID; @@ -22,19 +20,19 @@ import java.util.UUID; */ public final class ClotSliver extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("Slivers"); - - static { - filter.add(SubType.SLIVER.getPredicate()); - } + private static final FilterPermanent filter = new FilterPermanent(SubType.SLIVER, "all Slivers"); public ClotSliver(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}"); this.subtype.add(SubType.SLIVER); this.power = new MageInt(1); this.toughness = new MageInt(1); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateSourceEffect("this permanent"), new GenericManaCost(2)), Duration.WhileOnBattlefield, filter, false))); + this.addAbility(new SimpleStaticAbility(new GainAbilityAllEffect( + new SimpleActivatedAbility( + new RegenerateSourceEffect("this permanent"), new GenericManaCost(2) + ), Duration.WhileOnBattlefield, filter, false + ))); } private ClotSliver(final ClotSliver card) { diff --git a/Mage.Sets/src/mage/cards/c/CloudhoofKirin.java b/Mage.Sets/src/mage/cards/c/CloudhoofKirin.java index b3fcff2f4e2..041fce6be38 100644 --- a/Mage.Sets/src/mage/cards/c/CloudhoofKirin.java +++ b/Mage.Sets/src/mage/cards/c/CloudhoofKirin.java @@ -35,7 +35,7 @@ public final class CloudhoofKirin extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); // Whenever you cast a Spirit or Arcane spell, you may have target player put the top X cards of their library into their graveyard, where X is that spell's converted mana cost. - Ability ability = new SpellCastControllerTriggeredAbility(Zone.BATTLEFIELD, new CloudhoofKirinEffect(), StaticFilters.SPIRIT_OR_ARCANE_CARD, true, true); + Ability ability = new SpellCastControllerTriggeredAbility(Zone.BATTLEFIELD, new CloudhoofKirinEffect(), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true, true); ability.addTarget(new TargetPlayer()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/c/CloudshredderSliver.java b/Mage.Sets/src/mage/cards/c/CloudshredderSliver.java index 15a194d3a3a..203142ec60e 100644 --- a/Mage.Sets/src/mage/cards/c/CloudshredderSliver.java +++ b/Mage.Sets/src/mage/cards/c/CloudshredderSliver.java @@ -30,11 +30,11 @@ public final class CloudshredderSliver extends CardImpl { // Sliver creatures you control have flying and haste. Ability ability = new SimpleStaticAbility(new GainAbilityControlledEffect( FlyingAbility.getInstance(), Duration.WhileOnBattlefield, - StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS + StaticFilters.FILTER_PERMANENT_ALL_SLIVERS ).setText("Sliver creatures you control have flying")); ability.addEffect(new GainAbilityControlledEffect( HasteAbility.getInstance(), Duration.WhileOnBattlefield, - StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS + StaticFilters.FILTER_PERMANENT_ALL_SLIVERS ).setText("and haste")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/c/CloudsteelKirin.java b/Mage.Sets/src/mage/cards/c/CloudsteelKirin.java new file mode 100644 index 00000000000..63b8ed6f133 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CloudsteelKirin.java @@ -0,0 +1,88 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.ReconfigureAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.GameEvent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CloudsteelKirin extends CardImpl { + + public CloudsteelKirin(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{2}{W}"); + + this.subtype.add(SubType.EQUIPMENT); + this.subtype.add(SubType.KIRIN); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Equipped creature has flying and "You can't lose the game and your opponents can't win the game." + Ability ability = new SimpleStaticAbility(new GainAbilityAttachedEffect( + FlyingAbility.getInstance(), AttachmentType.EQUIPMENT + )); + ability.addEffect(new GainAbilityAttachedEffect( + new SimpleStaticAbility(new CloudsteelKirinEffect()), AttachmentType.EQUIPMENT + ).setText("and \"You can't lose the game and your opponents can't win the game.\"")); + this.addAbility(ability); + + // Reconfigure {5} + this.addAbility(new ReconfigureAbility("{5}")); + } + + private CloudsteelKirin(final CloudsteelKirin card) { + super(card); + } + + @Override + public CloudsteelKirin copy() { + return new CloudsteelKirin(this); + } +} + +class CloudsteelKirinEffect extends ContinuousRuleModifyingEffectImpl { + + CloudsteelKirinEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit, false, false); + staticText = "You can't lose the game and your opponents can't win the game"; + } + + private CloudsteelKirinEffect(final CloudsteelKirinEffect effect) { + super(effect); + } + + @Override + public CloudsteelKirinEffect copy() { + return new CloudsteelKirinEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + switch (event.getType()) { + case WINS: + return game.getOpponents(source.getControllerId()).contains(event.getPlayerId()); + case LOSES: + return source.isControlledBy(event.getPlayerId()); + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/c/CoalhaulerSwine.java b/Mage.Sets/src/mage/cards/c/CoalhaulerSwine.java index b55a43589fb..87acbeb4bf3 100644 --- a/Mage.Sets/src/mage/cards/c/CoalhaulerSwine.java +++ b/Mage.Sets/src/mage/cards/c/CoalhaulerSwine.java @@ -1,16 +1,15 @@ package mage.cards.c; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.DealtDamageToSourceTriggeredAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.dynamicvalue.common.SavedDamageValue; +import mage.abilities.effects.common.DamagePlayersEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; -import mage.game.Game; -import mage.players.Player; +import mage.constants.TargetController; import java.util.UUID; @@ -28,7 +27,9 @@ public final class CoalhaulerSwine extends CardImpl { this.toughness = new MageInt(4); // Whenever Coalhauler Swine is dealt damage, it deals that much damage to each player. - this.addAbility(new DealtDamageToSourceTriggeredAbility(new CoalhaulerSwineEffect(), false, false, true)); + this.addAbility(new DealtDamageToSourceTriggeredAbility(new DamagePlayersEffect( + Outcome.Neutral, SavedDamageValue.instance, TargetController.ANY, "it" + ), false, false)); } private CoalhaulerSwine(final CoalhaulerSwine card) { @@ -39,33 +40,4 @@ public final class CoalhaulerSwine extends CardImpl { public CoalhaulerSwine copy() { return new CoalhaulerSwine(this); } - - static class CoalhaulerSwineEffect extends OneShotEffect { - - public CoalhaulerSwineEffect() { - super(Outcome.Damage); - staticText = "it deals that much damage to each player"; - } - - public CoalhaulerSwineEffect(final CoalhaulerSwineEffect effect) { - super(effect); - } - - @Override - public CoalhaulerSwineEffect copy() { - return new CoalhaulerSwineEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - for (UUID playerId : game.getPlayers().keySet()) { - Player player = game.getPlayer(playerId); - if (player != null) { - player.damage((Integer) this.getValue("damage"), source.getSourceId(), source, game); - } - } - return true; - } - - } } diff --git a/Mage.Sets/src/mage/cards/c/CoaxFromTheBlindEternities.java b/Mage.Sets/src/mage/cards/c/CoaxFromTheBlindEternities.java index 8c94e731250..a38308132f8 100644 --- a/Mage.Sets/src/mage/cards/c/CoaxFromTheBlindEternities.java +++ b/Mage.Sets/src/mage/cards/c/CoaxFromTheBlindEternities.java @@ -16,7 +16,7 @@ import mage.filter.FilterCard; */ public final class CoaxFromTheBlindEternities extends CardImpl { - private static final FilterCard filter = new FilterCard("an Eldrazi card"); + private static final FilterCard filter = new FilterCard("Eldrazi card"); static { filter.add(SubType.ELDRAZI.getPredicate()); @@ -25,8 +25,10 @@ public final class CoaxFromTheBlindEternities extends CardImpl { public CoaxFromTheBlindEternities(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{U}"); - // You may choose an Eldrazi card you own from outside the game or in exile, reveal that card, and put it into your hand. - this.getSpellAbility().addEffect(new WishEffect(filter, true, true)); + // You may reveal an Eldrazi card you own from outside the game + // or choose a face-up Eldrazi card you own in exile. + // Put that card into your hand. + this.getSpellAbility().addEffect(new WishEffect(filter, true)); this.getSpellAbility().addHint(OpenSideboardHint.instance); } diff --git a/Mage.Sets/src/mage/cards/c/CobbledLancer.java b/Mage.Sets/src/mage/cards/c/CobbledLancer.java new file mode 100644 index 00000000000..2412b94826d --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CobbledLancer.java @@ -0,0 +1,50 @@ +package mage.cards.c; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.ExileFromGraveCost; +import mage.abilities.costs.common.ExileSourceFromGraveCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.target.common.TargetCardInYourGraveyard; + +/** + * + * @author weirddan455 + */ +public final class CobbledLancer extends CardImpl { + + public CobbledLancer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{U}"); + + this.subtype.add(SubType.ZOMBIE); + this.subtype.add(SubType.HORSE); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // As an additional cost to cast this spell, exile a creature card from your graveyard. + this.getSpellAbility().addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD))); + + // {3}{U}, Exile Cobbled Lancer from your graveyard: Draw a card. + Ability ability = new SimpleActivatedAbility(Zone.GRAVEYARD, new DrawCardSourceControllerEffect(1), new ManaCostsImpl<>("{3}{U}")); + ability.addCost(new ExileSourceFromGraveCost()); + this.addAbility(ability); + } + + private CobbledLancer(final CobbledLancer card) { + super(card); + } + + @Override + public CobbledLancer copy() { + return new CobbledLancer(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CodieVociferousCodex.java b/Mage.Sets/src/mage/cards/c/CodieVociferousCodex.java index 533d8dec1a0..42362e84333 100644 --- a/Mage.Sets/src/mage/cards/c/CodieVociferousCodex.java +++ b/Mage.Sets/src/mage/cards/c/CodieVociferousCodex.java @@ -170,7 +170,7 @@ class CodieVociferousCodexEffect extends OneShotEffect { } controller.moveCards(toCast, Zone.EXILED, source, game); PlayFromNotOwnHandZoneTargetEffect effect = new PlayFromNotOwnHandZoneTargetEffect(Zone.EXILED, TargetController.YOU, Duration.EndOfTurn, true); - effect.setTargetPointer(new FixedTarget(toCast.getId())); + effect.setTargetPointer(new FixedTarget(toCast.getId(), game)); game.addEffect(effect, source); toExile.remove(toCast); controller.putCardsOnBottomOfLibrary(toExile, game, source, false); diff --git a/Mage.Sets/src/mage/cards/c/CogworkAssembler.java b/Mage.Sets/src/mage/cards/c/CogworkAssembler.java index 89bd2f958eb..bac94ec4957 100644 --- a/Mage.Sets/src/mage/cards/c/CogworkAssembler.java +++ b/Mage.Sets/src/mage/cards/c/CogworkAssembler.java @@ -73,7 +73,7 @@ class CogworkAssemblerCreateTokenEffect extends OneShotEffect { if (copiedPermanent != null) { CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(null, CardType.ARTIFACT, true); if (effect.apply(game, source)) { - for (Permanent copyPermanent : effect.getAddedPermanent()) { + for (Permanent copyPermanent : effect.getAddedPermanents()) { ExileTargetEffect exileEffect = new ExileTargetEffect(); exileEffect.setTargetPointer(new FixedTarget(copyPermanent, game)); DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); diff --git a/Mage.Sets/src/mage/cards/c/CoilingStalker.java b/Mage.Sets/src/mage/cards/c/CoilingStalker.java new file mode 100644 index 00000000000..4990d4c8845 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CoilingStalker.java @@ -0,0 +1,72 @@ +package mage.cards.c; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.constants.SubType; +import mage.abilities.keyword.NinjutsuAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.counters.CounterType; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.Predicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetControlledCreaturePermanent; + +/** + * + * @author weirddan455 + */ +public final class CoilingStalker extends CardImpl { + + private static final FilterControlledCreaturePermanent filter + = new FilterControlledCreaturePermanent("creature you control that doesn't have a +1/+1 counter on it"); + + static { + filter.add(CoilingStalkerPredicate.instance); + } + + public CoilingStalker(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); + + this.subtype.add(SubType.SNAKE); + this.subtype.add(SubType.NINJA); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Ninjutsu {1}{G} + this.addAbility(new NinjutsuAbility("{1}{G}")); + + // Whenever Coiling Stalker deals combat damage to a player, put a +1/+1 counter on target creature you control that doesn't have a +1/+1 counter on it. + Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance()), false); + ability.addTarget(new TargetControlledCreaturePermanent(filter)); + this.addAbility(ability); + } + + private CoilingStalker(final CoilingStalker card) { + super(card); + } + + @Override + public CoilingStalker copy() { + return new CoilingStalker(this); + } +} + +enum CoilingStalkerPredicate implements Predicate { + instance; + + @Override + public boolean apply(Permanent input, Game game) { + return input.getCounters(game).getCount(CounterType.P1P1) == 0; + } + + @Override + public String toString() { + return "doesn't have a +1/+1 counter on it"; + } +} diff --git a/Mage.Sets/src/mage/cards/c/ColdStorage.java b/Mage.Sets/src/mage/cards/c/ColdStorage.java index db7f195974c..c2e91ed5c0c 100644 --- a/Mage.Sets/src/mage/cards/c/ColdStorage.java +++ b/Mage.Sets/src/mage/cards/c/ColdStorage.java @@ -1,36 +1,39 @@ - package mage.cards.c; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; -import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.common.ExileTargetEffect; -import mage.abilities.effects.common.ReturnCreaturesFromExileEffect; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ExileTargetForSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.Outcome; import mage.constants.Zone; +import mage.game.ExileZone; +import mage.game.Game; +import mage.players.Player; import mage.target.common.TargetControlledCreaturePermanent; +import mage.util.CardUtil; + +import java.util.UUID; /** - * * @author Plopman */ public final class ColdStorage extends CardImpl { public ColdStorage(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{4}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); // {3}: Exile target creature you control. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileTargetEffect(this.getId(), this.getIdName()), new ManaCostsImpl("{3}")); + Ability ability = new SimpleActivatedAbility(new ExileTargetForSourceEffect(), new GenericManaCost(3)); ability.addTarget(new TargetControlledCreaturePermanent()); this.addAbility(ability); + // Sacrifice Cold Storage: Return each creature card exiled with Cold Storage to the battlefield under your control. - ReturnCreaturesFromExileEffect returnFromExileEffect = new ReturnCreaturesFromExileEffect(this.getId(), false, "Return each creature card exiled with {this} to the battlefield under your control"); - ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, returnFromExileEffect, new SacrificeSourceCost()); - this.addAbility(ability); + this.addAbility(new SimpleActivatedAbility(new ColdStorageEffect(), new SacrificeSourceCost())); } private ColdStorage(final ColdStorage card) { @@ -42,3 +45,30 @@ public final class ColdStorage extends CardImpl { return new ColdStorage(this); } } + +class ColdStorageEffect extends OneShotEffect { + + ColdStorageEffect() { + super(Outcome.Benefit); + staticText = "return each creature card exiled with {this} to the battlefield under your control"; + } + + private ColdStorageEffect(final ColdStorageEffect effect) { + super(effect); + } + + @Override + public ColdStorageEffect copy() { + return new ColdStorageEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source)); + return player != null + && exileZone != null + && !exileZone.isEmpty() + && player.moveCards(exileZone, Zone.BATTLEFIELD, source, game); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CollisionOfRealms.java b/Mage.Sets/src/mage/cards/c/CollisionOfRealms.java new file mode 100644 index 00000000000..0c85fa53d2b --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CollisionOfRealms.java @@ -0,0 +1,103 @@ +package mage.cards.c; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.*; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.PermanentToken; +import mage.players.Player; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CollisionOfRealms extends CardImpl { + + public CollisionOfRealms(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{6}{R}"); + + // Each player shuffles all creatures they own into their library. Each player who shuffled a nontoken creature into their library this way reveals cards from the top of their library until they reveal a creature card, then puts that card onto the battlefield and the rest on the bottom of their library in a random order. + this.getSpellAbility().addEffect(new CollisionOfRealmsEffect()); + } + + private CollisionOfRealms(final CollisionOfRealms card) { + super(card); + } + + @Override + public CollisionOfRealms copy() { + return new CollisionOfRealms(this); + } +} + +class CollisionOfRealmsEffect extends OneShotEffect { + + CollisionOfRealmsEffect() { + super(Outcome.Benefit); + staticText = "each player shuffles all creatures they own into their library. " + + "Each player who shuffled a nontoken creature into their library this way reveals cards " + + "from the top of their library until they reveal a creature card, then puts that card " + + "onto the battlefield and the rest on the bottom of their library in a random order"; + } + + private CollisionOfRealmsEffect(final CollisionOfRealmsEffect effect) { + super(effect); + } + + @Override + public CollisionOfRealmsEffect copy() { + return new CollisionOfRealmsEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + List players = new ArrayList<>(); + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + if (player == null) { + continue; + } + List permanents = game.getBattlefield().getActivePermanents( + StaticFilters.FILTER_CONTROLLED_CREATURE, playerId, game + ); + boolean hasNonToken = permanents + .stream() + .filter(permanent -> !(permanent instanceof PermanentToken)) + .anyMatch(permanent -> permanent.isCreature(game)); + player.putCardsOnBottomOfLibrary(new CardsImpl(permanents), game, source, false); + player.shuffleLibrary(source, game); + if (hasNonToken) { + players.add(player); + } + } + for (Player player : players) { + Cards cards = new CardsImpl(); + Card creature = revealUntilCreature(cards, player, game); + player.revealCards(source, cards, game); + if (creature != null) { + player.moveCards(creature, Zone.BATTLEFIELD, source, game); + } + cards.retainZone(Zone.LIBRARY, game); + player.putCardsOnBottomOfLibrary(cards, game, source, false); + } + return true; + } + + private static Card revealUntilCreature(Cards cards, Player player, Game game) { + for (Card card : player.getLibrary().getCards(game)) { + cards.add(card); + if (card.isCreature(game)) { + return card; + } + } + return null; + } +} diff --git a/Mage.Sets/src/mage/cards/c/ColossalSkyturtle.java b/Mage.Sets/src/mage/cards/c/ColossalSkyturtle.java new file mode 100644 index 00000000000..fa829a03e7b --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ColossalSkyturtle.java @@ -0,0 +1,57 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.abilities.keyword.ChannelAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.WardAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.target.common.TargetCardInYourGraveyard; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ColossalSkyturtle extends CardImpl { + + public ColossalSkyturtle(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{4}{G}{G}{U}"); + + this.subtype.add(SubType.TURTLE); + this.power = new MageInt(6); + this.toughness = new MageInt(5); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Ward {2} + this.addAbility(new WardAbility(new ManaCostsImpl<>("{2}"))); + + // Channel — {2}{G}, Discard Colossal Skyturtle: Return target card from your graveyard to your hand. + Ability ability = new ChannelAbility("{2}{G}", new ReturnFromGraveyardToHandTargetEffect()); + ability.addTarget(new TargetCardInYourGraveyard()); + this.addAbility(ability); + + // Channel — {1}{U}, Discard Colossal Skyturtle: Return target creature to its owner's hand. + ability = new ChannelAbility("{1}{U}", new ReturnToHandTargetEffect()); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + private ColossalSkyturtle(final ColossalSkyturtle card) { + super(card); + } + + @Override + public ColossalSkyturtle copy() { + return new ColossalSkyturtle(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/Commandeer.java b/Mage.Sets/src/mage/cards/c/Commandeer.java index 12322d7f71b..30a294bf99f 100644 --- a/Mage.Sets/src/mage/cards/c/Commandeer.java +++ b/Mage.Sets/src/mage/cards/c/Commandeer.java @@ -12,8 +12,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.filter.FilterCard; -import mage.filter.FilterSpell; -import mage.filter.predicate.Predicates; +import mage.filter.StaticFilters; import mage.filter.predicate.mageobject.ColorPredicate; import mage.game.Game; import mage.game.stack.Spell; @@ -28,10 +27,9 @@ import mage.target.common.TargetCardInHand; public final class Commandeer extends CardImpl { private static final FilterCard filter = new FilterCard("two blue cards"); - private static final FilterSpell filterSpell = new FilterSpell("noncreature spell"); + static { filter.add(new ColorPredicate(ObjectColor.BLUE)); - filterSpell.add(Predicates.not(CardType.CREATURE.getPredicate())); } public Commandeer(UUID ownerId, CardSetInfo setInfo) { @@ -43,7 +41,7 @@ public final class Commandeer extends CardImpl { // Gain control of target noncreature spell. You may choose new targets for it. this.getSpellAbility().addEffect(new CommandeerEffect()); - this.getSpellAbility().addTarget(new TargetSpell(filterSpell)); + this.getSpellAbility().addTarget(new TargetSpell(StaticFilters.FILTER_SPELL_NON_CREATURE)); } private Commandeer(final Commandeer card) { @@ -60,7 +58,10 @@ class CommandeerEffect extends OneShotEffect { public CommandeerEffect() { super(Outcome.Benefit); - this.staticText = "Gain control of target noncreature spell. You may choose new targets for it"; + this.staticText = "Gain control of target noncreature spell. " + + "You may choose new targets for it. " + + " (If that spell is an artifact, enchantment, or planeswalker, " + + "the permanent enters the battlefield under your control.)"; } public CommandeerEffect(final CommandeerEffect effect) { diff --git a/Mage.Sets/src/mage/cards/c/CommuneWithLava.java b/Mage.Sets/src/mage/cards/c/CommuneWithLava.java index 0e4a40dd95d..7288a33b36b 100644 --- a/Mage.Sets/src/mage/cards/c/CommuneWithLava.java +++ b/Mage.Sets/src/mage/cards/c/CommuneWithLava.java @@ -66,7 +66,7 @@ class CommuneWithLavaEffect extends OneShotEffect { for (Card card : cards) { ContinuousEffect effect = new CommuneWithLavaMayPlayEffect(); - effect.setTargetPointer(new FixedTarget(card.getId())); + effect.setTargetPointer(new FixedTarget(card.getId(), game)); game.addEffect(effect, source); } diff --git a/Mage.Sets/src/mage/cards/c/CommuneWithSpirits.java b/Mage.Sets/src/mage/cards/c/CommuneWithSpirits.java new file mode 100644 index 00000000000..1614cb95261 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CommuneWithSpirits.java @@ -0,0 +1,48 @@ +package mage.cards.c; + +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.common.LookLibraryAndPickControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.predicate.Predicates; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CommuneWithSpirits extends CardImpl { + + private static final FilterCard filter = new FilterCard("enchantment or land card"); + + static { + filter.add(Predicates.or( + CardType.ENCHANTMENT.getPredicate(), + CardType.LAND.getPredicate() + )); + } + + public CommuneWithSpirits(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{G}"); + + // Look at the top four cards of your library. You may reveal an enchantment or land card from among them and put it into your hand. Put the rest on the bottom of your library in a random order. + this.getSpellAbility().addEffect(new LookLibraryAndPickControllerEffect( + StaticValue.get(4), false, StaticValue.get(1), filter, Zone.LIBRARY, false, + true, false, Zone.HAND, true, false, false + ).setBackInRandomOrder(true).setText("look at the top four cards of your library. " + + "You may reveal an enchantment or land card from among them and put it into your hand. " + + "Put the rest on the bottom of your library in a random order")); + } + + private CommuneWithSpirits(final CommuneWithSpirits card) { + super(card); + } + + @Override + public CommuneWithSpirits copy() { + return new CommuneWithSpirits(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/ConcealingCurtains.java b/Mage.Sets/src/mage/cards/c/ConcealingCurtains.java new file mode 100644 index 00000000000..ced78dc8f37 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ConcealingCurtains.java @@ -0,0 +1,47 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.keyword.DefenderAbility; +import mage.abilities.keyword.TransformAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ConcealingCurtains extends CardImpl { + + public ConcealingCurtains(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}"); + + this.subtype.add(SubType.WALL); + this.power = new MageInt(0); + this.toughness = new MageInt(4); + this.secondSideCardClazz = mage.cards.r.RevealingEye.class; + + // Defender + this.addAbility(DefenderAbility.getInstance()); + + // {2}{B}: Transform Concealing Curtains. Activate only as a sorcery. + this.addAbility(new TransformAbility()); + this.addAbility(new ActivateAsSorceryActivatedAbility( + new TransformSourceEffect(), new ManaCostsImpl<>("{2}{B}") + )); + } + + private ConcealingCurtains(final ConcealingCurtains card) { + super(card); + } + + @Override + public ConcealingCurtains copy() { + return new ConcealingCurtains(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/ConclaveGuildmage.java b/Mage.Sets/src/mage/cards/c/ConclaveGuildmage.java index a3d40887e38..31e8e6561ef 100644 --- a/Mage.Sets/src/mage/cards/c/ConclaveGuildmage.java +++ b/Mage.Sets/src/mage/cards/c/ConclaveGuildmage.java @@ -36,8 +36,8 @@ public final class ConclaveGuildmage extends CardImpl { new GainAbilityControlledEffect( TrampleAbility.getInstance(), Duration.EndOfTurn, - StaticFilters.FILTER_CONTROLLED_CREATURES - ), new ManaCostsImpl("{G}") + StaticFilters.FILTER_PERMANENT_CREATURES + ), new ManaCostsImpl<>("{G}") ); ability.addCost(new TapSourceCost()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/c/ConcordWithTheKami.java b/Mage.Sets/src/mage/cards/c/ConcordWithTheKami.java new file mode 100644 index 00000000000..841148ba064 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ConcordWithTheKami.java @@ -0,0 +1,91 @@ +package mage.cards.c; + +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.CounterAnyPredicate; +import mage.filter.predicate.permanent.EnchantedPredicate; +import mage.filter.predicate.permanent.EquippedPredicate; +import mage.game.permanent.token.SpiritToken; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ConcordWithTheKami extends CardImpl { + + private static final FilterPermanent counterFilter + = new FilterCreaturePermanent("creature with a counter on it"); + private static final FilterPermanent enchantedFilter + = new FilterControlledCreaturePermanent(); + private static final FilterPermanent equippedFilter + = new FilterControlledCreaturePermanent(); + + static { + counterFilter.add(CounterAnyPredicate.instance); + enchantedFilter.add(EnchantedPredicate.instance); + equippedFilter.add(EquippedPredicate.instance); + } + + private static final Condition enchantedCondition + = new PermanentsOnTheBattlefieldCondition(enchantedFilter); + private static final Condition equippedCondition + = new PermanentsOnTheBattlefieldCondition(equippedFilter); + private static final Hint enchantedHint + = new ConditionHint(enchantedCondition, "You control an enchanted creature"); + private static final Hint equippedHint + = new ConditionHint(equippedCondition, "You control an equipped creature"); + + public ConcordWithTheKami(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{G}"); + + // At the beginning of your end step, choose one or more — + // • Put a +1/+1 counter on target creature with a counter on it. + Ability ability = new BeginningOfEndStepTriggeredAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance()), TargetController.YOU, false + ); + ability.addTarget(new TargetPermanent(counterFilter)); + ability.getModes().setMinModes(1); + ability.getModes().setMaxModes(3); + + // • Draw a card if you control an enchanted creature. + ability.addMode(new Mode(new ConditionalOneShotEffect( + new DrawCardSourceControllerEffect(1), enchantedCondition, + "draw a card if you control an enchanted creature" + ))); + + // • Create a 1/1 colorless Spirit creature token if you control an equipped creature. + ability.addMode(new Mode(new ConditionalOneShotEffect( + new CreateTokenEffect(new SpiritToken()), equippedCondition, + "create a 1/1 colorless Spirit creature token if you control an equipped creature" + ))); + this.addAbility(ability.addHint(enchantedHint).addHint(equippedHint)); + } + + private ConcordWithTheKami(final ConcordWithTheKami card) { + super(card); + } + + @Override + public ConcordWithTheKami copy() { + return new ConcordWithTheKami(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/ConduitOfStorms.java b/Mage.Sets/src/mage/cards/c/ConduitOfStorms.java index 80233f34d53..caa54922039 100644 --- a/Mage.Sets/src/mage/cards/c/ConduitOfStorms.java +++ b/Mage.Sets/src/mage/cards/c/ConduitOfStorms.java @@ -33,7 +33,6 @@ public final class ConduitOfStorms extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(3); - this.transformable = true; this.secondSideCardClazz = ConduitOfEmrakul.class; // Whenever Conduit of Storms attacks, add {R} at the beginning of your next main phase this turn. @@ -44,7 +43,7 @@ public final class ConduitOfStorms extends CardImpl { this.addAbility(new AttacksTriggeredAbility(effect, false)); // {3}{R}{R}: Transform Conduit of Storms. this.addAbility(new TransformAbility()); - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new TransformSourceEffect(true), new ManaCostsImpl("{3}{R}{R}"))); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new TransformSourceEffect(), new ManaCostsImpl("{3}{R}{R}"))); } private ConduitOfStorms(final ConduitOfStorms card) { diff --git a/Mage.Sets/src/mage/cards/c/Conflagrate.java b/Mage.Sets/src/mage/cards/c/Conflagrate.java index f988a7b82d4..305c4e49bb4 100644 --- a/Mage.Sets/src/mage/cards/c/Conflagrate.java +++ b/Mage.Sets/src/mage/cards/c/Conflagrate.java @@ -1,4 +1,3 @@ - package mage.cards.c; import java.util.UUID; @@ -13,8 +12,7 @@ import mage.abilities.keyword.FlashbackAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.TimingRule; -import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.game.Game; import mage.target.common.TargetAnyTargetAmount; @@ -27,14 +25,14 @@ public final class Conflagrate extends CardImpl { public Conflagrate(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{X}{R}"); - // Conflagrate deals X damage divided as you choose among any number of target creatures and/or players. + // Conflagrate deals X damage divided as you choose among any number of targets. DynamicValue xValue = new ConflagrateVariableValue(); this.getSpellAbility().addEffect(new DamageMultiEffect(xValue)); this.getSpellAbility().addTarget(new TargetAnyTargetAmount(xValue)); // Flashback-{R}{R}, Discard X cards. Ability ability = new FlashbackAbility(this, new ManaCostsImpl("{R}{R}")); - ability.addCost(new DiscardXTargetCost(new FilterCard("cards"))); + ability.addCost(new DiscardXTargetCost(StaticFilters.FILTER_CARD_CARDS)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/c/ConfoundingConundrum.java b/Mage.Sets/src/mage/cards/c/ConfoundingConundrum.java index 3747315305e..f393c1e6cd6 100644 --- a/Mage.Sets/src/mage/cards/c/ConfoundingConundrum.java +++ b/Mage.Sets/src/mage/cards/c/ConfoundingConundrum.java @@ -19,6 +19,7 @@ import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetPermanent; +import mage.util.CardUtil; import mage.watchers.Watcher; import java.util.HashMap; @@ -128,7 +129,7 @@ class ConfoundingConundrumWatcher extends Watcher { } Permanent permanent = game.getPermanent(event.getTargetId()); if (permanent != null && permanent.isLand(game)) { - playerMap.compute(permanent.getControllerId(), (u, i) -> i == null ? 1 : Integer.sum(i, 1)); + playerMap.compute(permanent.getControllerId(), CardUtil::setOrIncrementValue); } } diff --git a/Mage.Sets/src/mage/cards/c/ConqueringManticore.java b/Mage.Sets/src/mage/cards/c/ConqueringManticore.java index 55bafbb6239..439e6048dcb 100644 --- a/Mage.Sets/src/mage/cards/c/ConqueringManticore.java +++ b/Mage.Sets/src/mage/cards/c/ConqueringManticore.java @@ -15,8 +15,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; /** @@ -25,12 +24,6 @@ import mage.target.common.TargetCreaturePermanent; */ public final class ConqueringManticore extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public ConqueringManticore(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}{R}"); this.subtype.add(SubType.MANTICORE); @@ -43,7 +36,7 @@ public final class ConqueringManticore extends CardImpl { Ability ability = new EntersBattlefieldTriggeredAbility(new GainControlTargetEffect(Duration.EndOfTurn), false); ability.addEffect(new UntapTargetEffect().setText("Untap that creature")); ability.addEffect(new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn).setText("It gains haste until end of turn.")); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/c/ConquerorsGalleon.java b/Mage.Sets/src/mage/cards/c/ConquerorsGalleon.java index bc45f90e584..9307d6c70e4 100644 --- a/Mage.Sets/src/mage/cards/c/ConquerorsGalleon.java +++ b/Mage.Sets/src/mage/cards/c/ConquerorsGalleon.java @@ -1,7 +1,7 @@ package mage.cards.c; import mage.MageInt; -import mage.abilities.Gender; +import mage.abilities.Pronoun; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; @@ -27,14 +27,13 @@ public final class ConquerorsGalleon extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(10); - this.transformable = true; this.secondSideCardClazz = ConquerorsFoothold.class; // When Conqueror's Galleon attacks, exile it at the end of combat, then return it to the battlefield transformed under your control. this.addAbility(new TransformAbility()); this.addAbility(new AttacksTriggeredAbility( new CreateDelayedTriggeredAbilityEffect( - new AtTheEndOfCombatDelayedTriggeredAbility(new ExileAndReturnTransformedSourceEffect(Gender.NEUTRAL, null, true))), + new AtTheEndOfCombatDelayedTriggeredAbility(new ExileAndReturnTransformedSourceEffect(Pronoun.IT, null, true))), false, "When {this} attacks, exile it at the end of combat, then return it to the battlefield transformed under your control.")); diff --git a/Mage.Sets/src/mage/cards/c/ConstrictingSliver.java b/Mage.Sets/src/mage/cards/c/ConstrictingSliver.java index 5f17ebe72e4..4d352e5fdaf 100644 --- a/Mage.Sets/src/mage/cards/c/ConstrictingSliver.java +++ b/Mage.Sets/src/mage/cards/c/ConstrictingSliver.java @@ -14,7 +14,6 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.StaticFilters; -import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; @@ -26,12 +25,6 @@ import mage.util.CardUtil; */ public final class ConstrictingSliver extends CardImpl { - private static final FilterCreaturePermanent filterTarget = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filterTarget.add(TargetController.OPPONENT.getControllerPredicate()); - } - public ConstrictingSliver(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{W}"); this.subtype.add(SubType.SLIVER); @@ -42,11 +35,11 @@ public final class ConstrictingSliver extends CardImpl { // Sliver creatures you control have "When this creature enters the battlefield, you may exile target creature an opponent controls // until this creature leaves the battlefield." Ability ability = new EntersBattlefieldTriggeredAbility(new ConstrictingSliverExileEffect(), true); - ability.addTarget(new TargetCreaturePermanent(filterTarget)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); ability.addEffect(new CreateDelayedTriggeredAbilityEffect(new OnLeaveReturnExiledToBattlefieldAbility())); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledEffect(ability, - Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS) + Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_ALL_SLIVERS) .setText("Sliver creatures you control have \"When this creature enters the battlefield, " + "you may exile target creature an opponent controls until this creature leaves the battlefield.\""))); diff --git a/Mage.Sets/src/mage/cards/c/ConsulsLieutenant.java b/Mage.Sets/src/mage/cards/c/ConsulsLieutenant.java index d5465a141be..4a1c5be70f7 100644 --- a/Mage.Sets/src/mage/cards/c/ConsulsLieutenant.java +++ b/Mage.Sets/src/mage/cards/c/ConsulsLieutenant.java @@ -12,8 +12,9 @@ import mage.abilities.keyword.RenownAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.TargetController; import mage.filter.common.FilterAttackingCreature; /** @@ -22,6 +23,12 @@ import mage.filter.common.FilterAttackingCreature; */ public final class ConsulsLieutenant extends CardImpl { + private static final FilterAttackingCreature filter = new FilterAttackingCreature("attacking creatures you control"); + + static { + filter.add(TargetController.YOU.getControllerPredicate()); + } + public ConsulsLieutenant(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{W}{W}"); this.subtype.add(SubType.HUMAN); @@ -35,10 +42,9 @@ public final class ConsulsLieutenant extends CardImpl { this.addAbility(new RenownAbility(1)); // Whenever Consul's Lieutenant attacks, if it's renowned, other attacking creatures you control get +1/+1 until end of turn. this.addAbility(new ConditionalInterveningIfTriggeredAbility(new AttacksTriggeredAbility( - new BoostControlledEffect(1, 1, Duration.EndOfTurn, new FilterAttackingCreature("other attacking creatures you control"), true), false), + new BoostControlledEffect(1, 1, Duration.EndOfTurn, filter, true), false), RenownedSourceCondition.instance, "Whenever Consul's Lieutenant attacks, if it's renowned, other attacking creatures you control get +1/+1 until end of turn.")); - } private ConsulsLieutenant(final ConsulsLieutenant card) { diff --git a/Mage.Sets/src/mage/cards/c/ConsulsShieldguard.java b/Mage.Sets/src/mage/cards/c/ConsulsShieldguard.java index 93d11a8282e..2efb58dae72 100644 --- a/Mage.Sets/src/mage/cards/c/ConsulsShieldguard.java +++ b/Mage.Sets/src/mage/cards/c/ConsulsShieldguard.java @@ -25,7 +25,7 @@ import java.util.UUID; */ public final class ConsulsShieldguard extends CardImpl { - private static final FilterAttackingCreature filter = new FilterAttackingCreature(); + private static final FilterAttackingCreature filter = new FilterAttackingCreature("another target attacking creature"); static { filter.add(AnotherPredicate.instance); @@ -41,13 +41,10 @@ public final class ConsulsShieldguard extends CardImpl { // When Consul's Shieldguard enters the battlefield, you get {E}{E}. this.addAbility(new EntersBattlefieldTriggeredAbility(new GetEnergyCountersControllerEffect(2))); - // Whenever Consul's Shiedguard attacks, you may pay {E}. If you do, another target attacking creature gets indestructible until end of turn. - DoIfCostPaid doIfCostPaidEffect = new DoIfCostPaid(new GainAbilityTargetEffect(IndestructibleAbility.getInstance(), Duration.EndOfTurn), new PayEnergyCost(1)); - Ability ability = new AttacksTriggeredAbility(doIfCostPaidEffect, false, - "Whenever {this} attacks, you may pay {E}. If you do, another target attacking creature gets indestructible until end of turn."); + // Whenever Consul's Shieldguard attacks, you may pay {E}. If you do, another target attacking creature gains indestructible until end of turn. + Ability ability = new AttacksTriggeredAbility(new DoIfCostPaid(new GainAbilityTargetEffect(IndestructibleAbility.getInstance(), Duration.EndOfTurn), new PayEnergyCost(1))); ability.addTarget(new TargetCreaturePermanent(filter)); this.addAbility(ability); - } private ConsulsShieldguard(final ConsulsShieldguard card) { diff --git a/Mage.Sets/src/mage/cards/c/ConsumingBlob.java b/Mage.Sets/src/mage/cards/c/ConsumingBlob.java index b49efca1438..0ac5a90b823 100644 --- a/Mage.Sets/src/mage/cards/c/ConsumingBlob.java +++ b/Mage.Sets/src/mage/cards/c/ConsumingBlob.java @@ -13,7 +13,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.game.Game; -import mage.game.permanent.token.ConsumingBlobToken; +import mage.game.permanent.token.ConsumingBlobOozeToken; import java.util.UUID; @@ -34,7 +34,7 @@ public final class ConsumingBlob extends CardImpl { // At the beginning of your end step, create a green Ooze creature token with "This creature's power is equal to the number of card types among cards in your graveyard and its toughness is equal to that number plus 1". this.addAbility(new BeginningOfEndStepTriggeredAbility( - new CreateTokenEffect(new ConsumingBlobToken()), TargetController.YOU, false) + new CreateTokenEffect(new ConsumingBlobOozeToken()), TargetController.YOU, false) ); } diff --git a/Mage.Sets/src/mage/cards/c/ConsumingTide.java b/Mage.Sets/src/mage/cards/c/ConsumingTide.java new file mode 100644 index 00000000000..3e71ee8ec1c --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ConsumingTide.java @@ -0,0 +1,98 @@ +package mage.cards.c; + +import java.util.HashSet; +import java.util.UUID; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetNonlandPermanent; + +/** + * + * @author weirddan455 + */ +public final class ConsumingTide extends CardImpl { + + public ConsumingTide(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{U}{U}"); + + // Each player chooses a nonland permanent they control. Return all nonland permanents not chosen this way to their owners' hands. + // Then you draw a card for each opponent who has more cards in their hand than you. + this.getSpellAbility().addEffect(new ConsumingTideEffect()); + } + + private ConsumingTide(final ConsumingTide card) { + super(card); + } + + @Override + public ConsumingTide copy() { + return new ConsumingTide(this); + } +} + +class ConsumingTideEffect extends OneShotEffect { + + public ConsumingTideEffect() { + super(Outcome.ReturnToHand); + staticText = "Each player chooses a nonland permanent they control. Return all nonland permanents not chosen this way to their owners' hands. " + + "Then you draw a card for each opponent who has more cards in their hand than you"; + } + + private ConsumingTideEffect(final ConsumingTideEffect effect) { + super(effect); + } + + @Override + public ConsumingTideEffect copy() { + return new ConsumingTideEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + HashSet chosenPermanents = new HashSet<>(); + for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { + Player player = game.getPlayer(playerId); + if (player != null) { + TargetNonlandPermanent target = new TargetNonlandPermanent(); + target.setNotTarget(true); + player.choose(Outcome.Benefit, target, source.getSourceId(), game); + UUID permId = target.getFirstTarget(); + if (permId != null) { + chosenPermanents.add(permId); + } + } + } + HashSet permanentsToHand = new HashSet<>(); + for (Permanent permanent : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_NON_LAND, controller.getId(), source.getSourceId(), game)) { + if (!chosenPermanents.contains(permanent.getId())) { + permanentsToHand.add(permanent); + } + } + controller.moveCards(permanentsToHand, Zone.HAND, source, game); + int controllerHandSize = controller.getHand().size(); + int cardsToDraw = 0; + for (UUID opponentId : game.getOpponents(controller.getId())) { + Player opponent = game.getPlayer(opponentId); + if (opponent != null && opponent.getHand().size() > controllerHandSize) { + cardsToDraw++; + } + } + controller.drawCards(cardsToDraw, source, game); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/c/Contagion.java b/Mage.Sets/src/mage/cards/c/Contagion.java index 4a951ba2189..c1e223c9079 100644 --- a/Mage.Sets/src/mage/cards/c/Contagion.java +++ b/Mage.Sets/src/mage/cards/c/Contagion.java @@ -1,7 +1,5 @@ - package mage.cards.c; -import java.util.UUID; import mage.ObjectColor; import mage.abilities.costs.AlternativeCostSourceAbility; import mage.abilities.costs.common.ExileFromHandCost; @@ -12,24 +10,26 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.counters.CounterType; import mage.filter.common.FilterOwnedCard; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardIdPredicate; import mage.filter.predicate.mageobject.ColorPredicate; import mage.target.common.TargetCardInHand; import mage.target.common.TargetCreaturePermanentAmount; +import java.util.UUID; + /** - * * @author Plopman */ public final class Contagion extends CardImpl { - public Contagion(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{3}{B}{B}"); + private static final FilterOwnedCard filter + = new FilterOwnedCard("a black card from your hand"); - FilterOwnedCard filter = new FilterOwnedCard("black card from your hand"); + static { filter.add(new ColorPredicate(ObjectColor.BLACK)); - filter.add(Predicates.not(new CardIdPredicate(this.getId()))); // the exile cost can never be paid with the card itself + } + + public Contagion(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{B}{B}"); // You may pay 1 life and exile a black card from your hand rather than pay Contagion's mana cost. AlternativeCostSourceAbility ability = new AlternativeCostSourceAbility(new PayLifeCost(1)); @@ -38,7 +38,10 @@ public final class Contagion extends CardImpl { // Distribute two -2/-1 counters among one or two target creatures. this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount(2)); - this.getSpellAbility().addEffect(new DistributeCountersEffect(CounterType.M2M1, 2, false, "one or two target creatures")); + this.getSpellAbility().addEffect(new DistributeCountersEffect( + CounterType.M2M1, 2, false, + "one or two target creatures" + )); } private Contagion(final Contagion card) { diff --git a/Mage.Sets/src/mage/cards/c/ContainmentConstruct.java b/Mage.Sets/src/mage/cards/c/ContainmentConstruct.java new file mode 100644 index 00000000000..47de50387c3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ContainmentConstruct.java @@ -0,0 +1,121 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; +import mage.cards.Card; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.players.Player; +import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; + +/** + * @author jeffwadsworth + */ +public final class ContainmentConstruct extends CardImpl { + + public ContainmentConstruct(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{2}"); + + this.subtype.add(SubType.CONSTRUCT); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Whenever you discard a card, you may exile that card from your graveyard. If you do, you may play that card this turn. + this.addAbility(new ContainmentConstructTriggeredAbility()); + + } + + private ContainmentConstruct(final ContainmentConstruct card) { + super(card); + } + + @Override + public ContainmentConstruct copy() { + return new ContainmentConstruct(this); + } +} + +class ContainmentConstructTriggeredAbility extends TriggeredAbilityImpl { + + public ContainmentConstructTriggeredAbility() { + super(Zone.BATTLEFIELD, new ContainmentConstructEffect(), false); + } + + public ContainmentConstructTriggeredAbility(final ContainmentConstructTriggeredAbility ability) { + super(ability); + } + + @Override + public ContainmentConstructTriggeredAbility copy() { + return new ContainmentConstructTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DISCARDED_CARD; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (event.getPlayerId() == this.getControllerId() + && game.getState().getZone(event.getTargetId()) == Zone.GRAVEYARD) { + this.getEffects().get(0).setTargetPointer(new FixedTarget(event.getTargetId())); + return true; + } + return false; + } + + @Override + public String getTriggerPhrase() { + return "Whenever you discard a card, "; + } +} + +class ContainmentConstructEffect extends OneShotEffect { + + public ContainmentConstructEffect() { + super(Outcome.Benefit); + this.staticText = "you may exile that card from your graveyard. If you do, you may play that card this turn"; + } + + public ContainmentConstructEffect(final ContainmentConstructEffect effect) { + super(effect); + } + + @Override + public ContainmentConstructEffect copy() { + return new ContainmentConstructEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Card discardedCard = game.getCard(targetPointer.getFirst(game, source)); + Card containmentConstruct = game.getCard(source.getSourceId()); + if (discardedCard != null + && containmentConstruct != null) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller.chooseUse(Outcome.Benefit, "Do you want to exile the discarded card? You may play it this turn from exile.", source, game)) { + UUID exileId = CardUtil.getExileZoneId(game, source); + controller.moveCardsToExile(discardedCard, source, game, true, exileId, "Exiled by " + containmentConstruct.getIdName()); + PlayFromNotOwnHandZoneTargetEffect effect = new PlayFromNotOwnHandZoneTargetEffect(Duration.EndOfTurn); + effect.setTargetPointer(new FixedTarget(discardedCard.getId(), game)); + game.addEffect(effect, source); + return true; + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/c/Contempt.java b/Mage.Sets/src/mage/cards/c/Contempt.java index 5a804ec28a8..b6894e7330f 100644 --- a/Mage.Sets/src/mage/cards/c/Contempt.java +++ b/Mage.Sets/src/mage/cards/c/Contempt.java @@ -78,7 +78,7 @@ class ContemptEffect extends OneShotEffect { if (attachedToPermanent != null) { Effect effect = new ReturnToHandTargetEffect(); effect.setTargetPointer(new FixedTarget( - attachedToPermanent.getId())).setText("return " + attachedToPermanent.getId(), game)).setText("return " + attachedToPermanent.getName() + " to owner's hand."); AtTheEndOfCombatDelayedTriggeredAbility ability = new AtTheEndOfCombatDelayedTriggeredAbility(effect); game.addDelayedTriggeredAbility(ability, source); diff --git a/Mage.Sets/src/mage/cards/c/ContestedCliffs.java b/Mage.Sets/src/mage/cards/c/ContestedCliffs.java index a27994372be..a9b29635f42 100644 --- a/Mage.Sets/src/mage/cards/c/ContestedCliffs.java +++ b/Mage.Sets/src/mage/cards/c/ContestedCliffs.java @@ -15,6 +15,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.TargetController; import mage.constants.Zone; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.target.Target; import mage.target.common.TargetCreaturePermanent; @@ -26,11 +27,9 @@ import mage.target.common.TargetCreaturePermanent; public final class ContestedCliffs extends CardImpl { private static final FilterCreaturePermanent filter1 = new FilterCreaturePermanent("Beast creature you control"); - private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent("creature an opponent controls"); static { filter1.add(TargetController.YOU.getControllerPredicate()); filter1.add(SubType.BEAST.getPredicate()); - filter2.add(TargetController.OPPONENT.getControllerPredicate()); } public ContestedCliffs(UUID ownerId, CardSetInfo setInfo) { @@ -38,17 +37,16 @@ public final class ContestedCliffs extends CardImpl { // {tap}: Add {C}. this.addAbility(new ColorlessManaAbility()); + // {R}{G}, {tap}: Choose target Beast creature you control and target creature an opponent controls. Those creatures fight each other. Effect effect = new FightTargetsEffect(); - effect.setText("Choose target Beast creature you control and target creature an opponent controls. Those creatures fight each other"); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl("{R}{G}")); ability.addCost(new TapSourceCost()); Target target1 = new TargetCreaturePermanent(filter1); ability.addTarget(target1); - Target target2 = new TargetCreaturePermanent(filter2); + Target target2 = new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE); ability.addTarget(target2); this.addAbility(ability); - } private ContestedCliffs(final ContestedCliffs card) { diff --git a/Mage.Sets/src/mage/cards/c/ContestedWarZone.java b/Mage.Sets/src/mage/cards/c/ContestedWarZone.java index 4e1ba74d209..81f3372220e 100644 --- a/Mage.Sets/src/mage/cards/c/ContestedWarZone.java +++ b/Mage.Sets/src/mage/cards/c/ContestedWarZone.java @@ -1,4 +1,3 @@ - package mage.cards.c; import java.util.UUID; @@ -7,14 +6,14 @@ import mage.abilities.Mode; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.common.continuous.BoostAllEffect; import mage.abilities.mana.ColorlessManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.common.FilterAttackingCreature; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.DamagedPlayerEvent; import mage.game.events.GameEvent; @@ -26,8 +25,6 @@ import mage.game.permanent.Permanent; */ public final class ContestedWarZone extends CardImpl { - private static final FilterAttackingCreature filter = new FilterAttackingCreature("Attacking creatures"); - public ContestedWarZone(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.LAND}, null); @@ -38,7 +35,7 @@ public final class ContestedWarZone extends CardImpl { this.addAbility(new ColorlessManaAbility()); // {1}, {T}: Attacking creatures get +1/+0 until end of turn. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostAllEffect(1, 0, Duration.EndOfTurn, filter, false), new ManaCostsImpl("{1}")); + Ability ability = new SimpleActivatedAbility(new BoostAllEffect(1, 0, Duration.EndOfTurn, StaticFilters.FILTER_ATTACKING_CREATURES, false), new GenericManaCost(1)); ability.addCost(new TapSourceCost()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/c/ContrabandKingpin.java b/Mage.Sets/src/mage/cards/c/ContrabandKingpin.java index 2fe369114ef..696bd8569bb 100644 --- a/Mage.Sets/src/mage/cards/c/ContrabandKingpin.java +++ b/Mage.Sets/src/mage/cards/c/ContrabandKingpin.java @@ -1,26 +1,25 @@ package mage.cards.c; -import java.util.UUID; import mage.MageInt; -import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; +import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; import mage.abilities.effects.keyword.ScryEffect; import mage.abilities.keyword.LifelinkAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.common.FilterControlledArtifactPermanent; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * * @author fireshoes */ public final class ContrabandKingpin extends CardImpl { public ContrabandKingpin(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{U}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{U}{B}"); this.subtype.add(SubType.AETHERBORN); this.subtype.add(SubType.ROGUE); this.power = new MageInt(1); @@ -30,7 +29,10 @@ public final class ContrabandKingpin extends CardImpl { this.addAbility(LifelinkAbility.getInstance()); // Whenever an artifact enters the battlefield under your control, scry 1. - this.addAbility(new EntersBattlefieldAllTriggeredAbility(Zone.BATTLEFIELD, new ScryEffect(1), new FilterControlledArtifactPermanent(), false, null, true)); + this.addAbility(new EntersBattlefieldControlledTriggeredAbility( + new ScryEffect(1, false), + StaticFilters.FILTER_PERMANENT_ARTIFACT_AN + )); } private ContrabandKingpin(final ContrabandKingpin card) { diff --git a/Mage.Sets/src/mage/cards/c/ConvictedKiller.java b/Mage.Sets/src/mage/cards/c/ConvictedKiller.java index b81f22ce7e1..ed0602e0025 100644 --- a/Mage.Sets/src/mage/cards/c/ConvictedKiller.java +++ b/Mage.Sets/src/mage/cards/c/ConvictedKiller.java @@ -3,18 +3,12 @@ package mage.cards.c; import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.WerewolfFrontTriggeredAbility; -import mage.abilities.condition.common.NoSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.TransformSourceEffect; import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; /** * @author fireshoes @@ -28,7 +22,6 @@ public final class ConvictedKiller extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); - this.transformable = true; this.secondSideCardClazz = mage.cards.b.BrandedHowler.class; // At the beginning of each upkeep, if no spells were cast last turn, transform Convicted Killer. diff --git a/Mage.Sets/src/mage/cards/c/CopyEnchantment.java b/Mage.Sets/src/mage/cards/c/CopyEnchantment.java index 1919a3d908f..ea86c88f4bf 100644 --- a/Mage.Sets/src/mage/cards/c/CopyEnchantment.java +++ b/Mage.Sets/src/mage/cards/c/CopyEnchantment.java @@ -20,7 +20,7 @@ public final class CopyEnchantment extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{U}"); // You may have Copy Enchantment enter the battlefield as a copy of any enchantment on the battlefield. - this.addAbility(new EntersBattlefieldAbility(new CopyPermanentEffect(StaticFilters.FILTER_ENCHANTMENT_PERMANENT), true)); + this.addAbility(new EntersBattlefieldAbility(new CopyPermanentEffect(StaticFilters.FILTER_PERMANENT_ENCHANTMENT), true)); } private CopyEnchantment(final CopyEnchantment card) { diff --git a/Mage.Sets/src/mage/cards/c/Corpsehatch.java b/Mage.Sets/src/mage/cards/c/Corpsehatch.java index 147e6738b83..a9837165b39 100644 --- a/Mage.Sets/src/mage/cards/c/Corpsehatch.java +++ b/Mage.Sets/src/mage/cards/c/Corpsehatch.java @@ -1,16 +1,12 @@ - package mage.cards.c; import java.util.UUID; -import mage.ObjectColor; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.DestroyTargetEffect; 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.game.permanent.token.EldraziSpawnToken; import mage.target.common.TargetCreaturePermanent; @@ -20,17 +16,10 @@ import mage.target.common.TargetCreaturePermanent; */ public final class Corpsehatch extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public Corpsehatch(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{B}{B}"); - - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); this.getSpellAbility().addEffect(new DestroyTargetEffect()); this.getSpellAbility().addEffect(new CreateTokenEffect(new EldraziSpawnToken(), 2)); } diff --git a/Mage.Sets/src/mage/cards/c/Corpseweft.java b/Mage.Sets/src/mage/cards/c/Corpseweft.java index 75c0672c613..d5037d2e3cf 100644 --- a/Mage.Sets/src/mage/cards/c/Corpseweft.java +++ b/Mage.Sets/src/mage/cards/c/Corpseweft.java @@ -1,4 +1,3 @@ - package mage.cards.c; import java.util.UUID; @@ -14,7 +13,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; -import mage.filter.common.FilterCreatureCard; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.token.CorpseweftZombieToken; import mage.players.Player; @@ -31,7 +30,7 @@ public final class Corpseweft extends CardImpl { // {1}{B}, Exile one or more creature cards from your graveyard: Create a tapped X/X black Zombie Horror creature token, where X is twice the number of cards exiled this way. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CorpseweftEffect(), new ManaCostsImpl("{1}{B}")); - ability.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard(1, Integer.MAX_VALUE, new FilterCreatureCard("creature cards from your graveyard")))); + ability.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard(1, Integer.MAX_VALUE, StaticFilters.FILTER_CARD_CREATURES_YOUR_GRAVEYARD))); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/c/CorruptOfficial.java b/Mage.Sets/src/mage/cards/c/CorruptOfficial.java index 03d139f6c62..fb5618523a1 100644 --- a/Mage.Sets/src/mage/cards/c/CorruptOfficial.java +++ b/Mage.Sets/src/mage/cards/c/CorruptOfficial.java @@ -2,22 +2,16 @@ package mage.cards.c; import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.RegenerateSourceEffect; +import mage.abilities.effects.common.discard.DiscardTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Outcome; import mage.constants.Zone; -import mage.game.Game; -import mage.game.combat.Combat; -import mage.game.permanent.Permanent; -import mage.players.Player; /** * @@ -36,7 +30,9 @@ public final class CorruptOfficial extends CardImpl { this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateSourceEffect(), new ManaCostsImpl("{2}{B}"))); // Whenever Corrupt Official becomes blocked, defending player discards a card at random. - this.addAbility(new BecomesBlockedSourceTriggeredAbility(new CorruptOfficialDiscardEffect(), false)); + this.addAbility(new BecomesBlockedSourceTriggeredAbility( + new DiscardTargetEffect(1, true).setText("defending player discards a card at random"), + false, true)); } private CorruptOfficial(final CorruptOfficial card) { @@ -48,34 +44,3 @@ public final class CorruptOfficial extends CardImpl { return new CorruptOfficial(this); } } - -class CorruptOfficialDiscardEffect extends OneShotEffect { - - public CorruptOfficialDiscardEffect() { - super(Outcome.Discard); - this.staticText = "defending player discards a card at random"; - } - - public CorruptOfficialDiscardEffect(final CorruptOfficialDiscardEffect effect) { - super(effect); - } - - @Override - public CorruptOfficialDiscardEffect copy() { - return new CorruptOfficialDiscardEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent corruptOfficial = game.getPermanent(source.getSourceId()); - if (corruptOfficial != null) { - Combat combat = game.getCombat(); - Player defendingPlayer = game.getPlayer(combat.getDefendingPlayerId(corruptOfficial.getId(), game)); - if (defendingPlayer != null) { - defendingPlayer.discard(1, true, false, source, game); - return true; - } - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/c/CosmotronicWave.java b/Mage.Sets/src/mage/cards/c/CosmotronicWave.java index 9d238f37b36..86fc51e8685 100644 --- a/Mage.Sets/src/mage/cards/c/CosmotronicWave.java +++ b/Mage.Sets/src/mage/cards/c/CosmotronicWave.java @@ -1,33 +1,29 @@ package mage.cards.c; -import java.util.UUID; import mage.abilities.effects.common.DamageAllEffect; import mage.abilities.effects.common.combat.CantBlockAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.FilterPermanent; -import mage.filter.StaticFilters; +import mage.filter.common.FilterCreaturePermanent; import mage.filter.common.FilterOpponentsCreaturePermanent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class CosmotronicWave extends CardImpl { - private static final FilterPermanent filter = new FilterOpponentsCreaturePermanent("creature your opponents control"); + private static final FilterCreaturePermanent filter = new FilterOpponentsCreaturePermanent("creature your opponents control"); public CosmotronicWave(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{R}"); // Cosmotronic Wave deals 1 damage to each creature your opponents control. Creatures your opponents control can't block this turn. this.getSpellAbility().addEffect(new DamageAllEffect(1, filter)); - this.getSpellAbility().addEffect(new CantBlockAllEffect( - StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE, - Duration.EndOfTurn - )); + this.getSpellAbility().addEffect(new CantBlockAllEffect(filter, Duration.EndOfTurn).setText("Creatures your opponents control can't block this turn")); } private CosmotronicWave(final CosmotronicWave card) { diff --git a/Mage.Sets/src/mage/cards/c/Counterspell.java b/Mage.Sets/src/mage/cards/c/Counterspell.java index ec03747be96..b0e269c4c5c 100644 --- a/Mage.Sets/src/mage/cards/c/Counterspell.java +++ b/Mage.Sets/src/mage/cards/c/Counterspell.java @@ -1,4 +1,3 @@ - package mage.cards.c; import java.util.UUID; @@ -17,6 +16,7 @@ public final class Counterspell extends CardImpl { public Counterspell(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{U}{U}"); + // Counter target spell. this.getSpellAbility().addEffect(new CounterTargetEffect()); this.getSpellAbility().addTarget(new TargetSpell()); } diff --git a/Mage.Sets/src/mage/cards/c/Countersquall.java b/Mage.Sets/src/mage/cards/c/Countersquall.java index e0074df9607..5a2204a3a07 100644 --- a/Mage.Sets/src/mage/cards/c/Countersquall.java +++ b/Mage.Sets/src/mage/cards/c/Countersquall.java @@ -7,8 +7,7 @@ import mage.abilities.effects.common.LoseLifeTargetControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.FilterSpell; -import mage.filter.predicate.Predicates; +import mage.filter.StaticFilters; import mage.target.TargetSpell; /** @@ -17,18 +16,11 @@ import mage.target.TargetSpell; */ public final class Countersquall extends CardImpl { - private static final FilterSpell filter = new FilterSpell("noncreature spell"); - - static { - filter.add(Predicates.not(CardType.CREATURE.getPredicate())); - } - public Countersquall(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{U}{B}"); - // Counter target noncreature spell. Its controller loses 2 life. - this.getSpellAbility().addTarget(new TargetSpell(filter)); + this.getSpellAbility().addTarget(new TargetSpell(StaticFilters.FILTER_SPELL_NON_CREATURE)); this.getSpellAbility().addEffect(new CounterTargetEffect()); this.getSpellAbility().addEffect(new LoseLifeTargetControllerEffect(2)); } diff --git a/Mage.Sets/src/mage/cards/c/CountlessGearsRenegade.java b/Mage.Sets/src/mage/cards/c/CountlessGearsRenegade.java index 374e9212c7a..96974b33217 100644 --- a/Mage.Sets/src/mage/cards/c/CountlessGearsRenegade.java +++ b/Mage.Sets/src/mage/cards/c/CountlessGearsRenegade.java @@ -1,7 +1,5 @@ - package mage.cards.c; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -16,6 +14,8 @@ import mage.constants.SubType; import mage.game.permanent.token.ServoToken; import mage.watchers.common.RevoltWatcher; +import java.util.UUID; + /** * @author JRHerlehy */ @@ -31,13 +31,13 @@ public final class CountlessGearsRenegade extends CardImpl { // Revolt — When Countless Gears Renegade enters the battlefield, if a permanent you controlled // left the battlefield this turn, create a 1/1 colorless Servo artifact creature token. - Ability ability = new ConditionalInterveningIfTriggeredAbility(new EntersBattlefieldTriggeredAbility( - new CreateTokenEffect(new ServoToken(), 1), false), RevoltCondition.instance, - "Revolt — When {this} enters the battlefield, if a permanent you controlled left" - + " the battlefield this turn, create a 1/1 colorless Servo artifact creature token."); + Ability ability = new ConditionalInterveningIfTriggeredAbility( + new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new ServoToken()), false), + RevoltCondition.instance, "When {this} enters the battlefield, if a permanent you controlled " + + "left the battlefield this turn, create a 1/1 colorless Servo artifact creature token." + ); ability.setAbilityWord(AbilityWord.REVOLT); - ability.addWatcher(new RevoltWatcher()); - this.addAbility(ability); + this.addAbility(ability, new RevoltWatcher()); } private CountlessGearsRenegade(final CountlessGearsRenegade card) { diff --git a/Mage.Sets/src/mage/cards/c/CourierBat.java b/Mage.Sets/src/mage/cards/c/CourierBat.java new file mode 100644 index 00000000000..d74d2ddaddb --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CourierBat.java @@ -0,0 +1,54 @@ +package mage.cards.c; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.common.YouGainedLifeCondition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.constants.ComparisonType; +import mage.constants.SubType; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.target.common.TargetCardInYourGraveyard; +import mage.watchers.common.PlayerGainedLifeWatcher; + +/** + * + * @author weirddan455 + */ +public final class CourierBat extends CardImpl { + + public CourierBat(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.subtype.add(SubType.BAT); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When Courier Bat enters the battlefield, if you gained life this turn, return up to one target creature card from your graveyard to your hand. + Ability ability = new ConditionalInterveningIfTriggeredAbility( + new EntersBattlefieldTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect()), + new YouGainedLifeCondition(ComparisonType.MORE_THAN, 0), + "When {this} enters the battlefield, if you gained life this turn, return up to one target creature card from your graveyard to your hand." + ); + ability.addTarget(new TargetCardInYourGraveyard(0, 1, StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); + this.addAbility(ability, new PlayerGainedLifeWatcher()); + } + + private CourierBat(final CourierBat card) { + super(card); + } + + @Override + public CourierBat copy() { + return new CourierBat(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CourtStreetDenizen.java b/Mage.Sets/src/mage/cards/c/CourtStreetDenizen.java index 7c728e6e2fe..cd528de8ece 100644 --- a/Mage.Sets/src/mage/cards/c/CourtStreetDenizen.java +++ b/Mage.Sets/src/mage/cards/c/CourtStreetDenizen.java @@ -1,4 +1,3 @@ - package mage.cards.c; import java.util.UUID; @@ -11,10 +10,9 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; import mage.constants.Zone; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; @@ -25,13 +23,10 @@ import mage.target.common.TargetCreaturePermanent; */ public final class CourtStreetDenizen extends CardImpl { - private static final FilterPermanent filter = new FilterCreaturePermanent("another white creature"); - private static final FilterCreaturePermanent filterOpponentCreature = new FilterCreaturePermanent("creature an opponent controls"); + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("another white creature"); static { filter.add(AnotherPredicate.instance); filter.add(new ColorPredicate(ObjectColor.WHITE)); - filter.add(TargetController.YOU.getControllerPredicate()); - filterOpponentCreature.add(TargetController.OPPONENT.getControllerPredicate()); } public CourtStreetDenizen(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}"); @@ -42,8 +37,8 @@ public final class CourtStreetDenizen extends CardImpl { this.toughness = new MageInt(2); // Whenever another white creature enters the battlefield under your control, tap target creature an opponent controls. - Ability ability = new EntersBattlefieldAllTriggeredAbility(Zone.BATTLEFIELD, new TapTargetEffect(),filter,false,null, true); - ability.addTarget(new TargetCreaturePermanent(filterOpponentCreature)); + Ability ability = new EntersBattlefieldAllTriggeredAbility(Zone.BATTLEFIELD, new TapTargetEffect(), filter, false, null, true); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/c/CovertCutpurse.java b/Mage.Sets/src/mage/cards/c/CovertCutpurse.java index 4659de478d1..25f3911e341 100644 --- a/Mage.Sets/src/mage/cards/c/CovertCutpurse.java +++ b/Mage.Sets/src/mage/cards/c/CovertCutpurse.java @@ -6,7 +6,6 @@ import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.keyword.DisturbAbility; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -39,7 +38,6 @@ public final class CovertCutpurse extends CardImpl { this.subtype.add(SubType.ROGUE); this.power = new MageInt(2); this.toughness = new MageInt(1); - this.transformable = true; this.secondSideCardClazz = mage.cards.c.CovetousGeist.class; // When Covert Cutpurse enters the battlefield, destroy target creature you don't control that was dealt damage this turn. @@ -48,8 +46,7 @@ public final class CovertCutpurse extends CardImpl { this.addAbility(ability); // Disturb {4}{B} - this.addAbility(new TransformAbility()); - this.addAbility(new DisturbAbility(new ManaCostsImpl<>("{4}{B}"))); + this.addAbility(new DisturbAbility(this, "{4}{B}")); } private CovertCutpurse(final CovertCutpurse card) { diff --git a/Mage.Sets/src/mage/cards/c/CovertTechnician.java b/Mage.Sets/src/mage/cards/c/CovertTechnician.java new file mode 100644 index 00000000000..95e3adf0d93 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CovertTechnician.java @@ -0,0 +1,78 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.PutCardFromHandOntoBattlefieldEffect; +import mage.abilities.keyword.NinjutsuAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.common.FilterArtifactCard; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.game.Game; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CovertTechnician extends CardImpl { + + public CovertTechnician(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{2}{U}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.NINJA); + this.power = new MageInt(2); + this.toughness = new MageInt(4); + + // Ninjutsu {1}{U} + this.addAbility(new NinjutsuAbility("{1}{U}")); + + // Whenever Covert Technician deals combat damage to a player, you may put an artifact card with mana value less than or equal to that damage from your hand onto the battlefield. + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility( + new CovertTechnicianEffect(), false, true, false + )); + } + + private CovertTechnician(final CovertTechnician card) { + super(card); + } + + @Override + public CovertTechnician copy() { + return new CovertTechnician(this); + } +} + +class CovertTechnicianEffect extends OneShotEffect { + + CovertTechnicianEffect() { + super(Outcome.Benefit); + staticText = "you may put an artifact card with mana value less than " + + "or equal to that damage from your hand onto the battlefield"; + } + + private CovertTechnicianEffect(final CovertTechnicianEffect effect) { + super(effect); + } + + @Override + public CovertTechnicianEffect copy() { + return new CovertTechnicianEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + int damage = (Integer) getValue("damage"); + FilterCard filter = new FilterArtifactCard("artifact card with mana value " + damage + " or less"); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, damage + 1)); + return new PutCardFromHandOntoBattlefieldEffect(filter).apply(game, source); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CovetousCastaway.java b/Mage.Sets/src/mage/cards/c/CovetousCastaway.java index 9f1a2180606..1fd63048184 100644 --- a/Mage.Sets/src/mage/cards/c/CovetousCastaway.java +++ b/Mage.Sets/src/mage/cards/c/CovetousCastaway.java @@ -5,7 +5,6 @@ import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.MillCardsControllerEffect; import mage.abilities.keyword.DisturbAbility; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -24,15 +23,13 @@ public final class CovetousCastaway extends CardImpl { this.subtype.add(SubType.HUMAN); this.power = new MageInt(1); this.toughness = new MageInt(3); - this.transformable = true; this.secondSideCardClazz = mage.cards.g.GhostlyCastigator.class; // When Covetous Castaway dies, mill three cards. this.addAbility(new DiesSourceTriggeredAbility(new MillCardsControllerEffect(3))); // Disturb {3}{U}{U} - this.addAbility(new TransformAbility()); - this.addAbility(new DisturbAbility(new ManaCostsImpl<>("{3}{U}{U}"))); + this.addAbility(new DisturbAbility(this, "{3}{U}{U}")); } private CovetousCastaway(final CovetousCastaway card) { diff --git a/Mage.Sets/src/mage/cards/c/CovetousGeist.java b/Mage.Sets/src/mage/cards/c/CovetousGeist.java index 7f1be068826..fbc4f1faec1 100644 --- a/Mage.Sets/src/mage/cards/c/CovetousGeist.java +++ b/Mage.Sets/src/mage/cards/c/CovetousGeist.java @@ -25,7 +25,6 @@ public final class CovetousGeist extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); this.color.setBlack(true); - this.transformable = true; this.nightCard = true; // Flying diff --git a/Mage.Sets/src/mage/cards/c/Cowardice.java b/Mage.Sets/src/mage/cards/c/Cowardice.java index f31103ddf82..aa7a552438f 100644 --- a/Mage.Sets/src/mage/cards/c/Cowardice.java +++ b/Mage.Sets/src/mage/cards/c/Cowardice.java @@ -55,7 +55,7 @@ class CowardiceTriggeredAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { Permanent permanent = game.getPermanent(event.getTargetId()); if (permanent != null && permanent.isCreature(game)) { - getEffects().get(0).setTargetPointer(new FixedTarget(event.getTargetId())); + getEffects().get(0).setTargetPointer(new FixedTarget(event.getTargetId(), game)); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/c/CracklingEmergence.java b/Mage.Sets/src/mage/cards/c/CracklingEmergence.java new file mode 100644 index 00000000000..b4756ff1419 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CracklingEmergence.java @@ -0,0 +1,113 @@ +package mage.cards.c; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.continuous.BecomesCreatureAttachedEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.IndestructibleAbility; +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.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.custom.CreatureToken; +import mage.target.TargetPermanent; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CracklingEmergence extends CardImpl { + + public CracklingEmergence(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{R}"); + + this.subtype.add(SubType.AURA); + + // Enchant land you control + TargetPermanent auraTarget = new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + this.addAbility(new EnchantAbility(auraTarget.getTargetName())); + + // Enchanted land is a 3/3 red Spirit creature with haste. It's still a land. + this.addAbility(new SimpleStaticAbility(new BecomesCreatureAttachedEffect( + new CreatureToken(3, 3) + .withColor("R") + .withSubType(SubType.SPIRIT) + .withAbility(HasteAbility.getInstance()), + "enchanted land is a 3/3 red Spirit creature with haste. It's still a land", + Duration.WhileOnBattlefield, BecomesCreatureAttachedEffect.LoseType.COLOR + ))); + + // If enchanted land would be destroyed, instead sacrifice Crackling Emergence and that land gains indestructible until end of turn. + this.addAbility(new SimpleStaticAbility(new CracklingEmergenceEffect())); + } + + private CracklingEmergence(final CracklingEmergence card) { + super(card); + } + + @Override + public CracklingEmergence copy() { + return new CracklingEmergence(this); + } +} + +class CracklingEmergenceEffect extends ReplacementEffectImpl { + + CracklingEmergenceEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "if enchanted land would be destroyed, instead sacrifice " + + "{this} and that land gains indestructible until end of turn"; + } + + private CracklingEmergenceEffect(final CracklingEmergenceEffect effect) { + super(effect); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game); + Permanent enchantedPermanent = game.getPermanent(event.getTargetId()); + if (sourcePermanent == null || enchantedPermanent == null) { + return false; + } + sourcePermanent.sacrifice(source, game); + game.addEffect(new GainAbilityTargetEffect(IndestructibleAbility.getInstance()) + .setTargetPointer(new FixedTarget(enchantedPermanent, game)), source); + return true; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DESTROY_PERMANENT; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game); + return sourcePermanent != null && event.getTargetId().equals(sourcePermanent.getAttachedTo()); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public CracklingEmergenceEffect copy() { + return new CracklingEmergenceEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CradleOfSafety.java b/Mage.Sets/src/mage/cards/c/CradleOfSafety.java new file mode 100644 index 00000000000..13f30cbff7a --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CradleOfSafety.java @@ -0,0 +1,58 @@ +package mage.cards.c; + +import java.util.UUID; + +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.HexproofAbility; +import mage.constants.*; +import mage.abilities.keyword.FlashAbility; +import mage.abilities.Ability; +import mage.abilities.effects.common.AttachEffect; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; + +/** + * + * @author weirddan455 + */ +public final class CradleOfSafety extends CardImpl { + + public CradleOfSafety(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}"); + + this.subtype.add(SubType.AURA); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // Enchant creature you control + TargetPermanent auraTarget = new TargetPermanent(StaticFilters.FILTER_CONTROLLED_CREATURE); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // When Cradle of Safety enters the battlefield, enchanted creature gains hexproof until end of turn. + this.addAbility(new EntersBattlefieldTriggeredAbility(new GainAbilityAttachedEffect( + HexproofAbility.getInstance(), AttachmentType.AURA, Duration.EndOfTurn + ))); + + // Enchanted creature gets +1/+1. + this.addAbility(new SimpleStaticAbility(new BoostEnchantedEffect(1, 1))); + } + + private CradleOfSafety(final CradleOfSafety card) { + super(card); + } + + @Override + public CradleOfSafety copy() { + return new CradleOfSafety(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CragSaurian.java b/Mage.Sets/src/mage/cards/c/CragSaurian.java index a996468a6f3..25f98583b8f 100644 --- a/Mage.Sets/src/mage/cards/c/CragSaurian.java +++ b/Mage.Sets/src/mage/cards/c/CragSaurian.java @@ -64,7 +64,7 @@ public final class CragSaurian extends CardImpl { Player newController = game.getPlayer(this.getTargetPointer().getFirst(game, source)); if (newController != null && controller != null && !controller.equals(newController)) { ContinuousEffect effect = new GainControlTargetEffect(Duration.Custom, newController.getId()); - effect.setTargetPointer(new FixedTarget(source.getSourceId())); + effect.setTargetPointer(new FixedTarget(source.getSourceId(), game)); game.addEffect(effect, source); return true; } diff --git a/Mage.Sets/src/mage/cards/c/CrashThrough.java b/Mage.Sets/src/mage/cards/c/CrashThrough.java index 3492f65f390..cf382233315 100644 --- a/Mage.Sets/src/mage/cards/c/CrashThrough.java +++ b/Mage.Sets/src/mage/cards/c/CrashThrough.java @@ -25,7 +25,7 @@ public final class CrashThrough extends CardImpl { getSpellAbility().addEffect(new GainAbilityAllEffect(TrampleAbility.getInstance(), Duration.EndOfTurn, new FilterControlledCreaturePermanent(), "Creatures you control gain trample until end of turn")); // Draw a card. - getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); + getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("
")); } private CrashThrough(final CrashThrough card) { diff --git a/Mage.Sets/src/mage/cards/c/CrashingBoars.java b/Mage.Sets/src/mage/cards/c/CrashingBoars.java index 1c3597ed16c..b9e66a2c13f 100644 --- a/Mage.Sets/src/mage/cards/c/CrashingBoars.java +++ b/Mage.Sets/src/mage/cards/c/CrashingBoars.java @@ -76,7 +76,7 @@ class CrashingBoarsEffect extends OneShotEffect { Target target = new TargetControlledCreaturePermanent(1, 1, filter, true); if (target.choose(Outcome.Neutral, defendingPlayer.getId(), source.getSourceId(), game)) { RequirementEffect effect = new MustBeBlockedByTargetSourceEffect(); - effect.setTargetPointer(new FixedTarget(target.getFirstTarget())); + effect.setTargetPointer(new FixedTarget(target.getFirstTarget(), game)); game.addEffect(effect, source); } return true; diff --git a/Mage.Sets/src/mage/cards/c/CrashingTide.java b/Mage.Sets/src/mage/cards/c/CrashingTide.java index ed1a2cc645a..d604b1b447a 100644 --- a/Mage.Sets/src/mage/cards/c/CrashingTide.java +++ b/Mage.Sets/src/mage/cards/c/CrashingTide.java @@ -33,7 +33,7 @@ public final class CrashingTide extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.ALL, new ConditionalContinuousEffect(new GainAbilitySourceEffect(FlashAbility.getInstance(), Duration.WhileOnBattlefield, true), new PermanentsOnTheBattlefieldCondition(filter), - "{this} has flash as long as you control a Merfolk")).setRuleAtTheTop(true)); + "this spell has flash as long as you control a Merfolk")).setRuleAtTheTop(true)); // Return target creature to it's owner's hand. this.getSpellAbility().addEffect(new ReturnToHandTargetEffect()); diff --git a/Mage.Sets/src/mage/cards/c/CraterhoofBehemoth.java b/Mage.Sets/src/mage/cards/c/CraterhoofBehemoth.java index 3de0705a662..748692f3763 100644 --- a/Mage.Sets/src/mage/cards/c/CraterhoofBehemoth.java +++ b/Mage.Sets/src/mage/cards/c/CraterhoofBehemoth.java @@ -40,8 +40,13 @@ public final class CraterhoofBehemoth extends CardImpl { this.addAbility(HasteAbility.getInstance()); // When Craterhoof Behemoth enters the battlefield, creatures you control gain trample and get +X/+X until end of turn, where X is the number of creatures you control. - Ability ability = new EntersBattlefieldTriggeredAbility(new GainAbilityControlledEffect(TrampleAbility.getInstance(), Duration.EndOfTurn, filter)); - ability.addEffect(new BoostControlledEffect(CreaturesYouControlCount.instance, CreaturesYouControlCount.instance, Duration.EndOfTurn, filter, false, true)); + Ability ability = new EntersBattlefieldTriggeredAbility(new GainAbilityControlledEffect( + TrampleAbility.getInstance(), Duration.EndOfTurn, filter + ).setText("creatures you control gain trample")); + ability.addEffect(new BoostControlledEffect( + CreaturesYouControlCount.instance, CreaturesYouControlCount.instance, + Duration.EndOfTurn, filter, false, true + ).setText("and get +X/+X until end of turn, where X is the number of creatures you control")); ability.addHint(CreaturesYouControlHint.instance); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/c/CrawlingInfestation.java b/Mage.Sets/src/mage/cards/c/CrawlingInfestation.java new file mode 100644 index 00000000000..fc5511c8457 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CrawlingInfestation.java @@ -0,0 +1,81 @@ +package mage.cards.c; + +import java.util.UUID; + +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.MillCardsControllerEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.token.InsectToken; + +/** + * + * @author weirddan455 + */ +public final class CrawlingInfestation extends CardImpl { + + public CrawlingInfestation(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}"); + + // At the beginning of your upkeep, you may mill two cards. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new MillCardsControllerEffect(2), TargetController.YOU, true)); + + // Whenever one or more creature cards are put into your graveyard from anywhere during your turn, create a 1/1 green Insect creature token. This ability triggers only once each turn. + this.addAbility(new CrawlingInfestationTriggeredAbility()); + } + + private CrawlingInfestation(final CrawlingInfestation card) { + super(card); + } + + @Override + public CrawlingInfestation copy() { + return new CrawlingInfestation(this); + } +} + +class CrawlingInfestationTriggeredAbility extends TriggeredAbilityImpl { + + public CrawlingInfestationTriggeredAbility() { + super(Zone.BATTLEFIELD, new CreateTokenEffect(new InsectToken())); + this.setTriggersOnce(true); + } + + private CrawlingInfestationTriggeredAbility(final CrawlingInfestationTriggeredAbility ability) { + super(ability); + } + + @Override + public CrawlingInfestationTriggeredAbility copy() { + return new CrawlingInfestationTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + if (zEvent.getToZone() == Zone.GRAVEYARD && game.isActivePlayer(controllerId)) { + Card card = game.getCard(zEvent.getTargetId()); + return card != null && !card.isCopy() && card.isCreature(game) && card.isOwnedBy(controllerId); + } + return false; + } + + @Override + public String getTriggerPhrase() { + return "Whenever one or more creature cards are put into your graveyard from anywhere during your turn, "; + } +} diff --git a/Mage.Sets/src/mage/cards/c/CreepingInn.java b/Mage.Sets/src/mage/cards/c/CreepingInn.java index 791bfedba22..5ba7b0aa98c 100644 --- a/Mage.Sets/src/mage/cards/c/CreepingInn.java +++ b/Mage.Sets/src/mage/cards/c/CreepingInn.java @@ -37,7 +37,6 @@ public final class CreepingInn extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(7); this.color.setBlack(true); - this.transformable = true; this.nightCard = true; // Whenever Creeping Inn attacks, you may exile a creature card from your graveyard. @@ -46,7 +45,7 @@ public final class CreepingInn extends CardImpl { this.addAbility(new AttacksTriggeredAbility(new CreepingInnEffect())); // {4}: Creeping Inn phases out. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new PhaseOutSourceEffect(), new ManaCostsImpl("{4}"))); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new PhaseOutSourceEffect(), new ManaCostsImpl<>("{4}"))); } private CreepingInn(final CreepingInn card) { diff --git a/Mage.Sets/src/mage/cards/c/CreepyPuppeteer.java b/Mage.Sets/src/mage/cards/c/CreepyPuppeteer.java new file mode 100644 index 00000000000..4e3abebfd7e --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CreepyPuppeteer.java @@ -0,0 +1,95 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.common.continuous.SetPowerToughnessTargetEffect; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CreepyPuppeteer extends CardImpl { + + public CreepyPuppeteer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ROGUE); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // Whenever Creepy Puppeteer attacks, if you attacked with exactly one other creature this combat, you may have that creature's base power and toughness become 4/3 until end of turn. + this.addAbility(new CreepyPuppeteerTriggeredAbility()); + } + + private CreepyPuppeteer(final CreepyPuppeteer card) { + super(card); + } + + @Override + public CreepyPuppeteer copy() { + return new CreepyPuppeteer(this); + } +} + +class CreepyPuppeteerTriggeredAbility extends TriggeredAbilityImpl { + + CreepyPuppeteerTriggeredAbility() { + super(Zone.BATTLEFIELD, new SetPowerToughnessTargetEffect(4, 3, Duration.EndOfTurn), true); + } + + private CreepyPuppeteerTriggeredAbility(final CreepyPuppeteerTriggeredAbility ability) { + super(ability); + } + + @Override + public CreepyPuppeteerTriggeredAbility copy() { + return new CreepyPuppeteerTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DECLARED_ATTACKERS; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (!isControlledBy(game.getCombat().getAttackingPlayerId()) + || !game.getCombat().getAttackers().contains(getSourceId()) + || game.getCombat().getAttackers().size() != 2) { + return false; + } + UUID otherAttacker = game + .getCombat() + .getAttackers() + .stream() + .filter(uuid -> !getSourceId().equals(uuid)) + .findFirst() + .orElse(null); + if (otherAttacker == null) { + return false; + } + this.getEffects().setTargetPointer(new FixedTarget(otherAttacker, game)); + return true; + } + + @Override + public String getRule() { + return "Whenever {this} attacks, if you attacked with exactly one other creature this combat, " + + "you may have that creature's base power and toughness become 4/3 until end of turn."; + } +} diff --git a/Mage.Sets/src/mage/cards/c/CrescendoOfWar.java b/Mage.Sets/src/mage/cards/c/CrescendoOfWar.java index 004e47df5cc..16e7f9b2960 100644 --- a/Mage.Sets/src/mage/cards/c/CrescendoOfWar.java +++ b/Mage.Sets/src/mage/cards/c/CrescendoOfWar.java @@ -1,4 +1,3 @@ - package mage.cards.c; import java.util.UUID; @@ -16,8 +15,7 @@ import mage.constants.Duration; import mage.constants.TargetController; import mage.constants.Zone; import mage.counters.CounterType; -import mage.filter.common.FilterAttackingCreature; -import mage.filter.common.FilterBlockingCreature; +import mage.filter.StaticFilters; /** * @@ -25,6 +23,8 @@ import mage.filter.common.FilterBlockingCreature; */ public final class CrescendoOfWar extends CardImpl { + private static final CountersSourceCount xValue = new CountersSourceCount(CounterType.STRIFE); + public CrescendoOfWar(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{W}"); @@ -32,12 +32,12 @@ public final class CrescendoOfWar extends CardImpl { this.addAbility(new BeginningOfUpkeepTriggeredAbility(new AddCountersSourceEffect(CounterType.STRIFE.createInstance(1), true), TargetController.ANY, false)); // Attacking creatures get +1/+0 for each strife counter on Crescendo of War. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostAllEffect(new CountersSourceCount(CounterType.STRIFE), StaticValue.get(0), - Duration.WhileOnBattlefield, new FilterAttackingCreature(), false))); + this.addAbility(new SimpleStaticAbility(new BoostAllEffect(xValue, StaticValue.get(0), + Duration.WhileOnBattlefield, StaticFilters.FILTER_ATTACKING_CREATURES, false))); // Blocking creatures you control get +1/+0 for each strife counter on Crescendo of War. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(new CountersSourceCount(CounterType.STRIFE), StaticValue.get(0), - Duration.WhileOnBattlefield, new FilterBlockingCreature(), false))); + this.addAbility(new SimpleStaticAbility(new BoostControlledEffect(xValue, StaticValue.get(0), + Duration.WhileOnBattlefield, StaticFilters.FILTER_BLOCKING_CREATURES, false))); } private CrescendoOfWar(final CrescendoOfWar card) { diff --git a/Mage.Sets/src/mage/cards/c/CrimsonHonorGuard.java b/Mage.Sets/src/mage/cards/c/CrimsonHonorGuard.java index 0118e9df1de..9e4ef9f7b5b 100644 --- a/Mage.Sets/src/mage/cards/c/CrimsonHonorGuard.java +++ b/Mage.Sets/src/mage/cards/c/CrimsonHonorGuard.java @@ -4,7 +4,6 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.common.BeginningOfEndStepTriggeredAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; @@ -37,7 +36,7 @@ public final class CrimsonHonorGuard extends CardImpl { this.addAbility(TrampleAbility.getInstance()); // At the beginning of each player's end step, Crimson Honor Guard deals 4 damage to that player unless they control a commander. - this.addAbility(new BeginningOfEndStepTriggeredAbility(new CrimsonHonorGuardEffect(), TargetController.ANY, false)); + this.addAbility(new BeginningOfEndStepTriggeredAbility(new CrimsonHonorGuardEffect(), TargetController.EACH_PLAYER, false)); } private CrimsonHonorGuard(final CrimsonHonorGuard card) { diff --git a/Mage.Sets/src/mage/cards/c/CrosissCharm.java b/Mage.Sets/src/mage/cards/c/CrosissCharm.java index e29e5439fe8..ba0c61798fa 100644 --- a/Mage.Sets/src/mage/cards/c/CrosissCharm.java +++ b/Mage.Sets/src/mage/cards/c/CrosissCharm.java @@ -1,17 +1,13 @@ - package mage.cards.c; import java.util.UUID; -import mage.ObjectColor; import mage.abilities.Mode; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.ReturnToHandTargetEffect; 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.Target; import mage.target.TargetPermanent; import mage.target.common.TargetArtifactPermanent; @@ -23,22 +19,16 @@ import mage.target.common.TargetCreaturePermanent; */ public final class CrosissCharm extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public CrosissCharm(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{U}{B}{R}"); - // Choose one - Return target permanent to its owner's hand; this.getSpellAbility().addEffect(new ReturnToHandTargetEffect()); this.getSpellAbility().addTarget(new TargetPermanent()); // or destroy target nonblack creature, and it can't be regenerated; Mode mode = new Mode(); mode.addEffect(new DestroyTargetEffect(true)); - mode.addTarget(new TargetCreaturePermanent(filter)); + mode.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); this.getSpellAbility().addMode(mode); // or destroy target artifact. mode = new Mode(); diff --git a/Mage.Sets/src/mage/cards/c/CrossroadsConsecrator.java b/Mage.Sets/src/mage/cards/c/CrossroadsConsecrator.java index f16e5631fa9..b1637f09f63 100644 --- a/Mage.Sets/src/mage/cards/c/CrossroadsConsecrator.java +++ b/Mage.Sets/src/mage/cards/c/CrossroadsConsecrator.java @@ -1,4 +1,3 @@ - package mage.cards.c; import java.util.UUID; @@ -6,14 +5,14 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.costs.mana.ColoredManaCost; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.ColoredManaSymbol; import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.Zone; import mage.filter.common.FilterAttackingCreature; import mage.target.common.TargetAttackingCreature; @@ -37,7 +36,7 @@ public final class CrossroadsConsecrator extends CardImpl { this.toughness = new MageInt(2); // {G}, {T}: Target attacking Human gets +1/+1 until end of turn. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostTargetEffect(1, 1, Duration.EndOfTurn), new ManaCostsImpl<>("{G}")); + Ability ability = new SimpleActivatedAbility(new BoostTargetEffect(1, 1, Duration.EndOfTurn), new ColoredManaCost(ColoredManaSymbol.G)); ability.addTarget(new TargetAttackingCreature(1, 1, filter, false)); ability.addCost(new TapSourceCost()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/c/CrosswayTroublemakers.java b/Mage.Sets/src/mage/cards/c/CrosswayTroublemakers.java new file mode 100644 index 00000000000..0e52e0451bc --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CrosswayTroublemakers.java @@ -0,0 +1,68 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DiesCreatureTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.PayLifeCost; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.keyword.DeathtouchAbility; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.permanent.AttackingPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CrosswayTroublemakers extends CardImpl { + + private static final FilterPermanent filter + = new FilterPermanent(SubType.VAMPIRE, "attacking Vampires"); + private static final FilterPermanent filter2 + = new FilterControlledPermanent(SubType.VAMPIRE, "a Vampire you control"); + + static { + filter.add(AttackingPredicate.instance); + } + + public CrosswayTroublemakers(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{B}"); + + this.subtype.add(SubType.VAMPIRE); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + + // Attacking Vampires you control have deathtouch and lifelink. + Ability ability = new SimpleStaticAbility(new GainAbilityControlledEffect( + DeathtouchAbility.getInstance(), Duration.WhileOnBattlefield, filter + )); + ability.addEffect(new GainAbilityControlledEffect( + LifelinkAbility.getInstance(), Duration.WhileOnBattlefield, filter + ).setText("and lifelink")); + this.addAbility(ability); + + // Whenever a Vampire you control dies, you may pay 2 life. If you do, draw a card. + this.addAbility(new DiesCreatureTriggeredAbility(new DoIfCostPaid( + new DrawCardSourceControllerEffect(1), new PayLifeCost(2) + ), false, filter2)); + } + + private CrosswayTroublemakers(final CrosswayTroublemakers card) { + super(card); + } + + @Override + public CrosswayTroublemakers copy() { + return new CrosswayTroublemakers(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CrownOfDoom.java b/Mage.Sets/src/mage/cards/c/CrownOfDoom.java index 6d6b2669340..5fcb5a82d9c 100644 --- a/Mage.Sets/src/mage/cards/c/CrownOfDoom.java +++ b/Mage.Sets/src/mage/cards/c/CrownOfDoom.java @@ -115,7 +115,7 @@ class CrownOfDoomEffect extends OneShotEffect { && newController != null && !Objects.equals(controller.getId(), newController.getId())) { ContinuousEffect effect = new GainControlTargetEffect(Duration.Custom, newController.getId()); - effect.setTargetPointer(new FixedTarget(source.getSourceId())); + effect.setTargetPointer(new FixedTarget(source.getSourceId(), game)); game.addEffect(effect, source); return true; } diff --git a/Mage.Sets/src/mage/cards/c/CrownOfEmpires.java b/Mage.Sets/src/mage/cards/c/CrownOfEmpires.java index 98ab9f546f0..2c4352820ec 100644 --- a/Mage.Sets/src/mage/cards/c/CrownOfEmpires.java +++ b/Mage.Sets/src/mage/cards/c/CrownOfEmpires.java @@ -70,7 +70,7 @@ class CrownOfEmpiresEffect extends OneShotEffect { } if (scepter && throne) { ContinuousEffect effect = new CrownOfEmpiresControlEffect(); - effect.setTargetPointer(new FixedTarget(target.getId())); + effect.setTargetPointer(new FixedTarget(target.getId(), game)); game.getState().setValue(source.getSourceId().toString(), source.getControllerId()); game.addEffect(effect, source); } else { diff --git a/Mage.Sets/src/mage/cards/c/CrownedCeratok.java b/Mage.Sets/src/mage/cards/c/CrownedCeratok.java index ecaf8003ce1..0191ad079ce 100644 --- a/Mage.Sets/src/mage/cards/c/CrownedCeratok.java +++ b/Mage.Sets/src/mage/cards/c/CrownedCeratok.java @@ -11,10 +11,8 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.TargetController; import mage.constants.Zone; -import mage.counters.CounterType; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; /** * @@ -22,12 +20,6 @@ import mage.filter.common.FilterCreaturePermanent; */ public final class CrownedCeratok extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Each creature you control with a +1/+1 counter on it"); - static { - filter.add(CounterType.P1P1.getPredicate()); - filter.add(TargetController.YOU.getControllerPredicate()); - } - public CrownedCeratok(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{G}"); this.subtype.add(SubType.RHINO); @@ -39,8 +31,14 @@ public final class CrownedCeratok extends CardImpl { this.addAbility(TrampleAbility.getInstance()); // Each creature you control with a +1/+1 counter on it has trample. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(TrampleAbility.getInstance(), Duration.WhileOnBattlefield, filter))); - + this.addAbility(new SimpleStaticAbility( + Zone.BATTLEFIELD, + new GainAbilityAllEffect( + TrampleAbility.getInstance(), + Duration.WhileOnBattlefield, + StaticFilters.FILTER_EACH_CONTROLLED_CREATURE_P1P1) + ) + ); } private CrownedCeratok(final CrownedCeratok card) { diff --git a/Mage.Sets/src/mage/cards/c/CruelWitness.java b/Mage.Sets/src/mage/cards/c/CruelWitness.java new file mode 100644 index 00000000000..b7c32e7c4d4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CruelWitness.java @@ -0,0 +1,82 @@ +package mage.cards.c; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.players.Player; + +/** + * + * @author weirddan455 + */ +public final class CruelWitness extends CardImpl { + + public CruelWitness(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}{U}"); + + this.subtype.add(SubType.BIRD); + this.subtype.add(SubType.HORROR); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever you cast a noncreature spell, look at the top card of your library. You may put that card into your graveyard. + this.addAbility(new SpellCastControllerTriggeredAbility(new CruelWitnessEffect(), StaticFilters.FILTER_SPELL_A_NON_CREATURE, false)); + } + + private CruelWitness(final CruelWitness card) { + super(card); + } + + @Override + public CruelWitness copy() { + return new CruelWitness(this); + } +} + +class CruelWitnessEffect extends OneShotEffect { + + public CruelWitnessEffect() { + super(Outcome.Benefit); + staticText = "look at the top card of your library. You may put that card into your graveyard"; + } + + private CruelWitnessEffect(final CruelWitnessEffect effect) { + super(effect); + } + + @Override + public CruelWitnessEffect copy() { + return new CruelWitnessEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Card topCard = controller.getLibrary().getFromTop(game); + if (topCard != null) { + controller.lookAtCards("Top card of your library", topCard, game); + if (controller.chooseUse(Outcome.AIDontUseIt, "Put the top card of your library into your graveyard?", source, game)) { + controller.moveCards(topCard, Zone.GRAVEYARD, source, game); + } + return true; + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/c/CrueltyOfTheSith.java b/Mage.Sets/src/mage/cards/c/CrueltyOfTheSith.java index b02b36ca2ce..458c9acd38d 100644 --- a/Mage.Sets/src/mage/cards/c/CrueltyOfTheSith.java +++ b/Mage.Sets/src/mage/cards/c/CrueltyOfTheSith.java @@ -10,9 +10,7 @@ import mage.abilities.effects.common.discard.DiscardTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.FilterSpell; import mage.filter.StaticFilters; -import mage.filter.predicate.Predicates; import mage.target.TargetPlayer; import mage.target.TargetSpell; @@ -22,18 +20,12 @@ import mage.target.TargetSpell; */ public final class CrueltyOfTheSith extends CardImpl { - private static final FilterSpell filterNoncreature = new FilterSpell("noncreature spell"); - - static { - filterNoncreature.add(Predicates.not(CardType.CREATURE.getPredicate())); - } - public CrueltyOfTheSith(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{U}{B}{R}"); // Choose one - Counter target noncreature spell. this.getSpellAbility().addEffect(new CounterTargetEffect()); - this.getSpellAbility().addTarget(new TargetSpell(filterNoncreature)); + this.getSpellAbility().addTarget(new TargetSpell(StaticFilters.FILTER_SPELL_NON_CREATURE)); // Target player sacrifices a creture. Mode mode = new Mode(); diff --git a/Mage.Sets/src/mage/cards/c/CryOfTheCarnarium.java b/Mage.Sets/src/mage/cards/c/CryOfTheCarnarium.java index ba9584e3488..0079b741b7d 100644 --- a/Mage.Sets/src/mage/cards/c/CryOfTheCarnarium.java +++ b/Mage.Sets/src/mage/cards/c/CryOfTheCarnarium.java @@ -64,15 +64,14 @@ class CryOfTheCarnariumExileEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); + Player controller = game.getPlayer(source.getControllerId()); CardsPutIntoGraveyardWatcher watcher = game.getState().getWatcher(CardsPutIntoGraveyardWatcher.class); - if (player == null || watcher == null) { - return false; - } - Cards cards = new CardsImpl(watcher.getCardsPutToGraveyardFromBattlefield(game)); + if (controller == null || watcher == null) { return false; } + + Cards cards = new CardsImpl(watcher.getCardsPutIntoGraveyardFromBattlefield(game)); cards.removeIf(uuid -> !game.getCard(uuid).isCreature(game)); - player.moveCards(cards, Zone.EXILED, source, game); - return true; + + return controller.moveCards(cards, Zone.EXILED, source, game); } } diff --git a/Mage.Sets/src/mage/cards/c/CryptSliver.java b/Mage.Sets/src/mage/cards/c/CryptSliver.java index 2fc6d710326..f7f025a6906 100644 --- a/Mage.Sets/src/mage/cards/c/CryptSliver.java +++ b/Mage.Sets/src/mage/cards/c/CryptSliver.java @@ -1,4 +1,3 @@ - package mage.cards.c; import java.util.UUID; @@ -15,8 +14,10 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; import mage.constants.Zone; +import mage.filter.FilterPermanent; import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; +import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; /** @@ -25,6 +26,7 @@ import mage.target.common.TargetCreaturePermanent; */ public final class CryptSliver extends CardImpl { + private static final FilterPermanent filter=new FilterPermanent(SubType.SLIVER,"Sliver"); public CryptSliver(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}"); this.subtype.add(SubType.SLIVER); @@ -33,9 +35,11 @@ public final class CryptSliver extends CardImpl { this.toughness = new MageInt(1); // All Slivers have "{tap}: Regenerate target Sliver." - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateTargetEffect(), new TapSourceCost()); - ability.addTarget(new TargetCreaturePermanent(new FilterCreaturePermanent(SubType.SLIVER, "Sliver"))); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(ability, Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS))); + Ability ability = new SimpleActivatedAbility( new RegenerateTargetEffect(), new TapSourceCost()); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(new SimpleStaticAbility( new GainAbilityAllEffect( + ability, Duration.WhileOnBattlefield, filter + ).setText("all Slivers have \"{T}: Regenerate target Sliver.\""))); } private CryptSliver(final CryptSliver card) { diff --git a/Mage.Sets/src/mage/cards/c/CrypticCommand.java b/Mage.Sets/src/mage/cards/c/CrypticCommand.java index 122e5becc7e..ba26fac0186 100644 --- a/Mage.Sets/src/mage/cards/c/CrypticCommand.java +++ b/Mage.Sets/src/mage/cards/c/CrypticCommand.java @@ -12,8 +12,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -68,12 +67,6 @@ public final class CrypticCommand extends CardImpl { class CrypticCommandEffect extends OneShotEffect { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public CrypticCommandEffect() { super(Outcome.Tap); staticText = "Tap all creatures your opponents control"; @@ -89,7 +82,7 @@ class CrypticCommandEffect extends OneShotEffect { if (player == null) { return false; } - for (Permanent creature : game.getBattlefield().getActivePermanents(filter, player.getId(), source.getSourceId(), game)) { + for (Permanent creature : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE, player.getId(), source.getSourceId(), game)) { creature.tap(source, game); } return true; diff --git a/Mage.Sets/src/mage/cards/c/CryptolithFragment.java b/Mage.Sets/src/mage/cards/c/CryptolithFragment.java index 8730d91c2e3..8d7683d41b9 100644 --- a/Mage.Sets/src/mage/cards/c/CryptolithFragment.java +++ b/Mage.Sets/src/mage/cards/c/CryptolithFragment.java @@ -25,7 +25,6 @@ public final class CryptolithFragment extends CardImpl { public CryptolithFragment(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); - this.transformable = true; this.secondSideCardClazz = mage.cards.a.AuroraOfEmrakul.class; // Cryptolith Fragment enters the battlefield tapped. @@ -39,7 +38,7 @@ public final class CryptolithFragment extends CardImpl { // At the beginning of your upkeep, if each player has 10 or less life, transform Cryptolith Fragment. this.addAbility(new TransformAbility()); this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(true), TargetController.YOU, false), + new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(), TargetController.YOU, false), new XorLessLifeCondition(XorLessLifeCondition.CheckType.EACH_PLAYER, 10), "At the beginning of your upkeep, if each player has 10 or less life, transform Cryptolith Fragment.")); } diff --git a/Mage.Sets/src/mage/cards/c/Crystallization.java b/Mage.Sets/src/mage/cards/c/Crystallization.java index 2456d9f5f08..64455c7837a 100644 --- a/Mage.Sets/src/mage/cards/c/Crystallization.java +++ b/Mage.Sets/src/mage/cards/c/Crystallization.java @@ -1,12 +1,10 @@ - package mage.cards.c; import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetAttachedTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.AttachEffect; -import mage.abilities.effects.common.ExileTargetEffect; +import mage.abilities.effects.common.ExileAttachedEffect; import mage.abilities.effects.common.combat.CantAttackBlockAttachedEffect; import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; @@ -16,13 +14,8 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Outcome; import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.permanent.Permanent; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; -import mage.target.targetpointer.FixedTarget; /** * @@ -37,15 +30,14 @@ public final class Crystallization extends CardImpl { // Enchant creature TargetPermanent auraTarget = new TargetCreaturePermanent(); this.getSpellAbility().addTarget(auraTarget); - this.getSpellAbility().addEffect(new AttachEffect(Outcome.AddAbility)); - Ability ability = new EnchantAbility(auraTarget.getTargetName()); - this.addAbility(ability); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.Removal)); + this.addAbility(new EnchantAbility(auraTarget.getTargetName())); // Enchanted creature can't attack or block. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantAttackBlockAttachedEffect(AttachmentType.AURA))); // When enchanted creature becomes the target of a spell or ability, exile that creature. - this.addAbility(new CrystallizationTriggeredAbility()); + this.addAbility(new BecomesTargetAttachedTriggeredAbility(new ExileAttachedEffect().setText("exile that creature"))); } private Crystallization(final Crystallization card) { @@ -57,42 +49,3 @@ public final class Crystallization extends CardImpl { return new Crystallization(this); } } - -class CrystallizationTriggeredAbility extends TriggeredAbilityImpl { - - public CrystallizationTriggeredAbility() { - super(Zone.BATTLEFIELD, new ExileTargetEffect()); - } - - public CrystallizationTriggeredAbility(final CrystallizationTriggeredAbility ability) { - super(ability); - } - - @Override - public CrystallizationTriggeredAbility copy() { - return new CrystallizationTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent enchantment = game.getPermanent(sourceId); - if (enchantment != null && enchantment.getAttachedTo() != null) { - UUID enchanted = enchantment.getAttachedTo(); - if (event.getTargetId().equals(enchanted)) { - getEffects().get(0).setTargetPointer(new FixedTarget(enchanted)); - return true; - } - } - return false; - } - - @Override - public String getRule() { - return "When enchanted creature becomes the target of a spell or ability, exile that creature."; - } -} diff --git a/Mage.Sets/src/mage/cards/c/CultivatorColossus.java b/Mage.Sets/src/mage/cards/c/CultivatorColossus.java new file mode 100644 index 00000000000..1e30399f6eb --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CultivatorColossus.java @@ -0,0 +1,101 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.common.LandsYouControlCount; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.SetPowerToughnessSourceEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInHand; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CultivatorColossus extends CardImpl { + + public CultivatorColossus(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}{G}{G}"); + + this.subtype.add(SubType.PLANT); + this.subtype.add(SubType.BEAST); + this.power = new MageInt(0); + this.toughness = new MageInt(0); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Cultivator Colossus's power and toughness are each equal to the number of lands you control. + this.addAbility(new SimpleStaticAbility( + Zone.ALL, new SetPowerToughnessSourceEffect(LandsYouControlCount.instance, Duration.EndOfGame) + )); + + // When Cultivator Colossus enters the battlefield, you may put a land card from your hand onto the battlefield tapped. If you do, draw a card and repeat this process. + this.addAbility(new EntersBattlefieldTriggeredAbility(new CultivatorColossusEffect())); + } + + private CultivatorColossus(final CultivatorColossus card) { + super(card); + } + + @Override + public CultivatorColossus copy() { + return new CultivatorColossus(this); + } +} + +class CultivatorColossusEffect extends OneShotEffect { + + CultivatorColossusEffect() { + super(Outcome.PutLandInPlay); + staticText = "you may put a land card from your hand onto the battlefield tapped. " + + "If you do, draw a card and repeat this process"; + } + + private CultivatorColossusEffect(final CultivatorColossusEffect effect) { + super(effect); + } + + @Override + public CultivatorColossusEffect copy() { + return new CultivatorColossusEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + while (player.getHand().count(StaticFilters.FILTER_CARD_LAND, game) > 0) { + TargetCard target = new TargetCardInHand( + 0, 1, StaticFilters.FILTER_CARD_LAND + ); + player.choose(outcome, target, source.getSourceId(), game); + Card card = game.getCard(target.getFirstTarget()); + if (card == null) { + break; + } + player.moveCards( + card, Zone.BATTLEFIELD, source, game, true, + false, false, null + ); + if (game.getPermanent(card.getId()) == null) { + break; + } + player.drawCards(1, source, game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/c/CunningAbduction.java b/Mage.Sets/src/mage/cards/c/CunningAbduction.java index 9257bf91b9e..b3f5c7f8dae 100644 --- a/Mage.Sets/src/mage/cards/c/CunningAbduction.java +++ b/Mage.Sets/src/mage/cards/c/CunningAbduction.java @@ -89,7 +89,7 @@ class CunningAbductionExileEffect extends OneShotEffect { game.addEffect(effect, source); // and you may spend mana as though it were mana of any color to cast it effect = new CunningAbductionSpendAnyManaEffect(); - effect.setTargetPointer(new FixedTarget(card.getId())); + effect.setTargetPointer(new FixedTarget(card.getId(), game)); game.addEffect(effect, source); } return true; diff --git a/Mage.Sets/src/mage/cards/c/CunningBandit.java b/Mage.Sets/src/mage/cards/c/CunningBandit.java index 98c94280f71..ad5295ab07c 100644 --- a/Mage.Sets/src/mage/cards/c/CunningBandit.java +++ b/Mage.Sets/src/mage/cards/c/CunningBandit.java @@ -43,7 +43,7 @@ public final class CunningBandit extends CardImpl { this.flipCardName = "Azamuki, Treachery Incarnate"; // Whenever you cast a Spirit or Arcane spell, you may put a ki counter on Cunning Bandit. - this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.KI.createInstance()), StaticFilters.SPIRIT_OR_ARCANE_CARD, true)); + this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.KI.createInstance()), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true)); // At the beginning of the end step, if there are two or more ki counters on Cunning Bandit, you may flip it. this.addAbility(new ConditionalInterveningIfTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/c/CunningWish.java b/Mage.Sets/src/mage/cards/c/CunningWish.java index 1b085f99d38..67b48cde313 100644 --- a/Mage.Sets/src/mage/cards/c/CunningWish.java +++ b/Mage.Sets/src/mage/cards/c/CunningWish.java @@ -16,7 +16,7 @@ import mage.filter.FilterCard; */ public final class CunningWish extends CardImpl { - private static final FilterCard filter = new FilterCard("an instant card"); + private static final FilterCard filter = new FilterCard("instant card"); static { filter.add(CardType.INSTANT.getPredicate()); @@ -25,7 +25,7 @@ public final class CunningWish extends CardImpl { public CunningWish(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{U}"); - // You may choose an instant card you own from outside the game, reveal that card, and put it into your hand. + // You may reveal an instant card you own from outside the game and put it into your hand. this.getSpellAbility().addEffect(new WishEffect(filter)); this.getSpellAbility().addHint(OpenSideboardHint.instance); diff --git a/Mage.Sets/src/mage/cards/c/CuratorOfMysteries.java b/Mage.Sets/src/mage/cards/c/CuratorOfMysteries.java index 89676d6ec06..7254784abb6 100644 --- a/Mage.Sets/src/mage/cards/c/CuratorOfMysteries.java +++ b/Mage.Sets/src/mage/cards/c/CuratorOfMysteries.java @@ -30,7 +30,7 @@ public final class CuratorOfMysteries extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever you cycle or discard another card, scry 1. - this.addAbility(new CycleOrDiscardControllerTriggeredAbility(new ScryEffect(1)).setTriggerPhrase("Whenever you cycle or discard another card, ")); + this.addAbility(new CycleOrDiscardControllerTriggeredAbility(new ScryEffect(1, false)).setTriggerPhrase("Whenever you cycle or discard another card, ")); // Cycling {U} this.addAbility(new CyclingAbility(new ManaCostsImpl("{U}"))); diff --git a/Mage.Sets/src/mage/cards/c/CuriousHomunculus.java b/Mage.Sets/src/mage/cards/c/CuriousHomunculus.java index ad14b25935a..3c67f2eb171 100644 --- a/Mage.Sets/src/mage/cards/c/CuriousHomunculus.java +++ b/Mage.Sets/src/mage/cards/c/CuriousHomunculus.java @@ -33,7 +33,6 @@ public final class CuriousHomunculus extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); - this.transformable = true; this.secondSideCardClazz = mage.cards.v.VoraciousReader.class; // {T}: Add {C}. Spend this mana only to cast an instant or sorcery spell. @@ -42,7 +41,7 @@ public final class CuriousHomunculus extends CardImpl { // At the beginning of your upkeep, if there are three or more instant and/or sorcery cards in your graveyard, transform Curious Homunculus. this.addAbility(new TransformAbility()); this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(true), TargetController.YOU, false), + new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(), TargetController.YOU, false), new InstantOrSorceryCardsInControllerGraveyardCondition(3), "At the beginning of your upkeep, if there are three or more instant and/or sorcery cards in your graveyard, transform {this}")); } diff --git a/Mage.Sets/src/mage/cards/c/CurseOfHospitality.java b/Mage.Sets/src/mage/cards/c/CurseOfHospitality.java new file mode 100644 index 00000000000..28264cd8bdd --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CurseOfHospitality.java @@ -0,0 +1,166 @@ +package mage.cards.c; + +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.continuous.GainAbilityAllEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.game.Game; +import mage.game.events.DamagedEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPlayer; +import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CurseOfHospitality extends CardImpl { + + private static final FilterPermanent filter + = new FilterCreaturePermanent("creatures attacking enchanted player"); + + static { + filter.add(CurseOfHospitalityPredicate.instance); + } + + public CurseOfHospitality(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{R}"); + + this.subtype.add(SubType.AURA); + this.subtype.add(SubType.CURSE); + + // Enchant player + TargetPlayer auraTarget = new TargetPlayer(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + this.addAbility(new EnchantAbility(auraTarget.getTargetName())); + + // Creatures attacking enchanted player have trample. + this.addAbility(new SimpleStaticAbility(new GainAbilityAllEffect( + TrampleAbility.getInstance(), Duration.WhileOnBattlefield, filter + ))); + + // Whenever a creature deals combat damage to enchanted player, that player exiles the top card of their library. Until end of turn, that creature's controller may play that card and they may spend mana as though it were mana of any color to cast that spell. + this.addAbility(new CurseOfHospitalityTriggeredAbility()); + } + + private CurseOfHospitality(final CurseOfHospitality card) { + super(card); + } + + @Override + public CurseOfHospitality copy() { + return new CurseOfHospitality(this); + } +} + +enum CurseOfHospitalityPredicate implements ObjectSourcePlayerPredicate { + instance; + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + Permanent permanent = game.getPermanent(input.getSourceId()); + UUID defenderId = game.getCombat().getDefenderId(input.getObject().getId()); + return permanent != null && defenderId != null && defenderId.equals(permanent.getAttachedTo()); + } +} + +class CurseOfHospitalityTriggeredAbility extends TriggeredAbilityImpl { + + CurseOfHospitalityTriggeredAbility() { + super(Zone.BATTLEFIELD, new CurseOfHospitalityEffect()); + } + + private CurseOfHospitalityTriggeredAbility(final CurseOfHospitalityTriggeredAbility ability) { + super(ability); + } + + @Override + public CurseOfHospitalityTriggeredAbility copy() { + return new CurseOfHospitalityTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DAMAGED_PLAYER; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + DamagedEvent dEvent = (DamagedEvent) event; + Permanent permanent = getSourcePermanentIfItStillExists(game); + if (!dEvent.isCombatDamage() || permanent == null + || !dEvent.getPlayerId().equals(permanent.getAttachedTo())) { + return false; + } + this.getEffects().setTargetPointer(new FixedTarget(game.getControllerId(dEvent.getSourceId()))); + return true; + } + + @Override + public String getRule() { + return "Whenever a creature deals combat damage to enchanted player, " + + "that player exiles the top card of their library. " + + "Until end of turn, that creature's controller may play that card " + + "and they may spend mana as though it were mana of any color to cast that spell."; + } +} + +class CurseOfHospitalityEffect extends OneShotEffect { + + CurseOfHospitalityEffect() { + super(Outcome.Benefit); + } + + private CurseOfHospitalityEffect(final CurseOfHospitalityEffect effect) { + super(effect); + } + + @Override + public CurseOfHospitalityEffect copy() { + return new CurseOfHospitalityEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = source.getSourcePermanentOrLKI(game); + if (permanent == null) { + return false; + } + Player enchanted = game.getPlayer(permanent.getAttachedTo()); + if (enchanted == null) { + return false; + } + Card card = enchanted.getLibrary().getFromTop(game); + if (card == null) { + return false; + } + enchanted.moveCardsToExile( + card, source, game, true, + CardUtil.getExileZoneId(game, source), + CardUtil.getSourceName(game, source) + ); + Player player = game.getPlayer(getTargetPointer().getFirst(game, source)); + if (player == null) { + return true; + } + CardUtil.makeCardPlayable(game, source, card, Duration.EndOfTurn, true, player.getId(), null); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/c/CurseOfLeeches.java b/Mage.Sets/src/mage/cards/c/CurseOfLeeches.java new file mode 100644 index 00000000000..8758a9d82b7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CurseOfLeeches.java @@ -0,0 +1,109 @@ +package mage.cards.c; + +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.LoseLifeTargetEffect; +import mage.abilities.keyword.DayboundAbility; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPlayer; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CurseOfLeeches extends CardImpl { + + public CurseOfLeeches(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}"); + + this.subtype.add(SubType.AURA); + this.subtype.add(SubType.CURSE); + this.secondSideCardClazz = mage.cards.l.LeechingLurker.class; + + // Enchant player + TargetPlayer auraTarget = new TargetPlayer(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // As this permanent transforms into Curse of Leeches, attach it to a player. + this.addAbility(new SimpleStaticAbility(new CurseOfLeechesEffect())); + + // At the beginning of enchanted player's upkeep, they lose 1 life and you gain 1 life. + ability = new BeginningOfUpkeepTriggeredAbility( + Zone.BATTLEFIELD, new LoseLifeTargetEffect(1).setText("they lose 1 life"), + TargetController.ENCHANTED, false, true + ); + ability.addEffect(new GainLifeEffect(1).concatBy("and")); + this.addAbility(ability); + + // Daybound + this.addAbility(new DayboundAbility()); + } + + private CurseOfLeeches(final CurseOfLeeches card) { + super(card); + } + + @Override + public CurseOfLeeches copy() { + return new CurseOfLeeches(this); + } +} + +class CurseOfLeechesEffect extends ReplacementEffectImpl { + + CurseOfLeechesEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "as this permanent transforms into {this}, attach it to a player"; + } + + private CurseOfLeechesEffect(final CurseOfLeechesEffect effect) { + super(effect); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + TargetPlayer target = new TargetPlayer(); + target.withChooseHint("Player to attach to").setNotTarget(true); + controller.choose(Outcome.Detriment, target, source.getSourceId(), game); + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (permanent == null) { + return false; + } + permanent.addAttachment(target.getFirstTarget(), source, game); + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.TRANSFORMING; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return source.getSourceId().equals(event.getTargetId()); + } + + @Override + public CurseOfLeechesEffect copy() { + return new CurseOfLeechesEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CurseOfPredation.java b/Mage.Sets/src/mage/cards/c/CurseOfPredation.java index 2eb6d9d5ba5..13f4a32cbc4 100644 --- a/Mage.Sets/src/mage/cards/c/CurseOfPredation.java +++ b/Mage.Sets/src/mage/cards/c/CurseOfPredation.java @@ -78,7 +78,7 @@ class CurseOfPredationTriggeredAbility extends TriggeredAbilityImpl { if (enchantment != null && enchantment.isAttachedTo(defender.getId())) { for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getSourceId())); + effect.setTargetPointer(new FixedTarget(event.getSourceId(), game)); } return true; } diff --git a/Mage.Sets/src/mage/cards/c/CurseOfStalkedPrey.java b/Mage.Sets/src/mage/cards/c/CurseOfStalkedPrey.java index 114115877cb..3481dd69bb9 100644 --- a/Mage.Sets/src/mage/cards/c/CurseOfStalkedPrey.java +++ b/Mage.Sets/src/mage/cards/c/CurseOfStalkedPrey.java @@ -83,7 +83,7 @@ class CurseOfStalkedPreyTriggeredAbility extends TriggeredAbilityImpl { if (enchantment != null && enchantment.getAttachedTo() != null) { Player player = game.getPlayer(enchantment.getAttachedTo()); if (player != null && event.getTargetId().equals(player.getId())) { - getEffects().get(0).setTargetPointer(new FixedTarget(event.getSourceId())); + getEffects().get(0).setTargetPointer(new FixedTarget(event.getSourceId(), game)); return true; } } diff --git a/Mage.Sets/src/mage/cards/c/CurseOfVengeance.java b/Mage.Sets/src/mage/cards/c/CurseOfVengeance.java index de374dcce54..e6ed7c158af 100644 --- a/Mage.Sets/src/mage/cards/c/CurseOfVengeance.java +++ b/Mage.Sets/src/mage/cards/c/CurseOfVengeance.java @@ -84,7 +84,7 @@ class CurseOfVengeanceTriggeredAbility extends TriggeredAbilityImpl { if (enchantment != null && spell != null && enchantment.isAttachedTo(spell.getControllerId())) { - this.getEffects().get(0).setTargetPointer(new FixedTarget(getSourceId())); + this.getEffects().get(0).setTargetPointer(new FixedTarget(getSourceId(), game)); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/c/CurseboundWitch.java b/Mage.Sets/src/mage/cards/c/CurseboundWitch.java new file mode 100644 index 00000000000..f907e99b2f4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CurseboundWitch.java @@ -0,0 +1,59 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.effects.common.DraftFromSpellbookEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CurseboundWitch extends CardImpl { + + private static final List spellbook = Collections.unmodifiableList(Arrays.asList( + "Black Cat", + "Bloodhunter Bat", + "Cauldron Familiar", + "Cruel Reality", + "Curse of Leeches", + "Expanded Anatomy", + "Sorcerer's Broom", + "Torment of Scarabs", + "Trespasser's Curse", + "Unwilling Ingredient", + "Witch's Cauldron", + "Witch's Cottage", + "Witch's Familiar", + "Witch's Oven", + "Witch's Vengeance" + )); + + public CurseboundWitch(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARLOCK); + this.power = new MageInt(1); + this.toughness = new MageInt(2); + + // When Cursebound Witch dies, draft a card from Cursebound Witch's spellbook. + this.addAbility(new DiesSourceTriggeredAbility(new DraftFromSpellbookEffect(spellbook))); + } + + private CurseboundWitch(final CurseboundWitch card) { + super(card); + } + + @Override + public CurseboundWitch copy() { + return new CurseboundWitch(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CustodyBattle.java b/Mage.Sets/src/mage/cards/c/CustodyBattle.java index 3ca287ddcd8..e4fdefa78b9 100644 --- a/Mage.Sets/src/mage/cards/c/CustodyBattle.java +++ b/Mage.Sets/src/mage/cards/c/CustodyBattle.java @@ -2,6 +2,7 @@ package mage.cards.c; import java.util.UUID; +import mage.MageObject; import mage.target.common.TargetCreaturePermanent; import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; @@ -91,9 +92,10 @@ class GiveControlEffect extends ContinuousEffectImpl { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = (Permanent) source.getSourceObjectIfItStillExists(game); - if (permanent != null) { - return permanent.changeControllerId(source.getFirstTarget(), game, source); + MageObject mageObject = source.getSourceObjectIfItStillExists(game); + if (mageObject != null + && mageObject instanceof Permanent) { + return ((Permanent) mageObject).changeControllerId(source.getFirstTarget(), game, source); } else { discard(); } @@ -135,7 +137,7 @@ class CustodyBattleUnlessPaysEffect extends OneShotEffect { if (source.getSourceObjectZoneChangeCounter() == game.getState().getZoneChangeCounter(source.getSourceId()) && game.getState().getZone(source.getSourceId()) == Zone.BATTLEFIELD) { ContinuousEffect effect = new GiveControlEffect(); - effect.setTargetPointer(new FixedTarget(source.getFirstTarget())); + effect.setTargetPointer(new FixedTarget(source.getFirstTarget(), game)); game.addEffect(effect, source); game.informPlayers(game.getPlayer(source.getFirstTarget()).getLogName() + " gains control of " + sourcePermanent.getIdName()); } diff --git a/Mage.Sets/src/mage/cards/c/CyberdriveAwakener.java b/Mage.Sets/src/mage/cards/c/CyberdriveAwakener.java new file mode 100644 index 00000000000..a1cfa8d872e --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CyberdriveAwakener.java @@ -0,0 +1,131 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledArtifactPermanent; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.game.permanent.Permanent; + +import java.util.List; +import java.util.Objects; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class CyberdriveAwakener extends CardImpl { + + public CyberdriveAwakener(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{5}{U}"); + + this.subtype.add(SubType.CONSTRUCT); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Other artifact creatures you control have flying. + this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( + FlyingAbility.getInstance(), Duration.WhileOnBattlefield, + StaticFilters.FILTER_PERMANENTS_ARTIFACT_CREATURE, true + ))); + + // When Cyberdrive Awakener enters the battlefield, until end of turn, each noncreature artifact you control becomes an artifact creature with base power and toughness 4/4. + this.addAbility(new EntersBattlefieldTriggeredAbility(new CyberdriveAwakenerEffect())); + } + + private CyberdriveAwakener(final CyberdriveAwakener card) { + super(card); + } + + @Override + public CyberdriveAwakener copy() { + return new CyberdriveAwakener(this); + } +} + +class CyberdriveAwakenerEffect extends ContinuousEffectImpl { + + private static final FilterPermanent filter = new FilterControlledArtifactPermanent(); + + static { + filter.add(Predicates.not(CardType.CREATURE.getPredicate())); + } + + CyberdriveAwakenerEffect() { + super(Duration.EndOfTurn, Outcome.BecomeCreature); + staticText = "until end of turn, each noncreature artifact you control " + + "becomes an artifact creature with base power and toughness 4/4"; + } + + private CyberdriveAwakenerEffect(final CyberdriveAwakenerEffect effect) { + super(effect); + } + + @Override + public void init(Ability source, Game game) { + super.init(source, game); + game.getBattlefield() + .getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game) + .stream() + .filter(Objects::nonNull) + .map(permanent -> new MageObjectReference(permanent, game)) + .forEach(affectedObjectList::add); + } + + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + List permanents = affectedObjectList + .stream() + .map(mor -> mor.getPermanent(game)) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + if (permanents.isEmpty()) { + discard(); + return false; + } + for (Permanent permanent : permanents) { + switch (layer) { + case TypeChangingEffects_4: + permanent.addCardType(game, CardType.ARTIFACT, CardType.CREATURE); + break; + case PTChangingEffects_7: + if (sublayer == SubLayer.SetPT_7b) { + permanent.getPower().setValue(4); + permanent.getToughness().setValue(4); + } + } + } + return true; + } + + @Override + public boolean hasLayer(Layer layer) { + return layer == Layer.TypeChangingEffects_4 + || layer == Layer.PTChangingEffects_7; + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public CyberdriveAwakenerEffect copy() { + return new CyberdriveAwakenerEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CytoplastManipulator.java b/Mage.Sets/src/mage/cards/c/CytoplastManipulator.java index c72dce3fae3..f3a670722fb 100644 --- a/Mage.Sets/src/mage/cards/c/CytoplastManipulator.java +++ b/Mage.Sets/src/mage/cards/c/CytoplastManipulator.java @@ -17,8 +17,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Zone; -import mage.counters.CounterType; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; /** @@ -26,11 +25,6 @@ import mage.target.common.TargetCreaturePermanent; * @author JotaPeRL */ public final class CytoplastManipulator extends CardImpl { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature with a +1/+1 counter on it"); - static { - filter.add(CounterType.P1P1.getPredicate()); - } public CytoplastManipulator(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{U}{U}"); @@ -50,7 +44,7 @@ public final class CytoplastManipulator extends CardImpl { "gain control of target creature with a +1/+1 counter on it for as long as {this} remains on the battlefield"); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl("{U}")); ability.addCost(new TapSourceCost()); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_P1P1)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/c/CytoplastRootKin.java b/Mage.Sets/src/mage/cards/c/CytoplastRootKin.java index 5fe91f4613f..66dad5203e4 100644 --- a/Mage.Sets/src/mage/cards/c/CytoplastRootKin.java +++ b/Mage.Sets/src/mage/cards/c/CytoplastRootKin.java @@ -17,8 +17,7 @@ import mage.constants.SubType; import mage.constants.Outcome; import mage.constants.Zone; import mage.counters.CounterType; -import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetControlledCreaturePermanent; @@ -28,12 +27,6 @@ import mage.target.common.TargetControlledCreaturePermanent; * @author emerald000 */ public final class CytoplastRootKin extends CardImpl { - - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("other creature you control that has a +1/+1 counter on it"); - static { - filter.add(AnotherPredicate.instance); - filter.add(CounterType.P1P1.getPredicate()); - } public CytoplastRootKin(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{G}{G}"); @@ -45,8 +38,8 @@ public final class CytoplastRootKin extends CardImpl { // Graft 4 this.addAbility(new GraftAbility(this, 4)); - // When Cytoplast Root-Kin enters the battlefield, put a +1/+1 counter on each other creature you control that has a +1/+1 counter on it. - this.addAbility(new EntersBattlefieldTriggeredAbility(new AddCountersAllEffect(CounterType.P1P1.createInstance(), filter))); + // When Cytoplast Root-Kin enters the battlefield, put a +1/+1 counter on each other creature you control with a +1/+1 counter on it. + this.addAbility(new EntersBattlefieldTriggeredAbility(new AddCountersAllEffect(CounterType.P1P1.createInstance(), StaticFilters.FILTER_OTHER_CONTROLLED_CREATURE_P1P1))); // {2}: Move a +1/+1 counter from target creature you control onto Cytoplast Root-Kin. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CytoplastRootKinEffect(), new GenericManaCost(2)); diff --git a/Mage.Sets/src/mage/cards/c/CytospawnShambler.java b/Mage.Sets/src/mage/cards/c/CytospawnShambler.java index 685c3f018a6..7f6d1d0f13a 100644 --- a/Mage.Sets/src/mage/cards/c/CytospawnShambler.java +++ b/Mage.Sets/src/mage/cards/c/CytospawnShambler.java @@ -15,8 +15,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Zone; -import mage.counters.CounterType; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; /** @@ -24,11 +23,6 @@ import mage.target.common.TargetCreaturePermanent; * @author JotaPeRL */ public final class CytospawnShambler extends CardImpl { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature with a +1/+1 counter on it"); - static { - filter.add(CounterType.P1P1.getPredicate()); - } public CytospawnShambler(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{6}{G}"); @@ -42,7 +36,7 @@ public final class CytospawnShambler extends CardImpl { // {G}: Target creature with a +1/+1 counter on it gains trample until end of turn. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainAbilityTargetEffect(TrampleAbility.getInstance(), Duration.EndOfTurn), new ManaCostsImpl("{G}")); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_P1P1)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/d/DackFayden.java b/Mage.Sets/src/mage/cards/d/DackFayden.java index 6262699e044..bec6d58041f 100644 --- a/Mage.Sets/src/mage/cards/d/DackFayden.java +++ b/Mage.Sets/src/mage/cards/d/DackFayden.java @@ -3,7 +3,6 @@ package mage.cards.d; import java.util.UUID; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.DrawCardTargetEffect; import mage.abilities.effects.common.GetEmblemEffect; @@ -27,7 +26,7 @@ public final class DackFayden extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.DACK); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +1: Target player draws two cards, then discards two cards. LoyaltyAbility ability = new LoyaltyAbility(new DrawCardTargetEffect(2), 1); diff --git a/Mage.Sets/src/mage/cards/d/DakkonShadowSlayer.java b/Mage.Sets/src/mage/cards/d/DakkonShadowSlayer.java index 16e694976c5..0d398ef26c5 100644 --- a/Mage.Sets/src/mage/cards/d/DakkonShadowSlayer.java +++ b/Mage.Sets/src/mage/cards/d/DakkonShadowSlayer.java @@ -3,7 +3,6 @@ package mage.cards.d; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.common.EntersBattlefieldAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.dynamicvalue.common.LandsYouControlCount; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ExileTargetEffect; @@ -35,7 +34,7 @@ public final class DakkonShadowSlayer extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.DAKKON); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(0)); + this.setStartingLoyalty(0); // Dakkon, Shadow Slayer enters the battlefield with a number of loyalty counters on him equal to the number of lands you control. this.addAbility(new EntersBattlefieldAbility( diff --git a/Mage.Sets/src/mage/cards/d/DakmorLancer.java b/Mage.Sets/src/mage/cards/d/DakmorLancer.java index 78f229b148c..0014e1a5257 100644 --- a/Mage.Sets/src/mage/cards/d/DakmorLancer.java +++ b/Mage.Sets/src/mage/cards/d/DakmorLancer.java @@ -1,9 +1,7 @@ - package mage.cards.d; import java.util.UUID; import mage.MageInt; -import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.common.DestroyTargetEffect; @@ -11,9 +9,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -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; /** @@ -23,13 +19,6 @@ import mage.target.common.TargetCreaturePermanent; */ public final class DakmorLancer extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - - public DakmorLancer(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{B}{B}"); this.subtype.add(SubType.HUMAN); @@ -39,7 +28,7 @@ public final class DakmorLancer extends CardImpl { // When Dakmor Lancer enters the battlefield, destroy target nonblack creature. Ability ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect()); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/d/DanceOfMany.java b/Mage.Sets/src/mage/cards/d/DanceOfMany.java index 549c4c05b11..a6c2cf634e1 100644 --- a/Mage.Sets/src/mage/cards/d/DanceOfMany.java +++ b/Mage.Sets/src/mage/cards/d/DanceOfMany.java @@ -93,8 +93,8 @@ class DanceOfManyCreateTokenCopyEffect extends OneShotEffect { CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(); effect.setTargetPointer(new FixedTarget(permanent, game)); effect.apply(game, source); - game.getState().setValue(source.getSourceId() + "_token", effect.getAddedPermanent()); - for (Permanent addedToken : effect.getAddedPermanent()) { + game.getState().setValue(source.getSourceId() + "_token", effect.getAddedPermanents()); + for (Permanent addedToken : effect.getAddedPermanents()) { Effect sacrificeEffect = new SacrificeTargetEffect("sacrifice Dance of Many"); sacrificeEffect.setTargetPointer(new FixedTarget(sourceObject, game)); LeavesBattlefieldTriggeredAbility triggerAbility = new LeavesBattlefieldTriggeredAbility(sacrificeEffect, false); diff --git a/Mage.Sets/src/mage/cards/d/DanceOfTheDead.java b/Mage.Sets/src/mage/cards/d/DanceOfTheDead.java index 5891d52b4be..7b7e0567351 100644 --- a/Mage.Sets/src/mage/cards/d/DanceOfTheDead.java +++ b/Mage.Sets/src/mage/cards/d/DanceOfTheDead.java @@ -34,6 +34,7 @@ import mage.players.Player; import mage.target.Target; import mage.target.common.TargetCardInGraveyard; import mage.target.common.TargetCreaturePermanent; +import mage.util.CardUtil; /** * @@ -277,6 +278,6 @@ class DanceOfTheDeadDoIfCostPaidEffect extends DoIfCostPaid { @Override public String getText(Mode mode) { - return "that player may " + getCostText() + ". If they do, " + executingEffects.getText(mode); + return "that player may " + CardUtil.addCostVerb(cost.getText()) + ". If they do, " + executingEffects.getText(mode); } } diff --git a/Mage.Sets/src/mage/cards/d/DanseMacabre.java b/Mage.Sets/src/mage/cards/d/DanseMacabre.java index 0730a11ad88..1b767ccae65 100644 --- a/Mage.Sets/src/mage/cards/d/DanseMacabre.java +++ b/Mage.Sets/src/mage/cards/d/DanseMacabre.java @@ -94,7 +94,7 @@ class DanseMacabreEffect extends OneShotEffect { TargetPermanent target = new TargetPermanent(filter); target.setNotTarget(true); player.choose(Outcome.Sacrifice, target, source.getSourceId(), game); - Permanent permanent = game.getPermanent(source.getSourceId()); + Permanent permanent = game.getPermanent(target.getFirstTarget()); if (permanent == null) { continue; } diff --git a/Mage.Sets/src/mage/cards/d/DarettiIngeniousIconoclast.java b/Mage.Sets/src/mage/cards/d/DarettiIngeniousIconoclast.java index 21f49b83963..fdb27d80176 100644 --- a/Mage.Sets/src/mage/cards/d/DarettiIngeniousIconoclast.java +++ b/Mage.Sets/src/mage/cards/d/DarettiIngeniousIconoclast.java @@ -2,7 +2,6 @@ package mage.cards.d; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.effects.common.CreateTokenCopyTargetEffect; import mage.abilities.effects.common.CreateTokenEffect; @@ -43,7 +42,7 @@ public final class DarettiIngeniousIconoclast extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.DARETTI); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +1: Create a 1/1 colorless Construct artifact creature token with defender. this.addAbility(new LoyaltyAbility(new CreateTokenEffect(new DarettiConstructToken()), 1)); diff --git a/Mage.Sets/src/mage/cards/d/DarettiScrapSavant.java b/Mage.Sets/src/mage/cards/d/DarettiScrapSavant.java index 0a3f91aa63b..b5bb44a5724 100644 --- a/Mage.Sets/src/mage/cards/d/DarettiScrapSavant.java +++ b/Mage.Sets/src/mage/cards/d/DarettiScrapSavant.java @@ -3,7 +3,6 @@ package mage.cards.d; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.common.CanBeYourCommanderAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.GetEmblemEffect; import mage.abilities.effects.common.discard.DiscardAndDrawThatManyEffect; @@ -33,7 +32,7 @@ public final class DarettiScrapSavant extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.DARETTI); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +2: Discard up to two cards, then draw that many cards. this.addAbility(new LoyaltyAbility(new DiscardAndDrawThatManyEffect(2), 2)); diff --git a/Mage.Sets/src/mage/cards/d/DargoTheShipwrecker.java b/Mage.Sets/src/mage/cards/d/DargoTheShipwrecker.java index 745f8fd3eb8..8e36d815183 100644 --- a/Mage.Sets/src/mage/cards/d/DargoTheShipwrecker.java +++ b/Mage.Sets/src/mage/cards/d/DargoTheShipwrecker.java @@ -126,7 +126,7 @@ class DargoTheShipwreckerWatcher extends Watcher { } Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId()); if (permanent != null && (permanent.isCreature(game) || permanent.isArtifact(game))) { - sacMap.compute(event.getPlayerId(), (u, i) -> i == null ? 1 : Integer.sum(i, 1)); + sacMap.compute(event.getPlayerId(), CardUtil::setOrIncrementValue); } } diff --git a/Mage.Sets/src/mage/cards/d/DaringSleuth.java b/Mage.Sets/src/mage/cards/d/DaringSleuth.java index de0ba14e6c2..01499f1e4ce 100644 --- a/Mage.Sets/src/mage/cards/d/DaringSleuth.java +++ b/Mage.Sets/src/mage/cards/d/DaringSleuth.java @@ -12,7 +12,6 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import java.util.UUID; @@ -28,7 +27,6 @@ public final class DaringSleuth extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(1); - this.transformable = true; this.secondSideCardClazz = mage.cards.b.BearerOfOverwhelmingTruths.class; // When you sacrifice a Clue, transform Daring Sleuth. @@ -49,7 +47,7 @@ public final class DaringSleuth extends CardImpl { class DaringSleuthTriggeredAbility extends TriggeredAbilityImpl { public DaringSleuthTriggeredAbility() { - super(Zone.BATTLEFIELD, new TransformSourceEffect(true)); + super(Zone.BATTLEFIELD, new TransformSourceEffect()); } public DaringSleuthTriggeredAbility(final DaringSleuthTriggeredAbility ability) { diff --git a/Mage.Sets/src/mage/cards/d/DarkBanishing.java b/Mage.Sets/src/mage/cards/d/DarkBanishing.java index 6e431cb8209..b162617a5d8 100644 --- a/Mage.Sets/src/mage/cards/d/DarkBanishing.java +++ b/Mage.Sets/src/mage/cards/d/DarkBanishing.java @@ -1,15 +1,11 @@ - package mage.cards.d; import java.util.UUID; -import mage.ObjectColor; import mage.abilities.effects.common.DestroyTargetEffect; 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; /** @@ -18,17 +14,11 @@ import mage.target.common.TargetCreaturePermanent; */ public final class DarkBanishing extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public DarkBanishing(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{B}"); this.getSpellAbility().addEffect(new DestroyTargetEffect(true)); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); } private DarkBanishing(final DarkBanishing card) { diff --git a/Mage.Sets/src/mage/cards/d/DarkHatchling.java b/Mage.Sets/src/mage/cards/d/DarkHatchling.java index 413b78655bf..e39e6cd2f47 100644 --- a/Mage.Sets/src/mage/cards/d/DarkHatchling.java +++ b/Mage.Sets/src/mage/cards/d/DarkHatchling.java @@ -1,10 +1,7 @@ - - package mage.cards.d; import java.util.UUID; import mage.MageInt; -import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.common.DestroyTargetEffect; @@ -13,9 +10,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -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; /** @@ -24,11 +19,6 @@ import mage.target.common.TargetCreaturePermanent; */ public final class DarkHatchling extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public DarkHatchling (UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{B}{B}"); this.subtype.add(SubType.HORROR); @@ -41,7 +31,7 @@ public final class DarkHatchling extends CardImpl { // When Dark Hatchling enters the battlefield, destroy target nonblack creature. It can't be regenerated. Ability ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect(true)); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/d/DarkImpostor.java b/Mage.Sets/src/mage/cards/d/DarkImpostor.java index b3b21f423d0..31397b6dfd6 100644 --- a/Mage.Sets/src/mage/cards/d/DarkImpostor.java +++ b/Mage.Sets/src/mage/cards/d/DarkImpostor.java @@ -83,12 +83,8 @@ class DarkImpostorExileTargetEffect extends OneShotEffect { if (player == null || permanent == null) { return false; } - Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game); - if (sourcePermanent == null) { - return player.moveCards(permanent, Zone.EXILED, source, game); - } return player.moveCardsToExile( - permanent, source, game, true, CardUtil.getExileZoneId(game, source), sourcePermanent.getIdName() + permanent, source, game, true, CardUtil.getExileZoneId(game, source), CardUtil.getSourceName(game, source) ); } } diff --git a/Mage.Sets/src/mage/cards/d/DarkIntimations.java b/Mage.Sets/src/mage/cards/d/DarkIntimations.java index 2397f33cafa..947dff69c8b 100644 --- a/Mage.Sets/src/mage/cards/d/DarkIntimations.java +++ b/Mage.Sets/src/mage/cards/d/DarkIntimations.java @@ -48,7 +48,7 @@ public final class DarkIntimations extends CardImpl { this.getSpellAbility().addEffect(new DarkIntimationsEffect()); // When you cast a Bolas planeswalker spell, exile Dark Intimations from your graveyard. That planeswalker enters the battlefield with an additional loyalty counter on it. - this.addAbility(new SpellCastControllerTriggeredAbility(Zone.GRAVEYARD, new DarkIntimationsGraveyardEffect(), filter, false, true)); + this.addAbility(new SpellCastControllerTriggeredAbility(Zone.GRAVEYARD, new DarkIntimationsGraveyardEffect(), filter, false, true).setTriggerPhrase("When you cast a Bolas planeswalker spell, ")); } private DarkIntimations(final DarkIntimations card) { diff --git a/Mage.Sets/src/mage/cards/d/DarkOffering.java b/Mage.Sets/src/mage/cards/d/DarkOffering.java index 57799b7e60d..dd094ee0d4d 100644 --- a/Mage.Sets/src/mage/cards/d/DarkOffering.java +++ b/Mage.Sets/src/mage/cards/d/DarkOffering.java @@ -1,16 +1,12 @@ - package mage.cards.d; import java.util.UUID; -import mage.ObjectColor; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.GainLifeEffect; 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; /** @@ -18,12 +14,6 @@ import mage.target.common.TargetCreaturePermanent; * @author fireshoes */ public final class DarkOffering extends CardImpl { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } public DarkOffering(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{4}{B}{B}"); @@ -31,7 +21,7 @@ public final class DarkOffering extends CardImpl { // Destroy target nonblack creature. You gain 3 life. this.getSpellAbility().addEffect(new DestroyTargetEffect()); this.getSpellAbility().addEffect(new GainLifeEffect(3)); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); } private DarkOffering(final DarkOffering card) { diff --git a/Mage.Sets/src/mage/cards/d/DarkProphecy.java b/Mage.Sets/src/mage/cards/d/DarkProphecy.java index de64c5a0980..bb8326bba5f 100644 --- a/Mage.Sets/src/mage/cards/d/DarkProphecy.java +++ b/Mage.Sets/src/mage/cards/d/DarkProphecy.java @@ -9,6 +9,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.TargetController; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import java.util.UUID; @@ -18,18 +19,12 @@ import java.util.UUID; */ public final class DarkProphecy extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature you control"); - - static { - filter.add(TargetController.YOU.getControllerPredicate()); - } - public DarkProphecy(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{B}{B}{B}"); // Whenever a creature you control dies, you draw a card and you lose 1 life. Effect effect = new DrawCardSourceControllerEffect(1, "you"); - Ability ability = new DiesCreatureTriggeredAbility(effect, false, filter); + Ability ability = new DiesCreatureTriggeredAbility(effect, false, StaticFilters.FILTER_CONTROLLED_A_CREATURE); effect = new LoseLifeSourceControllerEffect(1); ability.addEffect(effect.concatBy("and")); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/d/DarkWithering.java b/Mage.Sets/src/mage/cards/d/DarkWithering.java index 37cf5965162..73ccc5e66fd 100644 --- a/Mage.Sets/src/mage/cards/d/DarkWithering.java +++ b/Mage.Sets/src/mage/cards/d/DarkWithering.java @@ -1,17 +1,13 @@ - package mage.cards.d; import java.util.UUID; -import mage.ObjectColor; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.keyword.MadnessAbility; 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; /** @@ -20,18 +16,11 @@ import mage.target.common.TargetCreaturePermanent; */ public final class DarkWithering extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public DarkWithering(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{4}{B}{B}"); - // Destroy target nonblack creature. - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); this.getSpellAbility().addEffect(new DestroyTargetEffect()); // Madness {B} diff --git a/Mage.Sets/src/mage/cards/d/DarkbladeAgent.java b/Mage.Sets/src/mage/cards/d/DarkbladeAgent.java index 03b4dec8549..5e902c1de29 100644 --- a/Mage.Sets/src/mage/cards/d/DarkbladeAgent.java +++ b/Mage.Sets/src/mage/cards/d/DarkbladeAgent.java @@ -52,7 +52,7 @@ public final class DarkbladeAgent extends CardImpl { ), Duration.WhileOnBattlefield ), DarkbladeAgentCondition.instance, "and \"Whenever this creature deals " - + "combat damage to a player, draw a card.\"" + + "combat damage to a player, you draw a card.\"" )); this.addAbility(ability, new DarkbladeAgentWatcher()); } diff --git a/Mage.Sets/src/mage/cards/d/DarkheartSliver.java b/Mage.Sets/src/mage/cards/d/DarkheartSliver.java index a3d2f57da18..bb7d549cb99 100644 --- a/Mage.Sets/src/mage/cards/d/DarkheartSliver.java +++ b/Mage.Sets/src/mage/cards/d/DarkheartSliver.java @@ -34,7 +34,7 @@ public final class DarkheartSliver extends CardImpl { Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainLifeEffect(3), new SacrificeSourceCost()); 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 \"Sacrifice this permanent: You gain 3 life.\""))); } diff --git a/Mage.Sets/src/mage/cards/d/DarthSidiousSithLord.java b/Mage.Sets/src/mage/cards/d/DarthSidiousSithLord.java index a8d859d1284..b06238c860e 100644 --- a/Mage.Sets/src/mage/cards/d/DarthSidiousSithLord.java +++ b/Mage.Sets/src/mage/cards/d/DarthSidiousSithLord.java @@ -4,7 +4,6 @@ package mage.cards.d; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.SacrificeEffect; @@ -39,7 +38,7 @@ public final class DarthSidiousSithLord extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.SIDIOUS); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +3: Destroy target noncreature permanent. Ability ability = new LoyaltyAbility(new DestroyTargetEffect(), +3); diff --git a/Mage.Sets/src/mage/cards/d/DarthTyranusCountOfSerenno.java b/Mage.Sets/src/mage/cards/d/DarthTyranusCountOfSerenno.java index 5207ab9898a..cfa20e3ece1 100644 --- a/Mage.Sets/src/mage/cards/d/DarthTyranusCountOfSerenno.java +++ b/Mage.Sets/src/mage/cards/d/DarthTyranusCountOfSerenno.java @@ -4,7 +4,6 @@ package mage.cards.d; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.SearchEffect; @@ -39,7 +38,7 @@ public final class DarthTyranusCountOfSerenno extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.DOOKU); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +1: Up to one target creature gets -6/-0 until your next turn. Effect effect = new BoostTargetEffect(-6, 0, Duration.UntilYourNextTurn); diff --git a/Mage.Sets/src/mage/cards/d/DauntlessAvenger.java b/Mage.Sets/src/mage/cards/d/DauntlessAvenger.java index d7004ce3498..8659565dace 100644 --- a/Mage.Sets/src/mage/cards/d/DauntlessAvenger.java +++ b/Mage.Sets/src/mage/cards/d/DauntlessAvenger.java @@ -36,7 +36,6 @@ public final class DauntlessAvenger extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(2); this.color.setWhite(true); - this.transformable = true; this.nightCard = true; // Whenever Dauntless Avenger attacks, return target creature card with mana value 2 or less from your graveyard to the battlefield tapped and attacking. diff --git a/Mage.Sets/src/mage/cards/d/DauntlessEscort.java b/Mage.Sets/src/mage/cards/d/DauntlessEscort.java index 96554b54bc4..4157804ce9c 100644 --- a/Mage.Sets/src/mage/cards/d/DauntlessEscort.java +++ b/Mage.Sets/src/mage/cards/d/DauntlessEscort.java @@ -14,8 +14,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Zone; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; /** * @@ -34,8 +33,7 @@ public final class DauntlessEscort extends CardImpl { this.toughness = new MageInt(3); // Sacrifice Dauntless Escort: Creatures you control are indestructible this turn. - FilterPermanent filter = new FilterControlledCreaturePermanent("Creatures you control"); - Effect effect = new GainAbilityAllEffect(IndestructibleAbility.getInstance(), Duration.EndOfTurn, filter, false); + Effect effect = new GainAbilityAllEffect(IndestructibleAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_CONTROLLED_CREATURES, false); effect.setText("Creatures you control are indestructible this turn"); this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new SacrificeSourceCost())); } diff --git a/Mage.Sets/src/mage/cards/d/DavrielRogueShadowmage.java b/Mage.Sets/src/mage/cards/d/DavrielRogueShadowmage.java index 7209a035552..efbca10c547 100644 --- a/Mage.Sets/src/mage/cards/d/DavrielRogueShadowmage.java +++ b/Mage.Sets/src/mage/cards/d/DavrielRogueShadowmage.java @@ -3,7 +3,6 @@ package mage.cards.d; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.condition.Condition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.DamageTargetEffect; @@ -27,7 +26,7 @@ public final class DavrielRogueShadowmage extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.DAVRIEL); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // At the beginning of each opponent's upkeep, if that player has one or fewer cards in hand, Davriel, Rogue Shadowmage deals 2 damage to them. this.addAbility(new ConditionalInterveningIfTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/d/DawnhartDisciple.java b/Mage.Sets/src/mage/cards/d/DawnhartDisciple.java new file mode 100644 index 00000000000..963698ca448 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DawnhartDisciple.java @@ -0,0 +1,49 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.predicate.mageobject.AnotherPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DawnhartDisciple extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent(SubType.HUMAN, "another Human"); + + static { + filter.add(AnotherPredicate.instance); + } + + public DawnhartDisciple(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARLOCK); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Whenever another Human enters the battlefield under your control, Dawnhart Disciple gets +1/+1 until end of turn. + this.addAbility(new EntersBattlefieldControlledTriggeredAbility( + new BoostSourceEffect(1, 1, Duration.EndOfTurn), filter + )); + } + + private DawnhartDisciple(final DawnhartDisciple card) { + super(card); + } + + @Override + public DawnhartDisciple copy() { + return new DawnhartDisciple(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DawnhartGeist.java b/Mage.Sets/src/mage/cards/d/DawnhartGeist.java new file mode 100644 index 00000000000..10f739bcd24 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DawnhartGeist.java @@ -0,0 +1,45 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.common.GainLifeEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterSpell; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DawnhartGeist extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("an enchantment spell"); + + static { + filter.add(CardType.ENCHANTMENT.getPredicate()); + } + + public DawnhartGeist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.subtype.add(SubType.SPIRIT); + this.subtype.add(SubType.WARLOCK); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // Whenever you cast an enchantment spell, you gain 2 life. + this.addAbility(new SpellCastControllerTriggeredAbility(new GainLifeEffect(2), filter, false)); + } + + private DawnhartGeist(final DawnhartGeist card) { + super(card); + } + + @Override + public DawnhartGeist copy() { + return new DawnhartGeist(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DaybreakCombatants.java b/Mage.Sets/src/mage/cards/d/DaybreakCombatants.java new file mode 100644 index 00000000000..343f6080e57 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DaybreakCombatants.java @@ -0,0 +1,49 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DaybreakCombatants extends CardImpl { + + public DaybreakCombatants(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // When Daybreak Combatants enters the battlefield, target creature gets +2/+0 until end of turn. + Ability ability = new EntersBattlefieldTriggeredAbility( + new BoostTargetEffect(2, 0, Duration.EndOfTurn) + ); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + private DaybreakCombatants(final DaybreakCombatants card) { + super(card); + } + + @Override + public DaybreakCombatants copy() { + return new DaybreakCombatants(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DaybreakCoronet.java b/Mage.Sets/src/mage/cards/d/DaybreakCoronet.java index 2d0b71ed7ce..3e7d04984ab 100644 --- a/Mage.Sets/src/mage/cards/d/DaybreakCoronet.java +++ b/Mage.Sets/src/mage/cards/d/DaybreakCoronet.java @@ -1,12 +1,7 @@ - package mage.cards.d; -import java.util.LinkedList; -import java.util.List; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.continuous.BoostEnchantedEffect; import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; @@ -17,44 +12,54 @@ import mage.abilities.keyword.VigilanceAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; +import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.Predicate; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; -import mage.target.common.TargetCreaturePermanent; + +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; +import java.util.UUID; /** - * * @author jeffwadsworth */ public final class DaybreakCoronet extends CardImpl { - + + private static final FilterPermanent filter + = new FilterCreaturePermanent("creature with another Aura attached to it"); + + static { + filter.add(AuraAttachedPredicate.instance); + } + public DaybreakCoronet(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{W}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}{W}"); this.subtype.add(SubType.AURA); - // Enchant creature with another Aura attached to it - FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with another Aura attached to it."); - filter.add(new AuraAttachedPredicate(this.getId())); - TargetPermanent auraTarget = new TargetCreaturePermanent(filter); + TargetPermanent auraTarget = new TargetPermanent(filter); this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); - Ability ability = new EnchantAbility(auraTarget.getTargetName()); - this.addAbility(ability); - + this.addAbility(new EnchantAbility(auraTarget.getTargetName())); + // Enchanted creature gets +3/+3 and has first strike, vigilance, and lifelink. - ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(3, 3, Duration.WhileOnBattlefield)); - Effect effect = new GainAbilityAttachedEffect(FirstStrikeAbility.getInstance(), AttachmentType.AURA); - effect.setText("and has first strike"); - ability.addEffect(effect); - effect = new GainAbilityAttachedEffect(VigilanceAbility.getInstance(), AttachmentType.AURA); - effect.setText(", vigilance"); - ability.addEffect(effect); - effect = new GainAbilityAttachedEffect(LifelinkAbility.getInstance(), AttachmentType.AURA); - effect.setText(", and lifelink"); - ability.addEffect(effect); + Ability ability = new SimpleStaticAbility(new BoostEnchantedEffect( + 3, 3, Duration.WhileOnBattlefield + )); + ability.addEffect(new GainAbilityAttachedEffect( + FirstStrikeAbility.getInstance(), AttachmentType.AURA + ).setText("and has first strike")); + ability.addEffect(new GainAbilityAttachedEffect( + VigilanceAbility.getInstance(), AttachmentType.AURA + ).setText(", vigilance")); + ability.addEffect(new GainAbilityAttachedEffect( + LifelinkAbility.getInstance(), AttachmentType.AURA + ).setText(", and lifelink")); this.addAbility(ability); } @@ -68,28 +73,21 @@ public final class DaybreakCoronet extends CardImpl { } } -class AuraAttachedPredicate implements Predicate { - - private final UUID ownId; - - public AuraAttachedPredicate(UUID ownId) { - this.ownId = ownId; - } +enum AuraAttachedPredicate implements ObjectSourcePlayerPredicate { + instance; @Override - public boolean apply(Permanent input, Game game) { - List attachments = new LinkedList(); - attachments.addAll(input.getAttachments()); - for (UUID uuid : attachments) { - if (!uuid.equals(ownId)) { - Permanent attachment = game.getPermanent(uuid); - if (attachment != null - && attachment.hasSubtype(SubType.AURA, game)) { - return true; - } - } - } - return false; + public boolean apply(ObjectSourcePlayer input, Game game) { + List attachments = new LinkedList<>(); + attachments.addAll(input.getObject().getAttachments()); + return input + .getObject() + .getAttachments() + .stream() + .filter(uuid -> !uuid.equals(input.getSourceId())) + .map(game::getPermanent) + .filter(Objects::nonNull) + .anyMatch(permanent -> permanent.hasSubtype(SubType.AURA, game)); } @Override diff --git a/Mage.Sets/src/mage/cards/d/DaybreakRanger.java b/Mage.Sets/src/mage/cards/d/DaybreakRanger.java index a5bf78e375a..6048aa33965 100644 --- a/Mage.Sets/src/mage/cards/d/DaybreakRanger.java +++ b/Mage.Sets/src/mage/cards/d/DaybreakRanger.java @@ -37,7 +37,6 @@ public final class DaybreakRanger extends CardImpl { this.subtype.add(SubType.RANGER); this.subtype.add(SubType.WEREWOLF); - this.transformable = true; this.secondSideCardClazz = mage.cards.n.NightfallPredator.class; this.power = new MageInt(2); diff --git a/Mage.Sets/src/mage/cards/d/DazzlingLights.java b/Mage.Sets/src/mage/cards/d/DazzlingLights.java index 47093b579be..c2bc970fe21 100644 --- a/Mage.Sets/src/mage/cards/d/DazzlingLights.java +++ b/Mage.Sets/src/mage/cards/d/DazzlingLights.java @@ -23,7 +23,7 @@ public final class DazzlingLights extends CardImpl { this.getSpellAbility().addTarget(new TargetCreaturePermanent()); // Surveil 2. - this.getSpellAbility().addEffect(new SurveilEffect(2)); + this.getSpellAbility().addEffect(new SurveilEffect(2).concatBy("
")); } private DazzlingLights(final DazzlingLights card) { diff --git a/Mage.Sets/src/mage/cards/d/DeadMansChest.java b/Mage.Sets/src/mage/cards/d/DeadMansChest.java index e5805813ffb..e906c28afa2 100644 --- a/Mage.Sets/src/mage/cards/d/DeadMansChest.java +++ b/Mage.Sets/src/mage/cards/d/DeadMansChest.java @@ -14,7 +14,7 @@ import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.ManaPoolItem; @@ -31,19 +31,13 @@ import java.util.UUID; */ public final class DeadMansChest extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public DeadMansChest(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}"); this.subtype.add(SubType.AURA); // Enchant creature an opponent controls - TargetPermanent auraTarget = new TargetPermanent(filter); + TargetPermanent auraTarget = new TargetPermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE); this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.Benefit)); Ability ability = new EnchantAbility(auraTarget.getTargetName()); diff --git a/Mage.Sets/src/mage/cards/d/DeadRevels.java b/Mage.Sets/src/mage/cards/d/DeadRevels.java index b0c8368e773..87422d87769 100644 --- a/Mage.Sets/src/mage/cards/d/DeadRevels.java +++ b/Mage.Sets/src/mage/cards/d/DeadRevels.java @@ -6,8 +6,7 @@ import mage.abilities.keyword.SpectacleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.FilterCard; -import mage.filter.common.FilterCreatureCard; +import mage.filter.StaticFilters; import mage.target.common.TargetCardInYourGraveyard; import java.util.UUID; @@ -17,14 +16,12 @@ import java.util.UUID; */ public final class DeadRevels extends CardImpl { - private static final FilterCard filter = new FilterCreatureCard("creature cards from your graveyard"); - public DeadRevels(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{B}"); // Return up to two target creature cards from your graveyard to your hand. this.getSpellAbility().addEffect(new ReturnFromGraveyardToHandTargetEffect()); - this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, 2, filter)); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, 2, StaticFilters.FILTER_CARD_CREATURES_YOUR_GRAVEYARD)); // Spectacle {1}{B} this.addAbility(new SpectacleAbility(this, new ManaCostsImpl("{1}{B}"))); diff --git a/Mage.Sets/src/mage/cards/d/DeadRingers.java b/Mage.Sets/src/mage/cards/d/DeadRingers.java index 5154aaaf609..c49c070e635 100644 --- a/Mage.Sets/src/mage/cards/d/DeadRingers.java +++ b/Mage.Sets/src/mage/cards/d/DeadRingers.java @@ -1,15 +1,12 @@ package mage.cards.d; import java.util.UUID; -import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.effects.common.DestroyTargetEffect; 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.game.Game; import mage.game.permanent.Permanent; import mage.target.Target; @@ -21,18 +18,12 @@ import mage.target.common.TargetCreaturePermanent; */ public final class DeadRingers extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public DeadRingers(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{B}"); // Destroy two target nonblack creatures unless either one is a color the other isn't. They can't be regenerated. this.getSpellAbility().addEffect(new DeadRingersEffect()); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(2, 2, filter, false)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(2, 2, StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK, false)); } private DeadRingers(final DeadRingers card) { diff --git a/Mage.Sets/src/mage/cards/d/DeadeyeHarpooner.java b/Mage.Sets/src/mage/cards/d/DeadeyeHarpooner.java index 05ea3661bb7..f6912f11f9d 100644 --- a/Mage.Sets/src/mage/cards/d/DeadeyeHarpooner.java +++ b/Mage.Sets/src/mage/cards/d/DeadeyeHarpooner.java @@ -1,7 +1,5 @@ - package mage.cards.d; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -13,18 +11,21 @@ import mage.cards.CardSetInfo; import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.SubType; +import mage.filter.FilterPermanent; import mage.filter.common.FilterOpponentsCreaturePermanent; import mage.filter.predicate.permanent.TappedPredicate; -import mage.target.common.TargetOpponentsCreaturePermanent; +import mage.target.TargetPermanent; import mage.watchers.common.RevoltWatcher; +import java.util.UUID; + /** - * * @author Styxo */ public final class DeadeyeHarpooner extends CardImpl { - private static final FilterOpponentsCreaturePermanent filter = new FilterOpponentsCreaturePermanent("tapped creature an opponent controls"); + private static final FilterPermanent filter + = new FilterOpponentsCreaturePermanent("tapped creature an opponent controls"); static { filter.add(TappedPredicate.TAPPED); @@ -39,15 +40,14 @@ public final class DeadeyeHarpooner extends CardImpl { this.toughness = new MageInt(2); // Revolt — When Deadeye Harpooner enters the battlefield, if a permanent you controlled left the battlefield this turn, destroy target tapped creature an opponent controls. - Ability ability = new ConditionalInterveningIfTriggeredAbility(new EntersBattlefieldTriggeredAbility( - new DestroyTargetEffect(), false), RevoltCondition.instance, - "Revolt — When {this} enters the battlefield, if a permanent you controlled left" - + " the battlefield this turn, destroy target tapped creature an opponent controls." + Ability ability = new ConditionalInterveningIfTriggeredAbility( + new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect(), false), + RevoltCondition.instance, "When {this} enters the battlefield, if a permanent you controlled " + + "left the battlefield this turn, destroy target tapped creature an opponent controls." ); ability.setAbilityWord(AbilityWord.REVOLT); - ability.addTarget(new TargetOpponentsCreaturePermanent(filter)); - ability.addWatcher(new RevoltWatcher()); - this.addAbility(ability); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability, new RevoltWatcher()); } private DeadeyeHarpooner(final DeadeyeHarpooner card) { diff --git a/Mage.Sets/src/mage/cards/d/DeadlyDancer.java b/Mage.Sets/src/mage/cards/d/DeadlyDancer.java new file mode 100644 index 00000000000..6cb2633093f --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DeadlyDancer.java @@ -0,0 +1,88 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.TransformIntoSourceTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.keyword.TrampleAbility; +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.game.Game; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DeadlyDancer extends CardImpl { + + public DeadlyDancer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); + + this.subtype.add(SubType.VAMPIRE); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + this.color.setRed(true); + this.nightCard = true; + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // When this creature transforms into Deadly Dancer, add {R}{R}. Until end of turn, you don't lose this mana as steps and phases end. + this.addAbility(new TransformIntoSourceTriggeredAbility(new DeadlyDancerEffect())); + + // {R}{R}: Deadly Dancer and another target creature each get +1/+0 until end of turn. + Ability ability = new SimpleActivatedAbility(new BoostSourceEffect( + 1, 0, Duration.EndOfTurn + ).setText("{this}"), new ManaCostsImpl<>("{R}{R}")); + ability.addEffect(new BoostTargetEffect(1, 0) + .setText("and another target creature each get +1/+0 until end of turn")); + this.addAbility(ability); + } + + private DeadlyDancer(final DeadlyDancer card) { + super(card); + } + + @Override + public DeadlyDancer copy() { + return new DeadlyDancer(this); + } +} + +class DeadlyDancerEffect extends OneShotEffect { + + DeadlyDancerEffect() { + super(Outcome.Benefit); + staticText = "add {R}{R}. Until end of turn, you don't lose this mana as steps and phases end"; + } + + private DeadlyDancerEffect(final DeadlyDancerEffect effect) { + super(effect); + } + + @Override + public DeadlyDancerEffect copy() { + return new DeadlyDancerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + player.getManaPool().addMana(Mana.RedMana(2), game, source, true); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/d/DeadlyVisit.java b/Mage.Sets/src/mage/cards/d/DeadlyVisit.java index 60bcebfe7ed..752847beb23 100644 --- a/Mage.Sets/src/mage/cards/d/DeadlyVisit.java +++ b/Mage.Sets/src/mage/cards/d/DeadlyVisit.java @@ -22,7 +22,7 @@ public final class DeadlyVisit extends CardImpl { this.getSpellAbility().addTarget(new TargetCreaturePermanent()); // Surveil 2. - this.getSpellAbility().addEffect(new SurveilEffect(2)); + this.getSpellAbility().addEffect(new SurveilEffect(2).concatBy("
")); } private DeadlyVisit(final DeadlyVisit card) { diff --git a/Mage.Sets/src/mage/cards/d/DeathBomb.java b/Mage.Sets/src/mage/cards/d/DeathBomb.java index 1c72e90cf2e..a528cb8af4f 100644 --- a/Mage.Sets/src/mage/cards/d/DeathBomb.java +++ b/Mage.Sets/src/mage/cards/d/DeathBomb.java @@ -1,8 +1,6 @@ - package mage.cards.d; import java.util.UUID; -import mage.ObjectColor; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.LoseLifeTargetControllerEffect; @@ -10,9 +8,6 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.filter.StaticFilters; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ColorPredicate; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; @@ -22,20 +17,15 @@ import mage.target.common.TargetCreaturePermanent; */ public final class DeathBomb extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public DeathBomb(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{3}{B}"); // As an additional cost to cast Death Bomb, sacrifice a creature. - this.getSpellAbility().addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1,1, StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT, true))); + this.getSpellAbility().addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT))); // Destroy target nonblack creature. It can't be regenerated. Its controller loses 2 life. this.getSpellAbility().addEffect(new DestroyTargetEffect(true)); this.getSpellAbility().addEffect(new LoseLifeTargetControllerEffect(2)); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); } private DeathBomb(final DeathBomb card) { diff --git a/Mage.Sets/src/mage/cards/d/DeathFrenzy.java b/Mage.Sets/src/mage/cards/d/DeathFrenzy.java index 2a58d9f5a48..d7b35c8d05d 100644 --- a/Mage.Sets/src/mage/cards/d/DeathFrenzy.java +++ b/Mage.Sets/src/mage/cards/d/DeathFrenzy.java @@ -1,4 +1,3 @@ - package mage.cards.d; import java.util.UUID; @@ -11,7 +10,6 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.filter.StaticFilters; -import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; @@ -25,9 +23,8 @@ public final class DeathFrenzy extends CardImpl { public DeathFrenzy(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{B}{G}"); - // All creatures get -2/-2 until end of turn. Whenever a creature dies this turn, you gain 1 life. - this.getSpellAbility().addEffect(new BoostAllEffect(-2, -2, Duration.EndOfTurn, new FilterCreaturePermanent("All creatures"), false)); + this.getSpellAbility().addEffect(new BoostAllEffect(-2, -2, Duration.EndOfTurn)); this.getSpellAbility().addEffect(new CreateDelayedTriggeredAbilityEffect(new DeathFrenzyDelayedTriggeredAbility())); } diff --git a/Mage.Sets/src/mage/cards/d/DeathMaskDuplicant.java b/Mage.Sets/src/mage/cards/d/DeathMaskDuplicant.java index bbe060867f5..50a21ea5903 100644 --- a/Mage.Sets/src/mage/cards/d/DeathMaskDuplicant.java +++ b/Mage.Sets/src/mage/cards/d/DeathMaskDuplicant.java @@ -1,4 +1,3 @@ - package mage.cards.d; import java.util.UUID; @@ -93,7 +92,9 @@ public final class DeathMaskDuplicant extends CardImpl { Player player = game.getPlayer(playerId); if (player != null) { UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), sourceObject.getZoneChangeCounter(game)); - if (exileId != null) { + if (exileId != null + && game.getState().getExile().getExileZone(exileId) != null + && !game.getState().getExile().getExileZone(exileId).isEmpty()) { for (UUID cardId : game.getState().getExile().getExileZone(exileId)) { Card card = game.getCard(cardId); if (card != null && card.isCreature(game)) { diff --git a/Mage.Sets/src/mage/cards/d/DeathMutation.java b/Mage.Sets/src/mage/cards/d/DeathMutation.java index 2fdbea7ba75..506aa97ca04 100644 --- a/Mage.Sets/src/mage/cards/d/DeathMutation.java +++ b/Mage.Sets/src/mage/cards/d/DeathMutation.java @@ -1,17 +1,13 @@ - package mage.cards.d; import java.util.UUID; -import mage.ObjectColor; import mage.abilities.dynamicvalue.common.TargetManaValue; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.DestroyTargetEffect; 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.game.permanent.token.SaprolingToken; import mage.target.TargetPermanent; @@ -21,19 +17,12 @@ import mage.target.TargetPermanent; */ public final class DeathMutation extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public DeathMutation(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{6}{B}{G}"); - // Destroy target nonblack creature. It can't be regenerated. this.getSpellAbility().addEffect(new DestroyTargetEffect(true)); - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); // create X 1/1 green Saproling creature tokens, where X is that creature's converted mana cost. this.getSpellAbility().addEffect(new CreateTokenEffect(new SaprolingToken(), TargetManaValue.instance)); } diff --git a/Mage.Sets/src/mage/cards/d/DeathTyrant.java b/Mage.Sets/src/mage/cards/d/DeathTyrant.java new file mode 100644 index 00000000000..c41d2a17c76 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DeathTyrant.java @@ -0,0 +1,104 @@ +package mage.cards.d; + +import java.util.UUID; +import mage.MageInt; +import mage.MageObject; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect; +import mage.constants.SubType; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.ZombieToken; + +/** + * + * @author weirddan455 + */ +public final class DeathTyrant extends CardImpl { + + public DeathTyrant(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}"); + + this.subtype.add(SubType.BEHOLDER); + this.subtype.add(SubType.SKELETON); + this.power = new MageInt(4); + this.toughness = new MageInt(6); + + // Menace + this.addAbility(new MenaceAbility(false)); + + // Negative Energy Cone — Whenever an attacking creature you control or a blocking creature an opponent controls dies, create a 2/2 black Zombie creature token. + this.addAbility(new DeathTyrantTriggeredAbility().withFlavorWord("Negative Energy Cone")); + + // {5}{B}: Return Death Tyrant from your graveyard to the battlefield tapped. + this.addAbility(new SimpleActivatedAbility( + Zone.GRAVEYARD, new ReturnSourceFromGraveyardToBattlefieldEffect(true), + new ManaCostsImpl<>("{5}{B}") + )); + } + + private DeathTyrant(final DeathTyrant card) { + super(card); + } + + @Override + public DeathTyrant copy() { + return new DeathTyrant(this); + } +} + +class DeathTyrantTriggeredAbility extends TriggeredAbilityImpl { + + public DeathTyrantTriggeredAbility() { + super(Zone.ALL, new CreateTokenEffect(new ZombieToken())); + } + + private DeathTyrantTriggeredAbility(final DeathTyrantTriggeredAbility ability) { + super(ability); + } + + @Override + public DeathTyrantTriggeredAbility copy() { + return new DeathTyrantTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + if (zEvent.isDiesEvent()) { + Permanent permanent = game.getPermanentOrLKIBattlefield(zEvent.getTargetId()); + if (permanent != null && permanent.isCreature(game)) { + if (permanent.isControlledBy(controllerId) && permanent.isAttacking()) { + return true; + } + return game.getOpponents(controllerId).contains(permanent.getControllerId()) && permanent.getBlocking() > 0; + } + } + return false; + } + + @Override + public boolean isInUseableZone(Game game, MageObject source, GameEvent event) { + return TriggeredAbilityImpl.isInUseableZoneDiesTrigger(this, event, game); + } + + @Override + public String getTriggerPhrase() { + return "Whenever an attacking creature you control or a blocking creature an opponent controls dies, "; + } +} diff --git a/Mage.Sets/src/mage/cards/d/DeathWish.java b/Mage.Sets/src/mage/cards/d/DeathWish.java index 526112d3cf8..16a8e5661cd 100644 --- a/Mage.Sets/src/mage/cards/d/DeathWish.java +++ b/Mage.Sets/src/mage/cards/d/DeathWish.java @@ -7,7 +7,6 @@ import mage.abilities.hint.common.OpenSideboardHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.StaticFilters; import java.util.UUID; @@ -19,8 +18,8 @@ public final class DeathWish extends CardImpl { public DeathWish(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}{B}"); - // You may choose a card you own from outside the game and put it into your hand. - this.getSpellAbility().addEffect(new WishEffect(StaticFilters.FILTER_CARD_A, false)); + // You may put a card you own from outside the game into your hand. + this.getSpellAbility().addEffect(new WishEffect()); this.getSpellAbility().addHint(OpenSideboardHint.instance); // You lose half your life, rounded up. diff --git a/Mage.Sets/src/mage/cards/d/DeathbonnetHulk.java b/Mage.Sets/src/mage/cards/d/DeathbonnetHulk.java index 99b151931c9..7d39869b0f5 100644 --- a/Mage.Sets/src/mage/cards/d/DeathbonnetHulk.java +++ b/Mage.Sets/src/mage/cards/d/DeathbonnetHulk.java @@ -30,7 +30,6 @@ public final class DeathbonnetHulk extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(3); this.color.setGreen(true); - this.transformable = true; this.nightCard = true; // At the beginning of your upkeep, you may exile a card from a graveyard. If a creature card was exiled this way, put a +1/+1 counter on Deathbonnet Hulk. diff --git a/Mage.Sets/src/mage/cards/d/DeathbonnetSprout.java b/Mage.Sets/src/mage/cards/d/DeathbonnetSprout.java index 1dca8bc1abc..9089004165e 100644 --- a/Mage.Sets/src/mage/cards/d/DeathbonnetSprout.java +++ b/Mage.Sets/src/mage/cards/d/DeathbonnetSprout.java @@ -2,7 +2,7 @@ package mage.cards.d; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.condition.Condition; import mage.abilities.condition.common.CardsInControllerGraveyardCondition; import mage.abilities.decorator.ConditionalOneShotEffect; @@ -16,6 +16,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; +import mage.constants.TargetController; import mage.filter.StaticFilters; import java.util.UUID; @@ -38,14 +39,15 @@ public final class DeathbonnetSprout extends CardImpl { this.subtype.add(SubType.FUNGUS); this.power = new MageInt(1); this.toughness = new MageInt(1); - this.transformable = true; this.secondSideCardClazz = mage.cards.d.DeathbonnetHulk.class; // At the beginning of your upkeep, mill a card. Then if there are three or more creature cards in your graveyard, transform Deathbonnet Sprout. this.addAbility(new TransformAbility()); - Ability ability = new EntersBattlefieldTriggeredAbility(new MillCardsControllerEffect(1)); + Ability ability = new BeginningOfUpkeepTriggeredAbility( + new MillCardsControllerEffect(1), TargetController.YOU, false + ); ability.addEffect(new ConditionalOneShotEffect( - new TransformSourceEffect(true), condition, + new TransformSourceEffect(), condition, "Then if there are three or more creature cards in your graveyard, transform {this}" )); this.addAbility(ability.addHint(hint)); diff --git a/Mage.Sets/src/mage/cards/d/DeathcapGlade.java b/Mage.Sets/src/mage/cards/d/DeathcapGlade.java new file mode 100644 index 00000000000..14e82f3f650 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DeathcapGlade.java @@ -0,0 +1,49 @@ +package mage.cards.d; + +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.TapSourceEffect; +import mage.abilities.mana.BlackManaAbility; +import mage.abilities.mana.GreenManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DeathcapGlade extends CardImpl { + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition( + StaticFilters.FILTER_LANDS, ComparisonType.FEWER_THAN, 2 + ); + + public DeathcapGlade(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // Deathcap Glade enters the battlefield tapped unless you control two or more other lands. + this.addAbility(new EntersBattlefieldAbility( + new ConditionalOneShotEffect(new TapSourceEffect(), condition), + "tapped unless you control two or more other lands" + )); + + // {T}: Add {B} or {G}. + this.addAbility(new BlackManaAbility()); + this.addAbility(new GreenManaAbility()); + } + + private DeathcapGlade(final DeathcapGlade card) { + super(card); + } + + @Override + public DeathcapGlade copy() { + return new DeathcapGlade(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DeathcultRogue.java b/Mage.Sets/src/mage/cards/d/DeathcultRogue.java index cf43f6fa02f..721ea64e062 100644 --- a/Mage.Sets/src/mage/cards/d/DeathcultRogue.java +++ b/Mage.Sets/src/mage/cards/d/DeathcultRogue.java @@ -1,17 +1,15 @@ package mage.cards.d; import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.RestrictionEffect; +import mage.abilities.common.SimpleEvasionAbility; +import mage.abilities.effects.common.combat.CantBeBlockedByCreaturesSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; import java.util.UUID; @@ -20,6 +18,12 @@ import java.util.UUID; */ public final class DeathcultRogue extends CardImpl { + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("except by Rogues"); + + static { + filter.add(Predicates.not(SubType.ROGUE.getPredicate())); + } + public DeathcultRogue(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U/B}{U/B}"); this.subtype.add(SubType.HUMAN); @@ -29,7 +33,7 @@ public final class DeathcultRogue extends CardImpl { this.toughness = new MageInt(2); // Deathcult Rogue can't be blocked except by Rogues. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DeathcultRogueRestrictionEffect())); + this.addAbility(new SimpleEvasionAbility(new CantBeBlockedByCreaturesSourceEffect(filter, Duration.WhileOnBattlefield))); } @@ -42,30 +46,3 @@ public final class DeathcultRogue extends CardImpl { return new DeathcultRogue(this); } } - -class DeathcultRogueRestrictionEffect extends RestrictionEffect { - - public DeathcultRogueRestrictionEffect() { - super(Duration.WhileOnBattlefield); - staticText = "Deathcult Rogue can't be blocked except by Rogues"; - } - - public DeathcultRogueRestrictionEffect(final DeathcultRogueRestrictionEffect effect) { - super(effect); - } - - @Override - public boolean applies(Permanent permanent, Ability source, Game game) { - return permanent.getId().equals(source.getSourceId()); - } - - @Override - public boolean canBeBlocked(Permanent attacker, Permanent blocker, Ability source, Game game, boolean canUseChooseDialogs) { - return blocker.hasSubtype(SubType.ROGUE, game); - } - - @Override - public DeathcultRogueRestrictionEffect copy() { - return new DeathcultRogueRestrictionEffect(this); - } -} diff --git a/Mage.Sets/src/mage/cards/d/Deathgazer.java b/Mage.Sets/src/mage/cards/d/Deathgazer.java index 77bc0059595..91b1633fdca 100644 --- a/Mage.Sets/src/mage/cards/d/Deathgazer.java +++ b/Mage.Sets/src/mage/cards/d/Deathgazer.java @@ -1,9 +1,7 @@ - package mage.cards.d; import java.util.UUID; import mage.MageInt; -import mage.ObjectColor; import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility; import mage.abilities.effects.Effect; @@ -13,9 +11,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ColorPredicate; +import mage.filter.StaticFilters; /** * @@ -23,12 +19,6 @@ import mage.filter.predicate.mageobject.ColorPredicate; */ public final class Deathgazer extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public Deathgazer(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); this.subtype.add(SubType.LIZARD); @@ -39,7 +29,7 @@ public final class Deathgazer extends CardImpl { // Whenever Deathgazer blocks or becomes blocked by a nonblack creature, destroy that creature at end of combat. Effect effect = new CreateDelayedTriggeredAbilityEffect(new AtTheEndOfCombatDelayedTriggeredAbility(new DestroyTargetEffect()), true); effect.setText("destroy that creature at end of combat"); - this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(effect, filter, false)); + this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(effect, StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK, false)); } diff --git a/Mage.Sets/src/mage/cards/d/DeathsDuet.java b/Mage.Sets/src/mage/cards/d/DeathsDuet.java index dfc50146cf8..0e8f8683e12 100644 --- a/Mage.Sets/src/mage/cards/d/DeathsDuet.java +++ b/Mage.Sets/src/mage/cards/d/DeathsDuet.java @@ -1,4 +1,3 @@ - package mage.cards.d; import java.util.UUID; @@ -6,7 +5,7 @@ import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterCreatureCard; +import mage.filter.StaticFilters; import mage.target.common.TargetCardInYourGraveyard; /** @@ -20,7 +19,7 @@ public final class DeathsDuet extends CardImpl { // Return two target creature cards from your graveyard to your hand. this.getSpellAbility().addEffect(new ReturnFromGraveyardToHandTargetEffect()); - this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(2, new FilterCreatureCard("creature cards from your graveyard"))); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(2, StaticFilters.FILTER_CARD_CREATURES_YOUR_GRAVEYARD)); } private DeathsDuet(final DeathsDuet card) { diff --git a/Mage.Sets/src/mage/cards/d/DebtToTheKami.java b/Mage.Sets/src/mage/cards/d/DebtToTheKami.java new file mode 100644 index 00000000000..45782470c23 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DebtToTheKami.java @@ -0,0 +1,122 @@ +package mage.cards.d; + +import java.util.UUID; + +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.common.FilterControlledPermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.Target; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetControlledPermanent; +import mage.target.common.TargetOpponent; + +/** + * + * @author weirddan455 + */ +public final class DebtToTheKami extends CardImpl { + + public DebtToTheKami(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{B}"); + + // Choose one— + // • Target opponent exiles a creature they control. + this.getSpellAbility().addEffect(new DebtToTheKamiExileCreatureEffect()); + this.getSpellAbility().addTarget(new TargetOpponent()); + + // • Target opponent exiles an enchantment they control. + Mode mode = new Mode(new DebtToTheKamiExileEnchantmentEffect()); + mode.addTarget(new TargetOpponent()); + this.getSpellAbility().addMode(mode); + } + + private DebtToTheKami(final DebtToTheKami card) { + super(card); + } + + @Override + public DebtToTheKami copy() { + return new DebtToTheKami(this); + } +} + +class DebtToTheKamiExileCreatureEffect extends OneShotEffect { + + public DebtToTheKamiExileCreatureEffect() { + super(Outcome.Exile); + this.staticText = "Target opponent exiles a creature they control"; + } + + private DebtToTheKamiExileCreatureEffect(final DebtToTheKamiExileCreatureEffect effect) { + super(effect); + } + + @Override + public DebtToTheKamiExileCreatureEffect copy() { + return new DebtToTheKamiExileCreatureEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getFirstTarget()); + if (player == null) { + return false; + } + Target target = new TargetControlledCreaturePermanent(); + target.setNotTarget(true); + player.choose(outcome, target, source.getSourceId(), game); + Permanent permanent = game.getPermanent(target.getFirstTarget()); + if (permanent == null) { + return false; + } + return player.moveCards(permanent, Zone.EXILED, source, game); + } +} + +class DebtToTheKamiExileEnchantmentEffect extends OneShotEffect { + + private static final FilterControlledPermanent filter = new FilterControlledPermanent("enchantment you control"); + + static { + filter.add(CardType.ENCHANTMENT.getPredicate()); + } + + public DebtToTheKamiExileEnchantmentEffect() { + super(Outcome.Exile); + this.staticText = "Target opponent exiles an enchantment they control"; + } + + private DebtToTheKamiExileEnchantmentEffect(final DebtToTheKamiExileEnchantmentEffect effect) { + super(effect); + } + + @Override + public DebtToTheKamiExileEnchantmentEffect copy() { + return new DebtToTheKamiExileEnchantmentEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getFirstTarget()); + if (player == null) { + return false; + } + Target target = new TargetControlledPermanent(filter); + target.setNotTarget(true); + player.choose(outcome, target, source.getSourceId(), game); + Permanent permanent = game.getPermanent(target.getFirstTarget()); + if (permanent == null) { + return false; + } + return player.moveCards(permanent, Zone.EXILED, source, game); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DecimatorBeetle.java b/Mage.Sets/src/mage/cards/d/DecimatorBeetle.java index c63be26b758..95ef4cd3486 100644 --- a/Mage.Sets/src/mage/cards/d/DecimatorBeetle.java +++ b/Mage.Sets/src/mage/cards/d/DecimatorBeetle.java @@ -1,4 +1,3 @@ - package mage.cards.d; import mage.MageInt; @@ -89,7 +88,7 @@ class DecimatorBeetleEffect extends OneShotEffect { targetCreature = game.getPermanent(source.getTargets().get(1).getFirstTarget()); if (targetCreature != null) { Effect effect = new AddCountersTargetEffect(CounterType.M1M1.createInstance(1)); - effect.setTargetPointer(new FixedTarget(source.getTargets().get(1).getFirstTarget())); + effect.setTargetPointer(new FixedTarget(source.getTargets().get(1).getFirstTarget(), game)); effect.apply(game, source); } diff --git a/Mage.Sets/src/mage/cards/d/DecisiveDenial.java b/Mage.Sets/src/mage/cards/d/DecisiveDenial.java index c57bed8a33f..d6957b31e90 100644 --- a/Mage.Sets/src/mage/cards/d/DecisiveDenial.java +++ b/Mage.Sets/src/mage/cards/d/DecisiveDenial.java @@ -25,7 +25,8 @@ public final class DecisiveDenial extends CardImpl { // Choose one — // • Target creature you control fights target creature you don't control. this.getSpellAbility().addEffect(new FightTargetsEffect() - .setText("target creature you control fights target creature you don't control")); + .setText("target creature you control fights target creature you don't control. " + + "(Each deals damage equal to its power to the other.)")); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); diff --git a/Mage.Sets/src/mage/cards/d/DecoctionModule.java b/Mage.Sets/src/mage/cards/d/DecoctionModule.java index 21a7694f3b9..cf47d50073a 100644 --- a/Mage.Sets/src/mage/cards/d/DecoctionModule.java +++ b/Mage.Sets/src/mage/cards/d/DecoctionModule.java @@ -29,7 +29,7 @@ public final class DecoctionModule extends CardImpl { this.addAbility(new EntersBattlefieldControlledTriggeredAbility( Zone.BATTLEFIELD, new GetEnergyCountersControllerEffect(1), - StaticFilters.FILTER_PERMANENT_CREATURE_A, + StaticFilters.FILTER_PERMANENT_A_CREATURE, false) ); diff --git a/Mage.Sets/src/mage/cards/d/DeepwaterHypnotist.java b/Mage.Sets/src/mage/cards/d/DeepwaterHypnotist.java index 12183901a7b..35021e43a5d 100644 --- a/Mage.Sets/src/mage/cards/d/DeepwaterHypnotist.java +++ b/Mage.Sets/src/mage/cards/d/DeepwaterHypnotist.java @@ -11,8 +11,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; /** @@ -20,13 +19,6 @@ import mage.target.common.TargetCreaturePermanent; * @author LevelX2 */ public final class DeepwaterHypnotist extends CardImpl { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public DeepwaterHypnotist(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}"); @@ -38,7 +30,7 @@ public final class DeepwaterHypnotist extends CardImpl { // Inspired — Whenever Deepwater Hypnotist becomes untapped, target creature an opponent controls gets -3/-0 until end of turn. Ability ability = new InspiredAbility(new BoostTargetEffect(-3,0,Duration.EndOfTurn)); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/d/DeftDismissal.java b/Mage.Sets/src/mage/cards/d/DeftDismissal.java index 0bb2c837786..53a76683cc4 100644 --- a/Mage.Sets/src/mage/cards/d/DeftDismissal.java +++ b/Mage.Sets/src/mage/cards/d/DeftDismissal.java @@ -1,13 +1,11 @@ - package mage.cards.d; import java.util.UUID; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.DamageMultiEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterAttackingOrBlockingCreature; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanentAmount; /** @@ -20,10 +18,8 @@ public final class DeftDismissal extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{W}"); // Deft Dismissal deals 3 damage divided as you choose among one, two, or three target attacking or blocking creatures. - Effect effect = new DamageMultiEffect(3); - effect.setText("{this} deals 3 damage divided as you choose among one, two, or three target attacking or blocking creatures"); - this.getSpellAbility().addEffect(effect); - this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount(3, new FilterAttackingOrBlockingCreature("attacking or blocking creatures"))); + this.getSpellAbility().addEffect(new DamageMultiEffect(3)); + this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount(3, StaticFilters.FILTER_ATTACKING_OR_BLOCKING_CREATURES)); } private DeftDismissal(final DeftDismissal card) { diff --git a/Mage.Sets/src/mage/cards/d/DelverOfSecrets.java b/Mage.Sets/src/mage/cards/d/DelverOfSecrets.java index b0c282c70dc..a02bc453eaf 100644 --- a/Mage.Sets/src/mage/cards/d/DelverOfSecrets.java +++ b/Mage.Sets/src/mage/cards/d/DelverOfSecrets.java @@ -33,7 +33,6 @@ public final class DelverOfSecrets extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); - this.transformable = true; this.secondSideCardClazz = mage.cards.i.InsectileAberration.class; // At the beginning of your upkeep, look at the top card of your library. You may reveal that card. If an instant or sorcery card is revealed this way, transform Delver of Secrets. @@ -73,25 +72,25 @@ class DelverOfSecretsEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); - if (player != null && sourcePermanent != null) { - if (player.getLibrary().hasCards()) { - Card card = player.getLibrary().getFromTop(game); - if(card == null){ - return false; - } - Cards cards = new CardsImpl(); - cards.add(card); - player.lookAtCards(sourcePermanent.getName(), cards, game); - if (player.chooseUse(Outcome.DrawCard, "Reveal the top card of your library?", source, game)) { - player.revealCards(sourcePermanent.getName(), cards, game); - if (filter.match(card, game)) { - return new TransformSourceEffect(true, true).apply(game, source); - } - } - - } - return true; + if (player == null || sourcePermanent == null) { + return false; } - return false; + if (player.getLibrary().hasCards()) { + Card card = player.getLibrary().getFromTop(game); + if(card == null){ + return false; + } + Cards cards = new CardsImpl(); + cards.add(card); + player.lookAtCards(sourcePermanent.getName(), cards, game); + if (player.chooseUse(Outcome.DrawCard, "Reveal the top card of your library?", source, game)) { + player.revealCards(sourcePermanent.getName(), cards, game); + if (filter.match(card, game)) { + return new TransformSourceEffect().apply(game, source); + } + } + + } + return true; } } diff --git a/Mage.Sets/src/mage/cards/d/Demilich.java b/Mage.Sets/src/mage/cards/d/Demilich.java index da6ff4323ec..1a0aa25a236 100644 --- a/Mage.Sets/src/mage/cards/d/Demilich.java +++ b/Mage.Sets/src/mage/cards/d/Demilich.java @@ -76,7 +76,7 @@ enum DemilichValue implements DynamicValue { SpellsCastWatcher watcher = game.getState().getWatcher(SpellsCastWatcher.class); if (watcher != null) { for (Spell spell : watcher.getSpellsCastThisTurn(sourceAbility.getControllerId())) { - if (spell.isInstantOrSorcery()) { + if (spell.isInstantOrSorcery(game)) { spells++; } } diff --git a/Mage.Sets/src/mage/cards/d/DemonPossessedWitch.java b/Mage.Sets/src/mage/cards/d/DemonPossessedWitch.java index e87dde88ec8..ddfbb76f007 100644 --- a/Mage.Sets/src/mage/cards/d/DemonPossessedWitch.java +++ b/Mage.Sets/src/mage/cards/d/DemonPossessedWitch.java @@ -1,32 +1,25 @@ package mage.cards.d; -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.DestroyTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.target.Target; -import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; /** - * * @author fireshoes */ public final class DemonPossessedWitch extends CardImpl { - private static final String rule = "When this creature transforms into Demon-Possessed Witch, you may destroy target creature"; - public DemonPossessedWitch(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},""); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.SHAMAN); this.power = new MageInt(4); @@ -37,7 +30,9 @@ public final class DemonPossessedWitch extends CardImpl { this.nightCard = true; // When this creature transforms into Demon-Possessed Witch, you may destroy target creature. - this.addAbility(new DemonPossessedWitchAbility()); + Ability ability = new TransformIntoSourceTriggeredAbility(new DestroyTargetEffect(), true); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); } private DemonPossessedWitch(final DemonPossessedWitch card) { @@ -49,42 +44,3 @@ public final class DemonPossessedWitch extends CardImpl { return new DemonPossessedWitch(this); } } - -class DemonPossessedWitchAbility extends TriggeredAbilityImpl { - - public DemonPossessedWitchAbility() { - super(Zone.BATTLEFIELD, new DestroyTargetEffect(), true); - Target target = new TargetPermanent(new FilterCreaturePermanent()); - this.addTarget(target); - } - - public DemonPossessedWitchAbility(final DemonPossessedWitchAbility ability) { - super(ability); - } - - @Override - public DemonPossessedWitchAbility copy() { - return new DemonPossessedWitchAbility(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 "When this creature transforms into Demon-Possessed Witch, you may destroy target creature."; - } -} diff --git a/Mage.Sets/src/mage/cards/d/DemonicAppetite.java b/Mage.Sets/src/mage/cards/d/DemonicAppetite.java index 40578f7a562..28c5a77782d 100644 --- a/Mage.Sets/src/mage/cards/d/DemonicAppetite.java +++ b/Mage.Sets/src/mage/cards/d/DemonicAppetite.java @@ -7,13 +7,12 @@ import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.SacrificeControllerEffect; -import mage.abilities.effects.common.SacrificeTargetEffect; import mage.abilities.effects.common.continuous.BoostEnchantedEffect; import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.TargetPermanent; import mage.target.common.TargetControlledCreaturePermanent; @@ -38,7 +37,7 @@ public final class DemonicAppetite extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(3, 3, Duration.WhileOnBattlefield))); // At the beginning of your upkeep, sacrifice a creature. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(new SacrificeControllerEffect(new FilterCreaturePermanent("a creature"), 1, ""), + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new SacrificeControllerEffect(StaticFilters.FILTER_PERMANENT_A_CREATURE, 1, ""), TargetController.YOU, false)); } diff --git a/Mage.Sets/src/mage/cards/d/DemonicBargain.java b/Mage.Sets/src/mage/cards/d/DemonicBargain.java new file mode 100644 index 00000000000..bcce2af2ed9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DemonicBargain.java @@ -0,0 +1,72 @@ +package mage.cards.d; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInLibrary; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DemonicBargain extends CardImpl { + + public DemonicBargain(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}"); + + // Exile the top thirteen cards of your library, then search your library for a card. Put that card into your hand, then shuffle. + this.getSpellAbility().addEffect(new DemonicBargainEffect()); + } + + private DemonicBargain(final DemonicBargain card) { + super(card); + } + + @Override + public DemonicBargain copy() { + return new DemonicBargain(this); + } +} + +class DemonicBargainEffect extends OneShotEffect { + + DemonicBargainEffect() { + super(Outcome.Benefit); + staticText = "exile the top thirteen cards of your library, " + + "then search your library for a card. Put that card into your hand, then shuffle"; + } + + private DemonicBargainEffect(final DemonicBargainEffect effect) { + super(effect); + } + + @Override + public DemonicBargainEffect copy() { + return new DemonicBargainEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + player.moveCards(player.getLibrary().getTopCards(game, 13), Zone.EXILED, source, game); + TargetCardInLibrary target = new TargetCardInLibrary(); + player.searchLibrary(target, source, game); + Card card = player.getLibrary().getCard(target.getFirstTarget(), game); + if (card != null) { + player.moveCards(card, Zone.HAND, source, game); + } + player.shuffleLibrary(source, game); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/d/DemonicTutor.java b/Mage.Sets/src/mage/cards/d/DemonicTutor.java index d88597d4595..512e9d0f12e 100644 --- a/Mage.Sets/src/mage/cards/d/DemonicTutor.java +++ b/Mage.Sets/src/mage/cards/d/DemonicTutor.java @@ -1,27 +1,23 @@ - package mage.cards.d; -import java.util.UUID; import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.target.common.TargetCardInLibrary; -/** - * - * @author KholdFuzion +import java.util.UUID; +/** + * @author KholdFuzion */ public final class DemonicTutor extends CardImpl { public DemonicTutor(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{B}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}"); // Search your library for a card and put that card into your hand. Then shuffle your library. - TargetCardInLibrary target = new TargetCardInLibrary(); - this.getSpellAbility().addEffect(new SearchLibraryPutInHandEffect(target)); + this.getSpellAbility().addEffect(new SearchLibraryPutInHandEffect(new TargetCardInLibrary())); } private DemonicTutor(final DemonicTutor card) { diff --git a/Mage.Sets/src/mage/cards/d/DennickPiousApprentice.java b/Mage.Sets/src/mage/cards/d/DennickPiousApprentice.java index 20107974ca3..086bad1d31f 100644 --- a/Mage.Sets/src/mage/cards/d/DennickPiousApprentice.java +++ b/Mage.Sets/src/mage/cards/d/DennickPiousApprentice.java @@ -6,7 +6,6 @@ import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.CantBeTargetedCardsGraveyardsEffect; import mage.abilities.keyword.DisturbAbility; import mage.abilities.keyword.LifelinkAbility; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -29,7 +28,6 @@ public final class DennickPiousApprentice extends CardImpl { this.subtype.add(SubType.SOLDIER); this.power = new MageInt(2); this.toughness = new MageInt(3); - this.transformable = true; this.secondSideCardClazz = mage.cards.d.DennickPiousApparition.class; // Lifelink @@ -39,8 +37,7 @@ public final class DennickPiousApprentice extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantBeTargetedCardsGraveyardsEffect())); // Disturb {2}{W}{U} - this.addAbility(new TransformAbility()); - this.addAbility(new DisturbAbility(new ManaCostsImpl<>("{2}{W}{U}"))); + this.addAbility(new DisturbAbility(this, "{2}{W}{U}")); } diff --git a/Mage.Sets/src/mage/cards/d/DepartedSoulkeeper.java b/Mage.Sets/src/mage/cards/d/DepartedSoulkeeper.java index 91e19c92f0f..c937f9d2991 100644 --- a/Mage.Sets/src/mage/cards/d/DepartedSoulkeeper.java +++ b/Mage.Sets/src/mage/cards/d/DepartedSoulkeeper.java @@ -25,7 +25,6 @@ public final class DepartedSoulkeeper extends CardImpl { this.toughness = new MageInt(1); this.color.setWhite(true); this.color.setBlue(true); - this.transformable = true; this.nightCard = true; // Flying diff --git a/Mage.Sets/src/mage/cards/d/DepravedHarvester.java b/Mage.Sets/src/mage/cards/d/DepravedHarvester.java new file mode 100644 index 00000000000..8a385930f39 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DepravedHarvester.java @@ -0,0 +1,39 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DepravedHarvester extends CardImpl { + + public DepravedHarvester(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.KNIGHT); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + this.color.setBlack(true); + this.nightCard = true; + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + } + + private DepravedHarvester(final DepravedHarvester card) { + super(card); + } + + @Override + public DepravedHarvester copy() { + return new DepravedHarvester(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/Dermoplasm.java b/Mage.Sets/src/mage/cards/d/Dermoplasm.java index 496bc073603..465970fec0c 100644 --- a/Mage.Sets/src/mage/cards/d/Dermoplasm.java +++ b/Mage.Sets/src/mage/cards/d/Dermoplasm.java @@ -1,4 +1,3 @@ - package mage.cards.d; import java.util.UUID; @@ -81,7 +80,7 @@ class DermoplasmEffect extends OneShotEffect { if (effect.apply(game, source)) { if (thisCreature != null) { effect = new ReturnToHandTargetEffect(); - effect.setTargetPointer(new FixedTarget(thisCreature.getId())); + effect.setTargetPointer(new FixedTarget(thisCreature.getId(), game)); effect.apply(game, source); return true; } diff --git a/Mage.Sets/src/mage/cards/d/Dermotaxi.java b/Mage.Sets/src/mage/cards/d/Dermotaxi.java index b2448f0b5f1..dc1940d6325 100644 --- a/Mage.Sets/src/mage/cards/d/Dermotaxi.java +++ b/Mage.Sets/src/mage/cards/d/Dermotaxi.java @@ -6,36 +6,28 @@ import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapTargetCost; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.CopyEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.StaticFilters; -import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.TappedPredicate; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.game.permanent.PermanentCard; import mage.players.Player; import mage.target.common.TargetCardInGraveyard; import mage.target.common.TargetControlledPermanent; import java.util.UUID; +import mage.MageObject; +import mage.abilities.effects.common.CopyEffect; +import mage.game.permanent.PermanentCard; +import mage.util.functions.CopyApplier; /** * @author TheElk801 */ public final class Dermotaxi extends CardImpl { - private static final FilterControlledPermanent filter - = new FilterControlledCreaturePermanent("untapped creatures you control"); - - static { - filter.add(TappedPredicate.UNTAPPED); - } - public Dermotaxi(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); @@ -45,13 +37,16 @@ public final class Dermotaxi extends CardImpl { // Imprint — As Dermotaxi enters the battlefield, exile a creature card from a graveyard. this.addAbility(new EntersBattlefieldAbility( - new DermotaxiImprintEffect(), null, "Imprint — As {this} " + - "enters the battlefield, exile a creature card from a graveyard.", "" + new DermotaxiImprintEffect(), null, "Imprint — As {this} " + + "enters the battlefield, exile a creature card from a graveyard.", "" )); // Tap two untapped creatures you control: Until end of turn, Dermotaxi becomes a copy of the imprinted card, except it's a Vehicle artifact in addition to its other types. this.addAbility(new SimpleActivatedAbility( - new DermotaxiCopyEffect(), new TapTargetCost(new TargetControlledPermanent(2, filter)) + new DermotaxiCopyEffect(), + new TapTargetCost(new TargetControlledPermanent( + 2, StaticFilters.FILTER_CONTROLLED_UNTAPPED_CREATURES + )) )); } @@ -108,8 +103,8 @@ class DermotaxiCopyEffect extends OneShotEffect { DermotaxiCopyEffect() { super(Outcome.Benefit); - staticText = "until end of turn, {this} becomes a copy of the exiled card, " + - "except it's a Vehicle artifact in addition to its other types"; + staticText = "until end of turn, {this} becomes a copy of the exiled card, " + + "except it's a Vehicle artifact in addition to its other types"; } private DermotaxiCopyEffect(final DermotaxiCopyEffect effect) { @@ -127,19 +122,28 @@ class DermotaxiCopyEffect extends OneShotEffect { if (sourcePermanent == null) { return false; } - Card card = game.getPermanent(sourcePermanent.getImprinted().get(0)); + Card card = game.getCard(sourcePermanent.getImprinted().get(0)); if (card == null) { return false; } Permanent newBluePrint = new PermanentCard(card, source.getControllerId(), game); newBluePrint.assignNewId(); - newBluePrint.addCardType(CardType.ARTIFACT); - newBluePrint.addSubType(SubType.VEHICLE); + DermotaxiCopyApplier applier = new DermotaxiCopyApplier(); + applier.apply(game, newBluePrint, source, sourcePermanent.getId()); CopyEffect copyEffect = new CopyEffect(Duration.EndOfTurn, newBluePrint, sourcePermanent.getId()); copyEffect.newId(); - Ability newAbility = source.copy(); - copyEffect.init(newAbility, game); - game.addEffect(copyEffect, newAbility); + copyEffect.setApplier(applier); + game.addEffect(copyEffect, source); + return true; + } +} + +class DermotaxiCopyApplier extends CopyApplier { + + @Override + public boolean apply(Game game, MageObject blueprint, Ability source, UUID copyToObjectId) { + blueprint.addCardType(CardType.ARTIFACT); + blueprint.addSubType(SubType.VEHICLE); return true; } } diff --git a/Mage.Sets/src/mage/cards/d/DesperateFarmer.java b/Mage.Sets/src/mage/cards/d/DesperateFarmer.java new file mode 100644 index 00000000000..a0aae4476de --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DesperateFarmer.java @@ -0,0 +1,49 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.common.DiesCreatureTriggeredAbility; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.keyword.LifelinkAbility; +import mage.abilities.keyword.TransformAbility; +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 DesperateFarmer extends CardImpl { + + public DesperateFarmer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.PEASANT); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + this.secondSideCardClazz = mage.cards.d.DepravedHarvester.class; + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // When another creature you control dies, transform Desperate Farmer. + this.addAbility(new TransformAbility()); + this.addAbility(new DiesCreatureTriggeredAbility( + new TransformSourceEffect(), false, + StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE + ).setTriggerPhrase("When another creature you control dies, ")); + } + + private DesperateFarmer(final DesperateFarmer card) { + super(card); + } + + @Override + public DesperateFarmer copy() { + return new DesperateFarmer(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DespoilerOfSouls.java b/Mage.Sets/src/mage/cards/d/DespoilerOfSouls.java index 523cf9ba436..4e2067a33df 100644 --- a/Mage.Sets/src/mage/cards/d/DespoilerOfSouls.java +++ b/Mage.Sets/src/mage/cards/d/DespoilerOfSouls.java @@ -1,7 +1,5 @@ - package mage.cards.d; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.CantBlockAbility; @@ -16,29 +14,37 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardIdPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCardInYourGraveyard; +import java.util.UUID; + /** - * * @author fireshoes */ public final class DespoilerOfSouls extends CardImpl { - + + private static final FilterCard filter = new FilterCreatureCard("two other creature cards from your graveyard"); + + static { + filter.add(AnotherPredicate.instance); + } + public DespoilerOfSouls(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}{B}"); this.subtype.add(SubType.HORROR); this.power = new MageInt(3); this.toughness = new MageInt(1); // Despoiler of Souls can't block. this.addAbility(new CantBlockAbility()); - + // {B}{B}, Exile two other creature cards from your graveyard: Return Despoiler of Souls from your graveyard to the battlefield. - FilterCard filter = new FilterCreatureCard("two other creature cards from your graveyard"); - filter.add(Predicates.not(new CardIdPredicate(this.getId()))); - Ability ability = new SimpleActivatedAbility(Zone.GRAVEYARD, new ReturnSourceFromGraveyardToBattlefieldEffect(false), new ManaCostsImpl("{B}{B}")); + Ability ability = new SimpleActivatedAbility( + Zone.GRAVEYARD, + new ReturnSourceFromGraveyardToBattlefieldEffect(false), + new ManaCostsImpl<>("{B}{B}") + ); ability.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard(2, filter))); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/d/DestinySpinner.java b/Mage.Sets/src/mage/cards/d/DestinySpinner.java index c3feab1f982..b6097bff207 100644 --- a/Mage.Sets/src/mage/cards/d/DestinySpinner.java +++ b/Mage.Sets/src/mage/cards/d/DestinySpinner.java @@ -76,7 +76,7 @@ enum DestinySpinnerCount implements DynamicValue { @Override public int calculate(Game game, Ability sourceAbility, Effect effect) { - return game.getBattlefield().countAll(StaticFilters.FILTER_ENCHANTMENT_PERMANENT, sourceAbility.getControllerId(), game); + return game.getBattlefield().countAll(StaticFilters.FILTER_PERMANENT_ENCHANTMENT, sourceAbility.getControllerId(), game); } @Override diff --git a/Mage.Sets/src/mage/cards/d/DevotedGrafkeeper.java b/Mage.Sets/src/mage/cards/d/DevotedGrafkeeper.java index 667d86340c4..b54e5ad7092 100644 --- a/Mage.Sets/src/mage/cards/d/DevotedGrafkeeper.java +++ b/Mage.Sets/src/mage/cards/d/DevotedGrafkeeper.java @@ -7,7 +7,6 @@ import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.MillCardsControllerEffect; import mage.abilities.effects.common.TapTargetEffect; import mage.abilities.keyword.DisturbAbility; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -32,7 +31,6 @@ public final class DevotedGrafkeeper extends CardImpl { this.subtype.add(SubType.PEASANT); this.power = new MageInt(2); this.toughness = new MageInt(1); - this.transformable = true; this.secondSideCardClazz = mage.cards.d.DepartedSoulkeeper.class; // When Devoted Grafkeeper enters the battlefield, mill two cards. @@ -42,8 +40,7 @@ public final class DevotedGrafkeeper extends CardImpl { this.addAbility(new DevotedGrafkeeperTriggeredAbility()); // Disturb {1}{W}{U} - this.addAbility(new TransformAbility()); - this.addAbility(new DisturbAbility(new ManaCostsImpl<>("{1}{W}{U}"))); + this.addAbility(new DisturbAbility(this, "{1}{W}{U}")); } private DevotedGrafkeeper(final DevotedGrafkeeper card) { diff --git a/Mage.Sets/src/mage/cards/d/DevouringRage.java b/Mage.Sets/src/mage/cards/d/DevouringRage.java index 5cb41d13bed..787fe5644f8 100644 --- a/Mage.Sets/src/mage/cards/d/DevouringRage.java +++ b/Mage.Sets/src/mage/cards/d/DevouringRage.java @@ -85,7 +85,7 @@ class DevouringRageEffect extends OneShotEffect { Permanent targetCreature = game.getPermanent(getTargetPointer().getFirst(game, source)); if (targetCreature != null) { ContinuousEffect effect = new BoostTargetEffect(amount, 0, Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(targetCreature.getId())); + effect.setTargetPointer(new FixedTarget(targetCreature.getId(), game)); game.addEffect(effect, source); return true; } diff --git a/Mage.Sets/src/mage/cards/d/DhalsimPliablePacifist.java b/Mage.Sets/src/mage/cards/d/DhalsimPliablePacifist.java new file mode 100644 index 00000000000..dd864834dda --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DhalsimPliablePacifist.java @@ -0,0 +1,198 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.AttacksCreatureYouControlTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.InvertCondition; +import mage.abilities.condition.common.SourceAttackingCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.RestrictionEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.keyword.HexproofAbility; +import mage.abilities.keyword.ReachAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.game.Game; +import mage.game.events.DamagedPlayerEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DhalsimPliablePacifist extends CardImpl { + + private static final Condition condition = new InvertCondition(SourceAttackingCondition.instance); + private static final FilterControlledCreaturePermanent filter + = new FilterControlledCreaturePermanent("a creature you control with reach"); + + static { + filter.add(new AbilityPredicate(ReachAbility.class)); + } + + public DhalsimPliablePacifist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.MONK); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // Reach + this.addAbility(ReachAbility.getInstance()); + + // Teleport—Dhalsim, Pliable Pacifist has hexproof unless he's attacking. + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new GainAbilitySourceEffect(HexproofAbility.getInstance()), + condition, "{this} has hexproof unless he's attacking" + )).withFlavorWord("Teleport")); + + // Whenever a creature you control with reach attacks, untap it and it can't be blocked by creatures with greater power this combat. + this.addAbility(new AttacksCreatureYouControlTriggeredAbility( + new DhalsimPliablePacifistEffect(), false, filter, true + )); + + // Fierce Punch—Whenever one or more creatures you control deal combat damage to a player, draw a card. + this.addAbility(new DhalsimPliablePacifistTriggeredAbility()); + } + + private DhalsimPliablePacifist(final DhalsimPliablePacifist card) { + super(card); + } + + @Override + public DhalsimPliablePacifist copy() { + return new DhalsimPliablePacifist(this); + } +} + +class DhalsimPliablePacifistEffect extends OneShotEffect { + + DhalsimPliablePacifistEffect() { + super(Outcome.Benefit); + staticText = "untap it and it can't be blocked by creatures with greater power this combat"; + } + + private DhalsimPliablePacifistEffect(final DhalsimPliablePacifistEffect effect) { + super(effect); + } + + @Override + public DhalsimPliablePacifistEffect copy() { + return new DhalsimPliablePacifistEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (permanent == null) { + return false; + } + permanent.untap(game); + game.addEffect(new DhalsimPliablePacifistBlockEffect(permanent, game), source); + return true; + } +} + +class DhalsimPliablePacifistBlockEffect extends RestrictionEffect { + + private final MageObjectReference mor; + + DhalsimPliablePacifistBlockEffect(Permanent permanent, Game game) { + super(Duration.EndOfTurn); + this.mor = new MageObjectReference(permanent, game); + } + + private DhalsimPliablePacifistBlockEffect(final DhalsimPliablePacifistBlockEffect effect) { + super(effect); + this.mor = effect.mor; + } + + @Override + public DhalsimPliablePacifistBlockEffect copy() { + return new DhalsimPliablePacifistBlockEffect(this); + } + + @Override + public boolean canBeBlocked(Permanent attacker, Permanent blocker, Ability source, Game game, boolean canUseChooseDialogs) { + return false; + } + + @Override + public boolean applies(Permanent permanent, Ability source, Game game) { + Permanent attacker = mor.getPermanent(game); + if (attacker == null) { + discard(); + return false; + } + return permanent.getPower().getValue() > attacker.getPower().getValue(); + } +} + +class DhalsimPliablePacifistTriggeredAbility extends TriggeredAbilityImpl { + + private final Set damagedPlayerIds = new HashSet<>(); + + DhalsimPliablePacifistTriggeredAbility() { + super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), false); + this.withFlavorWord("Fierce Punch"); + } + + private DhalsimPliablePacifistTriggeredAbility(final DhalsimPliablePacifistTriggeredAbility ability) { + super(ability); + } + + @Override + public DhalsimPliablePacifistTriggeredAbility copy() { + return new DhalsimPliablePacifistTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DAMAGED_PLAYER + || event.getType() == GameEvent.EventType.COMBAT_DAMAGE_STEP_PRIORITY + || event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.COMBAT_DAMAGE_STEP_PRIORITY || + (event.getType() == GameEvent.EventType.ZONE_CHANGE && event.getTargetId().equals(getSourceId()))) { + damagedPlayerIds.clear(); + return false; + } + if (event.getType() != GameEvent.EventType.DAMAGED_PLAYER) { + return false; + } + DamagedPlayerEvent damageEvent = (DamagedPlayerEvent) event; + Permanent permanent = game.getPermanent(event.getSourceId()); + if (!damageEvent.isCombatDamage() + || permanent == null + || !permanent.isControlledBy(this.getControllerId()) + || !permanent.isCreature(game) || + damagedPlayerIds.contains(event.getPlayerId())) { + return false; + } + damagedPlayerIds.add(event.getPlayerId()); + return true; + } + + @Override + public String getTriggerPhrase() { + return "Whenever one or more creatures you control deal combat damage to a player, "; + } +} diff --git a/Mage.Sets/src/mage/cards/d/DiabolicServitude.java b/Mage.Sets/src/mage/cards/d/DiabolicServitude.java index 4d63f0a25c1..64b35724b99 100644 --- a/Mage.Sets/src/mage/cards/d/DiabolicServitude.java +++ b/Mage.Sets/src/mage/cards/d/DiabolicServitude.java @@ -1,4 +1,3 @@ - package mage.cards.d; import java.util.UUID; @@ -21,7 +20,6 @@ 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.events.ZoneChangeEvent; import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetpointer.FixedTarget; @@ -148,7 +146,7 @@ class DiabolicServitudeExileCreatureEffect extends OneShotEffect { Object object = game.getState().getValue(source.getSourceId().toString() + "returnedCreature"); if ((object instanceof MageObjectReference)) { Effect effect = new ExileTargetEffect(); - effect.setTargetPointer(new FixedTarget(((MageObjectReference) object).getSourceId())); + effect.setTargetPointer(new FixedTarget(((MageObjectReference) object).getSourceId(), game)); effect.apply(game, source); return new ReturnToHandSourceEffect(true).apply(game, source); } @@ -177,7 +175,7 @@ class DiabolicServitudeSourceLeftBattlefieldEffect extends OneShotEffect { Object object = game.getState().getValue(source.getSourceId().toString() + "returnedCreature"); if ((object instanceof MageObjectReference)) { Effect effect = new ExileTargetEffect(null, "", Zone.BATTLEFIELD); - effect.setTargetPointer(new FixedTarget(((MageObjectReference) object).getSourceId())); + effect.setTargetPointer(new FixedTarget(((MageObjectReference) object).getSourceId(), game)); effect.apply(game, source); } return false; diff --git a/Mage.Sets/src/mage/cards/d/DictateOfErebos.java b/Mage.Sets/src/mage/cards/d/DictateOfErebos.java index 85d7a8055c0..f84c3750ea6 100644 --- a/Mage.Sets/src/mage/cards/d/DictateOfErebos.java +++ b/Mage.Sets/src/mage/cards/d/DictateOfErebos.java @@ -9,6 +9,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.TargetController; +import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreaturePermanent; @@ -18,19 +19,13 @@ import mage.filter.common.FilterCreaturePermanent; */ public final class DictateOfErebos extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature you control"); - - static { - filter.add(TargetController.YOU.getControllerPredicate()); - } - public DictateOfErebos(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{B}{B}"); // Flash this.addAbility(FlashAbility.getInstance()); // Whenever a creature you control dies, each opponent sacrifices a creature. - this.addAbility(new DiesCreatureTriggeredAbility(new SacrificeOpponentsEffect(new FilterControlledCreaturePermanent("creature")), false, filter)); + this.addAbility(new DiesCreatureTriggeredAbility(new SacrificeOpponentsEffect(new FilterControlledCreaturePermanent("creature")), false, StaticFilters.FILTER_CONTROLLED_A_CREATURE)); } private DictateOfErebos(final DictateOfErebos card) { diff --git a/Mage.Sets/src/mage/cards/d/DigUp.java b/Mage.Sets/src/mage/cards/d/DigUp.java new file mode 100644 index 00000000000..e13037699a0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DigUp.java @@ -0,0 +1,38 @@ +package mage.cards.d; + +import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; +import mage.abilities.keyword.CleaveAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.target.common.TargetCardInLibrary; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DigUp extends CardImpl { + + public DigUp(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{G}"); + + // Cleave {1}{B}{B}{G} + this.addAbility(new CleaveAbility(this, new SearchLibraryPutInHandEffect(new TargetCardInLibrary()), "{1}{B}{B}{G}")); + + // Search your library for a [basic land] card, [reveal it,] put it into your hand, then shuffle. + this.getSpellAbility().addEffect(new SearchLibraryPutInHandEffect( + new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND_A), true + ).setText("search your library for a [basic land] card, [reveal it,] put it into your hand, then shuffle")); + } + + private DigUp(final DigUp card) { + super(card); + } + + @Override + public DigUp copy() { + return new DigUp(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DingusEgg.java b/Mage.Sets/src/mage/cards/d/DingusEgg.java index f84fd826d10..69ddd23545c 100644 --- a/Mage.Sets/src/mage/cards/d/DingusEgg.java +++ b/Mage.Sets/src/mage/cards/d/DingusEgg.java @@ -1,4 +1,3 @@ - package mage.cards.d; import java.util.UUID; @@ -21,7 +20,7 @@ import mage.target.targetpointer.FixedTarget; public final class DingusEgg extends CardImpl { public DingusEgg(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{4}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); // Whenever a land is put into a graveyard from the battlefield, Dingus Egg deals 2 damage to that land's controller. this.addAbility(new DingusEggTriggeredAbility()); @@ -58,9 +57,9 @@ class DingusEggTriggeredAbility extends TriggeredAbilityImpl { if (zEvent.isDiesEvent() && zEvent.getTarget().isLand(game)) { if (getTargets().isEmpty()) { - UUID targetId = zEvent.getTarget().getControllerId(); + UUID targetControllerId = zEvent.getTarget().getControllerId(); for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(targetId)); + effect.setTargetPointer(new FixedTarget(targetControllerId)); } } return true; diff --git a/Mage.Sets/src/mage/cards/d/DinosaurStampede.java b/Mage.Sets/src/mage/cards/d/DinosaurStampede.java index f04097d2ff1..574df6b9e3d 100644 --- a/Mage.Sets/src/mage/cards/d/DinosaurStampede.java +++ b/Mage.Sets/src/mage/cards/d/DinosaurStampede.java @@ -1,8 +1,6 @@ - package mage.cards.d; import java.util.UUID; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.BoostAllEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.abilities.keyword.TrampleAbility; @@ -11,8 +9,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.TargetController; -import mage.filter.common.FilterAttackingCreature; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; /** @@ -21,22 +18,14 @@ import mage.filter.common.FilterCreaturePermanent; */ public final class DinosaurStampede extends CardImpl { - private static final FilterAttackingCreature filter = new FilterAttackingCreature("Attacking creatures"); - private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent("Dinosaurs you control"); - - static { - filter2.add(SubType.DINOSAUR.getPredicate()); - filter2.add(TargetController.YOU.getControllerPredicate()); - } + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(SubType.DINOSAUR, "Dinosaurs"); public DinosaurStampede(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{R}"); // Attacking creatures get +2/+0 until end of turn. Dinosaurs you control gain trample until end of turn. - this.getSpellAbility().addEffect(new BoostAllEffect(2, 0, Duration.EndOfTurn, filter, false)); - Effect effect = new GainAbilityControlledEffect(TrampleAbility.getInstance(), Duration.EndOfTurn, filter2); - effect.setText("Dinosaurs you control gain trample until end of turn."); - this.getSpellAbility().addEffect(effect); + this.getSpellAbility().addEffect(new BoostAllEffect(2, 0, Duration.EndOfTurn, StaticFilters.FILTER_ATTACKING_CREATURES, false)); + this.getSpellAbility().addEffect(new GainAbilityControlledEffect(TrampleAbility.getInstance(), Duration.EndOfTurn, filter)); } private DinosaurStampede(final DinosaurStampede card) { diff --git a/Mage.Sets/src/mage/cards/d/DireFleetCaptain.java b/Mage.Sets/src/mage/cards/d/DireFleetCaptain.java index 32f5a34f74d..0bca5697087 100644 --- a/Mage.Sets/src/mage/cards/d/DireFleetCaptain.java +++ b/Mage.Sets/src/mage/cards/d/DireFleetCaptain.java @@ -1,9 +1,9 @@ - package mage.cards.d; 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.effects.common.continuous.BoostSourceEffect; import mage.cards.CardImpl; @@ -27,6 +27,8 @@ public final class DireFleetCaptain extends CardImpl { filter.add(AnotherPredicate.instance); } + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter); + public DireFleetCaptain(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}{R}"); @@ -36,9 +38,7 @@ public final class DireFleetCaptain extends CardImpl { this.toughness = new MageInt(2); // Whenever Dire Fleet Captain attacks, it gets +1/+1 until end of turn for each other attacking Pirate. - PermanentsOnBattlefieldCount value = new PermanentsOnBattlefieldCount(filter); - this.addAbility(new AttacksTriggeredAbility(new BoostSourceEffect(value, value, Duration.EndOfTurn, true) - .setText("it gets +1/+1 until end of turn for each other attacking Pirate"), false)); + this.addAbility(new AttacksTriggeredAbility(new BoostSourceEffect(xValue, xValue, Duration.EndOfTurn, true, "it"), false)); } private DireFleetCaptain(final DireFleetCaptain card) { diff --git a/Mage.Sets/src/mage/cards/d/DireFleetInterloper.java b/Mage.Sets/src/mage/cards/d/DireFleetInterloper.java index 71ae84264ee..0cd77718f47 100644 --- a/Mage.Sets/src/mage/cards/d/DireFleetInterloper.java +++ b/Mage.Sets/src/mage/cards/d/DireFleetInterloper.java @@ -26,7 +26,7 @@ public final class DireFleetInterloper extends CardImpl { this.toughness = new MageInt(2); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // When Dire Fleet Interloper enters the battlefield, it explores. this.addAbility(new EntersBattlefieldTriggeredAbility(new ExploreSourceEffect())); diff --git a/Mage.Sets/src/mage/cards/d/DireFleetNeckbreaker.java b/Mage.Sets/src/mage/cards/d/DireFleetNeckbreaker.java index 5639fe12367..740d8624084 100644 --- a/Mage.Sets/src/mage/cards/d/DireFleetNeckbreaker.java +++ b/Mage.Sets/src/mage/cards/d/DireFleetNeckbreaker.java @@ -1,15 +1,16 @@ - package mage.cards.d; import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.common.continuous.BoostSourceEffect; -import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; -import mage.constants.*; +import mage.abilities.effects.common.continuous.BoostControlledEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.filter.common.FilterAttackingCreature; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.AttackingPredicate; /** * @@ -17,10 +18,10 @@ import mage.filter.common.FilterAttackingCreature; */ public final class DireFleetNeckbreaker extends CardImpl { - private static final FilterAttackingCreature filterYourAttackingPirates = new FilterAttackingCreature("Attacking Pirates"); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(SubType.PIRATE, "attacking Pirates"); + static { - filterYourAttackingPirates.add(TargetController.YOU.getControllerPredicate()); - filterYourAttackingPirates.add(SubType.PIRATE.getPredicate()); + filter.add(AttackingPredicate.instance); } public DireFleetNeckbreaker(UUID ownerId, CardSetInfo setInfo) { @@ -32,14 +33,7 @@ public final class DireFleetNeckbreaker extends CardImpl { this.toughness = new MageInt(2); // Attacking Pirates you control get +2/+0. - GainAbilityControlledEffect gainEffect = new GainAbilityControlledEffect( - new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostSourceEffect(2, 0, Duration.Custom)), - Duration.WhileOnBattlefield, - filterYourAttackingPirates, - false - ); - gainEffect.setText("Attacking Pirates you control get +2/+0."); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, gainEffect)); + this.addAbility(new SimpleStaticAbility(new BoostControlledEffect(2, 0, Duration.WhileOnBattlefield, filter))); } private DireFleetNeckbreaker(final DireFleetNeckbreaker card) { @@ -50,4 +44,4 @@ public final class DireFleetNeckbreaker extends CardImpl { public DireFleetNeckbreaker copy() { return new DireFleetNeckbreaker(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/d/DireFleetRavager.java b/Mage.Sets/src/mage/cards/d/DireFleetRavager.java index c78b07025b9..fee6aeefc3a 100644 --- a/Mage.Sets/src/mage/cards/d/DireFleetRavager.java +++ b/Mage.Sets/src/mage/cards/d/DireFleetRavager.java @@ -32,7 +32,7 @@ public final class DireFleetRavager extends CardImpl { this.toughness = new MageInt(4); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // Deathtouch this.addAbility(DeathtouchAbility.getInstance()); diff --git a/Mage.Sets/src/mage/cards/d/DireStrainAnarchist.java b/Mage.Sets/src/mage/cards/d/DireStrainAnarchist.java new file mode 100644 index 00000000000..a683736bce5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DireStrainAnarchist.java @@ -0,0 +1,59 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.MenaceAbility; +import mage.abilities.keyword.NightboundAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.target.TargetPlayer; +import mage.target.common.TargetCreaturePermanent; +import mage.target.common.TargetPlaneswalkerPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DireStrainAnarchist extends CardImpl { + + public DireStrainAnarchist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); + + this.subtype.add(SubType.WEREWOLF); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + this.color.setRed(true); + this.nightCard = true; + + // Menace + this.addAbility(new MenaceAbility(false)); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // Whenever Dire-Strain Anarchist attacks, it deals 2 damage to each of up to one target creature, up to one target player, and/or up to one target planeswalker. + Ability ability = new AttacksTriggeredAbility(new DamageTargetEffect(2).setText("it deals 2 damage to each of up to one target creature, up to one target player, and/or up to one target planeswalker")); + ability.addTarget(new TargetCreaturePermanent(0, 1)); + ability.addTarget(new TargetPlayer(0, 1, false)); + ability.addTarget(new TargetPlaneswalkerPermanent(0, 1)); + this.addAbility(ability); + + // Nightbound + this.addAbility(new NightboundAbility()); + } + + private DireStrainAnarchist(final DireStrainAnarchist card) { + super(card); + } + + @Override + public DireStrainAnarchist copy() { + return new DireStrainAnarchist(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DireStrainBrawler.java b/Mage.Sets/src/mage/cards/d/DireStrainBrawler.java index c7116d20e7f..b1ee9f52960 100644 --- a/Mage.Sets/src/mage/cards/d/DireStrainBrawler.java +++ b/Mage.Sets/src/mage/cards/d/DireStrainBrawler.java @@ -22,7 +22,6 @@ public final class DireStrainBrawler extends CardImpl { this.power = new MageInt(6); this.toughness = new MageInt(6); this.color.setGreen(true); - this.transformable = true; this.nightCard = true; // Vigilance diff --git a/Mage.Sets/src/mage/cards/d/DireStrainDemolisher.java b/Mage.Sets/src/mage/cards/d/DireStrainDemolisher.java index 51fcdcc7077..abb57f23514 100644 --- a/Mage.Sets/src/mage/cards/d/DireStrainDemolisher.java +++ b/Mage.Sets/src/mage/cards/d/DireStrainDemolisher.java @@ -23,7 +23,6 @@ public final class DireStrainDemolisher extends CardImpl { this.power = new MageInt(8); this.toughness = new MageInt(7); this.color.setGreen(true); - this.transformable = true; this.nightCard = true; // Ward {3} diff --git a/Mage.Sets/src/mage/cards/d/DiregrafScavenger.java b/Mage.Sets/src/mage/cards/d/DiregrafScavenger.java new file mode 100644 index 00000000000..0e33df1cf24 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DiregrafScavenger.java @@ -0,0 +1,91 @@ +package mage.cards.d; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.abilities.keyword.DeathtouchAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInGraveyard; + +/** + * + * @author weirddan455 + */ +public final class DiregrafScavenger extends CardImpl { + + public DiregrafScavenger(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); + + this.subtype.add(SubType.ZOMBIE); + this.subtype.add(SubType.BEAR); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Deathtouch + this.addAbility(DeathtouchAbility.getInstance()); + + // When Diregraf Scavenger enters the battlefield, exile up to one target card from a graveyard. If a creature card was exiled this way, each opponent loses 2 life and you gain 2 life. + Ability ability = new EntersBattlefieldTriggeredAbility(new DiregrafScavengerEffect()); + ability.addTarget(new TargetCardInGraveyard(0, 1)); + this.addAbility(ability); + } + + private DiregrafScavenger(final DiregrafScavenger card) { + super(card); + } + + @Override + public DiregrafScavenger copy() { + return new DiregrafScavenger(this); + } +} + +class DiregrafScavengerEffect extends OneShotEffect { + + public DiregrafScavengerEffect() { + super(Outcome.Exile); + staticText = "exile up to one target card from a graveyard. If a creature card was exiled this way, each opponent loses 2 life and you gain 2 life"; + } + + private DiregrafScavengerEffect(final DiregrafScavengerEffect effect) { + super(effect); + } + + @Override + public DiregrafScavengerEffect copy() { + return new DiregrafScavengerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Card targetCard = game.getCard(source.getFirstTarget()); + if (controller == null || targetCard == null) { + return false; + } + boolean creature = targetCard.isCreature(game); + if (!controller.moveCards(targetCard, Zone.EXILED, source, game)) { + return false; + } + if (creature) { + for (UUID opponentId : game.getOpponents(source.getControllerId())) { + Player opponent = game.getPlayer(opponentId); + if (opponent != null) { + opponent.loseLife(2, game, source, false); + } + } + controller.gainLife(2, game, source); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/d/DirgeOfDread.java b/Mage.Sets/src/mage/cards/d/DirgeOfDread.java index 4935f12ca35..7e763e2f75c 100644 --- a/Mage.Sets/src/mage/cards/d/DirgeOfDread.java +++ b/Mage.Sets/src/mage/cards/d/DirgeOfDread.java @@ -13,7 +13,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; /** @@ -25,9 +25,8 @@ public final class DirgeOfDread extends CardImpl { public DirgeOfDread(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{B}"); - // All creatures gain fear until end of turn. - this.getSpellAbility().addEffect(new GainAbilityAllEffect(FearAbility.getInstance(), Duration.EndOfTurn, new FilterCreaturePermanent("All creatures"))); + this.getSpellAbility().addEffect(new GainAbilityAllEffect(FearAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_ALL_CREATURES)); // Cycling {1}{B} this.addAbility(new CyclingAbility(new ManaCostsImpl("{1}{B}"))); // When you cycle Dirge of Dread, you may have target creature gain fear until end of turn. diff --git a/Mage.Sets/src/mage/cards/d/DiscipleOfPhenax.java b/Mage.Sets/src/mage/cards/d/DiscipleOfPhenax.java index 75c5cf16586..fa3d079d89e 100644 --- a/Mage.Sets/src/mage/cards/d/DiscipleOfPhenax.java +++ b/Mage.Sets/src/mage/cards/d/DiscipleOfPhenax.java @@ -4,19 +4,14 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.dynamicvalue.common.DevotionCount; -import mage.abilities.effects.OneShotEffect; -import mage.cards.*; +import mage.abilities.effects.common.discard.DiscardCardYouChooseTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.FilterCard; -import mage.game.Game; -import mage.players.Player; -import mage.target.TargetCard; +import mage.constants.TargetController; import mage.target.TargetPlayer; -import java.util.List; import java.util.UUID; /** @@ -34,7 +29,7 @@ public final class DiscipleOfPhenax extends CardImpl { // When Disciple of Phenax enters the battlefield, target player reveals a number of cards // from their hand equal to your devotion to black. You choose one of them. That player discards that card. - Ability ability = new EntersBattlefieldTriggeredAbility(new DiscipleOfPhenaxEffect(), false); + Ability ability = new EntersBattlefieldTriggeredAbility(new DiscardCardYouChooseTargetEffect(TargetController.ANY, DevotionCount.B)); ability.addTarget(new TargetPlayer()); ability.addHint(DevotionCount.B.getHint()); this.addAbility(ability); @@ -50,68 +45,3 @@ public final class DiscipleOfPhenax extends CardImpl { return new DiscipleOfPhenax(this); } } - -class DiscipleOfPhenaxEffect extends OneShotEffect { - - DiscipleOfPhenaxEffect() { - super(Outcome.Discard); - staticText = "target player reveals a number of cards from their hand " - + "equal to your devotion to black. You choose one of " - + "them. That player discards that card"; - } - - private DiscipleOfPhenaxEffect(final DiscipleOfPhenaxEffect effect) { - super(effect); - } - - @Override - public DiscipleOfPhenaxEffect copy() { - return new DiscipleOfPhenaxEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - int devotion = DevotionCount.B.calculate(game, source, this); - Player targetPlayer = game.getPlayer(targetPointer.getFirst(game, source)); - if (devotion <= 0 || targetPlayer == null) { - return false; - } - Cards revealedCards = new CardsImpl(); - int amount = Math.min(targetPlayer.getHand().size(), devotion); - if (targetPlayer.getHand().size() > amount) { - FilterCard filter = new FilterCard("card in target player's hand"); - TargetCard chosenCards = new TargetCard(amount, amount, Zone.HAND, filter); - chosenCards.setNotTarget(true); - if (chosenCards.canChoose(source.getSourceId(), targetPlayer.getId(), game) - && targetPlayer.choose(Outcome.Discard, targetPlayer.getHand(), chosenCards, game)) { - if (!chosenCards.getTargets().isEmpty()) { - List targets = chosenCards.getTargets(); - for (UUID targetid : targets) { - Card card = game.getCard(targetid); - if (card != null) { - revealedCards.add(card); - } - } - } - } - } else { - revealedCards.addAll(targetPlayer.getHand()); - } - if (revealedCards.isEmpty()) { - return true; - } - targetPlayer.revealCards("Disciple of Phenax", revealedCards, game); - Player you = game.getPlayer(source.getControllerId()); - if (you == null) { - return false; - } - TargetCard yourChoice = new TargetCard(Zone.HAND, new FilterCard()); - yourChoice.setNotTarget(true); - if (you.choose(Outcome.Benefit, revealedCards, yourChoice, game)) { - Card card = targetPlayer.getHand().get(yourChoice.getFirstTarget(), game); - return targetPlayer.discard(card, false, source, game); - - } - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/d/DiscipleOfTheRing.java b/Mage.Sets/src/mage/cards/d/DiscipleOfTheRing.java index 7a74b6247d0..62dbb1a2c40 100644 --- a/Mage.Sets/src/mage/cards/d/DiscipleOfTheRing.java +++ b/Mage.Sets/src/mage/cards/d/DiscipleOfTheRing.java @@ -18,10 +18,8 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Zone; -import mage.filter.FilterSpell; +import mage.filter.StaticFilters; import mage.filter.common.FilterInstantOrSorceryCard; -import mage.filter.predicate.Predicates; - import mage.target.TargetSpell; import mage.target.common.TargetCardInYourGraveyard; import mage.target.common.TargetCreaturePermanent; @@ -31,12 +29,6 @@ import mage.target.common.TargetCreaturePermanent; * @author fireshoes */ public final class DiscipleOfTheRing extends CardImpl { - - private static final FilterSpell filterSpell = new FilterSpell("noncreature spell"); - - static { - filterSpell.add(Predicates.not(CardType.CREATURE.getPredicate())); - } public DiscipleOfTheRing(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{U}{U}"); @@ -45,10 +37,11 @@ public final class DiscipleOfTheRing extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(4); - // {1}, Exile an instant or sorcery card from your graveyard: Choose one - Counter target noncreature spell unless its controller pay {2}; + // {1}, Exile an instant or sorcery card from your graveyard: + // Choose one - Counter target noncreature spell unless its controller pay {2}; Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CounterUnlessPaysEffect(new GenericManaCost(2)), new GenericManaCost(1)); ability.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard(1, new FilterInstantOrSorceryCard("an instant or sorcery card from your graveyard")))); - ability.addTarget(new TargetSpell(filterSpell)); + ability.addTarget(new TargetSpell(StaticFilters.FILTER_SPELL_NON_CREATURE)); // or Disciple of the Ring gets +1/+1 until end of turn; Mode mode = new Mode(); diff --git a/Mage.Sets/src/mage/cards/d/Disorder.java b/Mage.Sets/src/mage/cards/d/Disorder.java index 22878b335e6..3c6182bb79e 100644 --- a/Mage.Sets/src/mage/cards/d/Disorder.java +++ b/Mage.Sets/src/mage/cards/d/Disorder.java @@ -8,13 +8,16 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; +import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.game.Controllable; import mage.game.Game; import mage.players.Player; +import java.util.Objects; import java.util.UUID; +import java.util.stream.Collectors; /** * @author LoneFox @@ -47,7 +50,7 @@ public final class Disorder extends CardImpl { class DisorderEffect extends OneShotEffect { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("white creature"); + private static final FilterPermanent filter = new FilterCreaturePermanent("white creature"); static { filter.add(new ColorPredicate(ObjectColor.WHITE)); @@ -69,13 +72,16 @@ class DisorderEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - for (Player player : game.getPlayers().values()) { - FilterCreaturePermanent filter = new FilterCreaturePermanent(); - filter.add(new ControllerIdPredicate(player.getId())); - filter.add(new ColorPredicate(ObjectColor.WHITE)); - if (game.getBattlefield().count(filter, source.getSourceId(), source.getControllerId(), game) > 0) { - player.damage(2, source.getSourceId(), source, game); - } + for (Player player : game + .getBattlefield() + .getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game) + .stream() + .filter(Objects::nonNull) + .map(Controllable::getControllerId) + .distinct() + .map(game::getPlayer) + .collect(Collectors.toList())) { + player.damage(2, source.getSourceId(), source, game); } return true; } diff --git a/Mage.Sets/src/mage/cards/d/DisorderInTheCourt.java b/Mage.Sets/src/mage/cards/d/DisorderInTheCourt.java new file mode 100644 index 00000000000..80c749e4ac8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DisorderInTheCourt.java @@ -0,0 +1,53 @@ +package mage.cards.d; + +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.common.ManacostVariableValue; +import mage.abilities.effects.common.ExileTargetForSourceEffect; +import mage.abilities.effects.common.ReturnToBattlefieldUnderOwnerControlTargetEffect; +import mage.abilities.effects.keyword.InvestigateEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.game.Game; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetadjustment.TargetAdjuster; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DisorderInTheCourt extends CardImpl { + + public DisorderInTheCourt(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{X}{W}{U}"); + + // Exile X target creatures, then investigate X times. Return the exiled cards to the battlefield tapped under their owners' control at the beginning of the next end step. + this.getSpellAbility().addEffect(new ExileTargetForSourceEffect() + .setText("exile X target creatures")); + this.getSpellAbility().addEffect(new InvestigateEffect(ManacostVariableValue.REGULAR) + .setText(", then investigate X times")); + this.getSpellAbility().addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect(true, true) + .setText("Return the exiled cards to the battlefield tapped under their owners' control at the beginning of the next end step")); + this.getSpellAbility().setTargetAdjuster(DisorderInTheCourtAdjuster.instance); + } + + private DisorderInTheCourt(final DisorderInTheCourt card) { + super(card); + } + + @Override + public DisorderInTheCourt copy() { + return new DisorderInTheCourt(this); + } +} + +enum DisorderInTheCourtAdjuster implements TargetAdjuster { + instance; + + @Override + public void adjustTargets(Ability ability, Game game) { + ability.getTargets().clear(); + ability.addTarget(new TargetCreaturePermanent(ability.getManaCostsToPay().getX())); + } +} diff --git a/Mage.Sets/src/mage/cards/d/Displace.java b/Mage.Sets/src/mage/cards/d/Displace.java index 360982f0095..7173f443fa3 100644 --- a/Mage.Sets/src/mage/cards/d/Displace.java +++ b/Mage.Sets/src/mage/cards/d/Displace.java @@ -6,7 +6,7 @@ import mage.abilities.effects.common.ReturnToBattlefieldUnderOwnerControlTargetE import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetControlledCreaturePermanent; import java.util.UUID; @@ -20,7 +20,7 @@ public final class Displace extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{U}"); // Exile up to two target creatures you control, then return those cards to the battlefield under their owner's control. - this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent(0, 2, new FilterControlledCreaturePermanent("creatures you control"), false)); + this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent(0, 2, StaticFilters.FILTER_CONTROLLED_CREATURES, false)); this.getSpellAbility().addEffect(new ExileTargetForSourceEffect()); this.getSpellAbility().addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false) .withReturnNames("those cards", "their owner's").concatBy(", then")); diff --git a/Mage.Sets/src/mage/cards/d/DisruptingShoal.java b/Mage.Sets/src/mage/cards/d/DisruptingShoal.java index b0c7d91c62d..fc6d2df5859 100644 --- a/Mage.Sets/src/mage/cards/d/DisruptingShoal.java +++ b/Mage.Sets/src/mage/cards/d/DisruptingShoal.java @@ -14,8 +14,6 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; import mage.filter.common.FilterOwnedCard; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardIdPredicate; import mage.filter.predicate.mageobject.ColorPredicate; import mage.game.Game; import mage.game.stack.Spell; @@ -29,15 +27,21 @@ import java.util.UUID; */ public final class DisruptingShoal extends CardImpl { + private static final FilterOwnedCard filter + = new FilterOwnedCard("a blue card with mana value X from your hand"); + + static { + filter.add(new ColorPredicate(ObjectColor.BLUE)); + } + public DisruptingShoal(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{X}{U}{U}"); this.subtype.add(SubType.ARCANE); // You may exile a blue card with converted mana cost X from your hand rather than pay Disrupting Shoal's mana cost. - FilterOwnedCard filter = new FilterOwnedCard("a blue card with mana value X from your hand"); - filter.add(new ColorPredicate(ObjectColor.BLUE)); - filter.add(Predicates.not(new CardIdPredicate(this.getId()))); // the exile cost can never be paid with the card itself - this.addAbility(new AlternativeCostSourceAbility(new ExileFromHandCost(new TargetCardInHand(filter), true))); + this.addAbility(new AlternativeCostSourceAbility(new ExileFromHandCost( + new TargetCardInHand(filter), true + ))); // 2/1/2005: Disrupting Shoal can target any spell, but does nothing unless that spell's converted mana cost is X. // Counter target spell if its converted mana cost is X. @@ -96,5 +100,4 @@ class DisruptingShoalCounterTargetEffect extends OneShotEffect { public String getText(Mode mode) { return "Counter target spell if its mana value is X"; } - } diff --git a/Mage.Sets/src/mage/cards/d/DisruptionProtocol.java b/Mage.Sets/src/mage/cards/d/DisruptionProtocol.java new file mode 100644 index 00000000000..017af341b2c --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DisruptionProtocol.java @@ -0,0 +1,52 @@ +package mage.cards.d; + +import mage.abilities.costs.OrCost; +import mage.abilities.costs.common.TapTargetCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.CounterTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.common.FilterControlledArtifactPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.permanent.TappedPredicate; +import mage.target.TargetSpell; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DisruptionProtocol extends CardImpl { + + private static final FilterControlledPermanent filter + = new FilterControlledArtifactPermanent("untapped artifact you control"); + + static { + filter.add(TappedPredicate.UNTAPPED); + } + + public DisruptionProtocol(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{U}{U}"); + + // As an additional cost to cast this spell, tap an untapped artifact you control or pay {1}. + this.getSpellAbility().addCost(new OrCost( + new TapTargetCost(new TargetControlledPermanent(filter)), + new GenericManaCost(1), "tap an untapped artifact you control or pay {1}" + )); + + // Counter target spell. + this.getSpellAbility().addEffect(new CounterTargetEffect()); + this.getSpellAbility().addTarget(new TargetSpell()); + } + + private DisruptionProtocol(final DisruptionProtocol card) { + super(card); + } + + @Override + public DisruptionProtocol copy() { + return new DisruptionProtocol(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DissensionInTheRanks.java b/Mage.Sets/src/mage/cards/d/DissensionInTheRanks.java index aa495af369f..5c3567832f9 100644 --- a/Mage.Sets/src/mage/cards/d/DissensionInTheRanks.java +++ b/Mage.Sets/src/mage/cards/d/DissensionInTheRanks.java @@ -27,7 +27,7 @@ public final class DissensionInTheRanks extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{3}{R}{R}"); // Target blocking creature fights another target blocking creature. - this.getSpellAbility().addEffect(new FightTargetsEffect()); + this.getSpellAbility().addEffect(new FightTargetsEffect(false)); TargetCreaturePermanent target = new TargetCreaturePermanent(1, 1, filter, false); target.setTargetTag(1); this.getSpellAbility().addTarget(target); diff --git a/Mage.Sets/src/mage/cards/d/DistendedMindbender.java b/Mage.Sets/src/mage/cards/d/DistendedMindbender.java index 3a3f64d7b50..b47ef20c124 100644 --- a/Mage.Sets/src/mage/cards/d/DistendedMindbender.java +++ b/Mage.Sets/src/mage/cards/d/DistendedMindbender.java @@ -70,8 +70,9 @@ class DistendedMindbenderEffect extends OneShotEffect { public DistendedMindbenderEffect() { super(Outcome.Discard); - this.staticText = "target opponent reveals their hand. You choose from it a nonland card with mana value 3 or less and a card with " - + "mana value 4 or greater. That player discards those cards."; + this.staticText = "target opponent reveals their hand. " + + "You choose from it a nonland card with mana value 3 or less and a card with mana value 4 or greater." + + "That player discards those cards."; } public DistendedMindbenderEffect(final DistendedMindbenderEffect effect) { diff --git a/Mage.Sets/src/mage/cards/d/DistortingLens.java b/Mage.Sets/src/mage/cards/d/DistortingLens.java index 3091347f58d..528268e4931 100644 --- a/Mage.Sets/src/mage/cards/d/DistortingLens.java +++ b/Mage.Sets/src/mage/cards/d/DistortingLens.java @@ -1,4 +1,3 @@ - package mage.cards.d; import java.util.UUID; @@ -27,7 +26,7 @@ import mage.target.targetpointer.FixedTarget; public final class DistortingLens extends CardImpl { public DistortingLens(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{2}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); // {tap}: Target permanent becomes the color of your choice until end of turn. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ChangeColorEffect(), new TapSourceCost()); @@ -63,7 +62,7 @@ class ChangeColorEffect extends OneShotEffect { Permanent chosen = game.getPermanent(targetPointer.getFirst(game, source)); if (player != null && permanent != null) { ContinuousEffect effect = new BecomesColorTargetEffect(null, Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(chosen.getId())); + effect.setTargetPointer(new FixedTarget(chosen.getId(), game)); game.addEffect(effect, source); return true; } diff --git a/Mage.Sets/src/mage/cards/d/DistractingGeist.java b/Mage.Sets/src/mage/cards/d/DistractingGeist.java new file mode 100644 index 00000000000..dbd48e0f47e --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DistractingGeist.java @@ -0,0 +1,57 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.keyword.DisturbAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.DefendingPlayerControlsPredicate; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DistractingGeist extends CardImpl { + + private static final FilterPermanent filter + = new FilterCreaturePermanent("creature defending player controls"); + + static { + filter.add(DefendingPlayerControlsPredicate.instance); + } + + public DistractingGeist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + this.secondSideCardClazz = mage.cards.c.CleverDistraction.class; + + // Whenever Distracting Geist attacks, tap target creature defending player controls. + Ability ability = new AttacksTriggeredAbility(new TapTargetEffect()); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + + // Disturb {4}{W} + this.addAbility(new DisturbAbility(this, "{4}{W}")); + } + + private DistractingGeist(final DistractingGeist card) { + super(card); + } + + @Override + public DistractingGeist copy() { + return new DistractingGeist(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/Distress.java b/Mage.Sets/src/mage/cards/d/Distress.java index d4869558a73..5d2eda30401 100644 --- a/Mage.Sets/src/mage/cards/d/Distress.java +++ b/Mage.Sets/src/mage/cards/d/Distress.java @@ -7,8 +7,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.TargetController; -import mage.filter.FilterCard; -import mage.filter.predicate.Predicates; +import mage.filter.StaticFilters; import mage.target.TargetPlayer; /** @@ -16,19 +15,13 @@ import mage.target.TargetPlayer; */ public final class Distress extends CardImpl { - private static final FilterCard filter = new FilterCard("nonland card"); - - static { - filter.add(Predicates.not(CardType.LAND.getPredicate())); - } - public Distress(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{B}{B}"); // Target player reveals their hand. You choose a nonland card from it. That player discards that card. this.getSpellAbility().addTarget(new TargetPlayer()); - this.getSpellAbility().addEffect(new DiscardCardYouChooseTargetEffect(filter, TargetController.ANY)); + this.getSpellAbility().addEffect(new DiscardCardYouChooseTargetEffect(StaticFilters.FILTER_CARD_NON_LAND, TargetController.ANY)); } private Distress(final Distress card) { diff --git a/Mage.Sets/src/mage/cards/d/DisturbingPlot.java b/Mage.Sets/src/mage/cards/d/DisturbingPlot.java index e9ced3a80ce..f8b24e66d33 100644 --- a/Mage.Sets/src/mage/cards/d/DisturbingPlot.java +++ b/Mage.Sets/src/mage/cards/d/DisturbingPlot.java @@ -24,7 +24,7 @@ public final class DisturbingPlot extends CardImpl { this.getSpellAbility().addTarget(new TargetCardInGraveyard(new FilterCreatureCard("creature card in a graveyard"))); // Conspire - this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.ONE)); + this.addAbility(new ConspireAbility(ConspireAbility.ConspireTargets.ONE)); } diff --git a/Mage.Sets/src/mage/cards/d/DiverSkaab.java b/Mage.Sets/src/mage/cards/d/DiverSkaab.java new file mode 100644 index 00000000000..5a4897b1767 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DiverSkaab.java @@ -0,0 +1,79 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.ExploitCreatureTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.PutOnLibraryTargetEffect; +import mage.abilities.keyword.ExploitAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DiverSkaab extends CardImpl { + + public DiverSkaab(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{U}"); + + this.subtype.add(SubType.ZOMBIE); + this.power = new MageInt(3); + this.toughness = new MageInt(5); + + // Exploit + this.addAbility(new ExploitAbility()); + + // When Diver Skaab exploits a creature, target creature's owner puts it on the top or bottom of their library. + Ability ability = new ExploitCreatureTriggeredAbility(new DiverSkaabEffect()); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + private DiverSkaab(final DiverSkaab card) { + super(card); + } + + @Override + public DiverSkaab copy() { + return new DiverSkaab(this); + } +} + +class DiverSkaabEffect extends OneShotEffect { + + DiverSkaabEffect() { + super(Outcome.Removal); + staticText = "target creature's owner puts it on the top or bottom of their library"; + } + + private DiverSkaabEffect(final DiverSkaabEffect effect) { + super(effect); + } + + @Override + public DiverSkaabEffect copy() { + return new DiverSkaabEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(game.getOwnerId(source.getFirstTarget())); + if (player == null) { + return false; + } + if (player.chooseUse(Outcome.Detriment, "Put the targeted object on the top or bottom of your library?", + "", "Top", "Bottom", source, game)) { + return new PutOnLibraryTargetEffect(true).apply(game, source); + } + return new PutOnLibraryTargetEffect(false).apply(game, source); + } +} diff --git a/Mage.Sets/src/mage/cards/d/Divest.java b/Mage.Sets/src/mage/cards/d/Divest.java index 5419018b704..5ca23291feb 100644 --- a/Mage.Sets/src/mage/cards/d/Divest.java +++ b/Mage.Sets/src/mage/cards/d/Divest.java @@ -1,4 +1,3 @@ - package mage.cards.d; import java.util.UUID; @@ -7,8 +6,7 @@ import mage.abilities.effects.common.discard.DiscardCardYouChooseTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.FilterCard; -import mage.filter.predicate.Predicates; +import mage.filter.StaticFilters; import mage.constants.TargetController; import mage.target.TargetPlayer; @@ -19,19 +17,12 @@ import mage.target.TargetPlayer; */ public final class Divest extends CardImpl { - private static final FilterCard filter = new FilterCard("an artifact or creature card"); - - static { - filter.add(Predicates.or(CardType.ARTIFACT.getPredicate(), - CardType.CREATURE.getPredicate())); - } - public Divest(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{B}"); // Target player reveals their hand. You choose an artifact or creature card from it. That player discards that card. this.getSpellAbility().addTarget(new TargetPlayer()); - this.getSpellAbility().addEffect(new DiscardCardYouChooseTargetEffect(filter, TargetController.ANY)); + this.getSpellAbility().addEffect(new DiscardCardYouChooseTargetEffect(StaticFilters.FILTER_CARD_ARTIFACT_OR_CREATURE, TargetController.ANY)); } private Divest(final Divest card) { @@ -42,4 +33,4 @@ public final class Divest extends CardImpl { public Divest copy() { return new Divest(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/d/DivinersPortent.java b/Mage.Sets/src/mage/cards/d/DivinersPortent.java index c127f3b36b0..cb8f5a731dc 100644 --- a/Mage.Sets/src/mage/cards/d/DivinersPortent.java +++ b/Mage.Sets/src/mage/cards/d/DivinersPortent.java @@ -26,7 +26,7 @@ public final class DivinersPortent extends CardImpl { // Roll a d20 and add the number of cards in your hand. RollDieWithResultTableEffect effect = new RollDieWithResultTableEffect( 20, "roll a d20 and add the number " + - "of cards in your hand", CardsInControllerHandCount.instance + "of cards in your hand", CardsInControllerHandCount.instance, 0 ); this.getSpellAbility().addEffect(effect); diff --git a/Mage.Sets/src/mage/cards/d/DivinersWand.java b/Mage.Sets/src/mage/cards/d/DivinersWand.java index 4313ca2671a..84b20665e54 100644 --- a/Mage.Sets/src/mage/cards/d/DivinersWand.java +++ b/Mage.Sets/src/mage/cards/d/DivinersWand.java @@ -27,25 +27,13 @@ import java.util.UUID; */ public final class DivinersWand extends CardImpl { - private static final FilterPermanent filter = new FilterCreaturePermanent("a Wizard creature"); - - static { - filter.add(SubType.WIZARD.getPredicate()); - } + private static final FilterPermanent filter = new FilterCreaturePermanent(SubType.WIZARD, "a Wizard creature"); public DivinersWand(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.TRIBAL, CardType.ARTIFACT}, "{3}"); this.subtype.add(SubType.WIZARD); this.subtype.add(SubType.EQUIPMENT); - // Whenever a Wizard creature enters the battlefield, you may attach Diviner's Wand to it. - this.addAbility(new EntersBattlefieldAllTriggeredAbility( - Zone.BATTLEFIELD, new AttachEffect(Outcome.Detriment, "attach {this} to it"), - filter, true, SetTargetPointer.PERMANENT, null)); - - // Equip {3} - this.addAbility(new EquipAbility(Outcome.AddAbility, new GenericManaCost(3))); - // Equipped creature has "Whenever you draw a card, this creature gets +1/+1 and gains flying until end of turn" and "{4}: Draw a card." // new abilities Ability newBoost = new DrawCardControllerTriggeredAbility(new BoostSourceEffect(1, 1, Duration.EndOfTurn), false); @@ -60,6 +48,14 @@ public final class DivinersWand extends CardImpl { Ability totalAbility = new SimpleStaticAbility(Zone.BATTLEFIELD, effectBoost); totalAbility.addEffect(effectDraw.concatBy("and")); this.addAbility(totalAbility); + + // Whenever a Wizard creature enters the battlefield, you may attach Diviner's Wand to it. + this.addAbility(new EntersBattlefieldAllTriggeredAbility( + Zone.BATTLEFIELD, new AttachEffect(Outcome.Detriment, "attach {this} to it"), + filter, true, SetTargetPointer.PERMANENT, null)); + + // Equip {3} + this.addAbility(new EquipAbility(Outcome.AddAbility, new GenericManaCost(3))); } private DivinersWand(final DivinersWand card) { diff --git a/Mage.Sets/src/mage/cards/d/DocentOfPerfection.java b/Mage.Sets/src/mage/cards/d/DocentOfPerfection.java index 1848042ea01..eff5ca4030d 100644 --- a/Mage.Sets/src/mage/cards/d/DocentOfPerfection.java +++ b/Mage.Sets/src/mage/cards/d/DocentOfPerfection.java @@ -45,7 +45,6 @@ public final class DocentOfPerfection extends CardImpl { this.power = new MageInt(5); this.toughness = new MageInt(4); - this.transformable = true; this.secondSideCardClazz = mage.cards.f.FinalIteration.class; // Flying @@ -98,7 +97,7 @@ class DocentOfPerfectionEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { if (game.getBattlefield().count(filter, source.getSourceId(), source.getControllerId(), game) >= 3) { - return new TransformSourceEffect(true).apply(game, source); + return new TransformSourceEffect().apply(game, source); } } return false; diff --git a/Mage.Sets/src/mage/cards/d/DocksideChef.java b/Mage.Sets/src/mage/cards/d/DocksideChef.java new file mode 100644 index 00000000000..96bd2dbb4f2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DocksideChef.java @@ -0,0 +1,57 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.mana.ManaCostsImpl; +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.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DocksideChef extends CardImpl { + + private static final FilterControlledPermanent filter = new FilterControlledPermanent("artifact or creature"); + + static { + filter.add(Predicates.or( + CardType.ARTIFACT.getPredicate(), + CardType.CREATURE.getPredicate() + )); + } + + public DocksideChef(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{B}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.CITIZEN); + this.power = new MageInt(1); + this.toughness = new MageInt(2); + + // {1}{B}, Sacrifice an artifact or creature: Draw a card. + Ability ability = new SimpleActivatedAbility( + new DrawCardSourceControllerEffect(1), new ManaCostsImpl<>("{1}{B}") + ); + ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(filter))); + this.addAbility(ability); + } + + private DocksideChef(final DocksideChef card) { + super(card); + } + + @Override + public DocksideChef copy() { + return new DocksideChef(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DokuchiShadowWalker.java b/Mage.Sets/src/mage/cards/d/DokuchiShadowWalker.java new file mode 100644 index 00000000000..fb60d716a56 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DokuchiShadowWalker.java @@ -0,0 +1,37 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.keyword.NinjutsuAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DokuchiShadowWalker extends CardImpl { + + public DokuchiShadowWalker(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}{B}"); + + this.subtype.add(SubType.OGRE); + this.subtype.add(SubType.NINJA); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + + // Ninjutsu {3}{B} + this.addAbility(new NinjutsuAbility("{3}{B}")); + } + + private DokuchiShadowWalker(final DokuchiShadowWalker card) { + super(card); + } + + @Override + public DokuchiShadowWalker copy() { + return new DokuchiShadowWalker(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DokuchiSilencer.java b/Mage.Sets/src/mage/cards/d/DokuchiSilencer.java new file mode 100644 index 00000000000..88a256091b5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DokuchiSilencer.java @@ -0,0 +1,107 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.costs.common.DiscardTargetCost; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.DoWhenCostPaid; +import mage.abilities.keyword.NinjutsuAbility; +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.filter.common.FilterCreatureOrPlaneswalkerPermanent; +import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.game.Game; +import mage.game.events.DamagedEvent; +import mage.game.events.GameEvent; +import mage.players.Player; +import mage.target.common.TargetCardInHand; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DokuchiSilencer extends CardImpl { + + public DokuchiSilencer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.NINJA); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Ninjutsu {1}{B} + this.addAbility(new NinjutsuAbility("{1}{B}")); + + // Whenever Dokuchi Silencer deals combat damage to a player, you may discard a creature card. When you do, destroy target creature or planeswalker that player controls. + this.addAbility(new BlindZealotTriggeredAbility()); + } + + private DokuchiSilencer(final DokuchiSilencer card) { + super(card); + } + + @Override + public DokuchiSilencer copy() { + return new DokuchiSilencer(this); + } +} + +class BlindZealotTriggeredAbility extends TriggeredAbilityImpl { + + BlindZealotTriggeredAbility() { + super(Zone.BATTLEFIELD, null, false); + } + + private BlindZealotTriggeredAbility(final BlindZealotTriggeredAbility ability) { + super(ability); + } + + @Override + public BlindZealotTriggeredAbility copy() { + return new BlindZealotTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DAMAGED_PLAYER; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Player opponent = game.getPlayer(event.getPlayerId()); + if (opponent == null + || !event.getSourceId().equals(getSourceId()) + || !((DamagedEvent) event).isCombatDamage()) { + return false; + } + FilterPermanent filter = new FilterCreatureOrPlaneswalkerPermanent( + "creature or planeswalker" + opponent.getLogName() + " controls" + ); + filter.add(new ControllerIdPredicate(opponent.getId())); + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new DestroyTargetEffect(), false, + "destroy target creature or planeswalker that player controls" + ); + this.getEffects().clear(); + this.addEffect(new DoWhenCostPaid( + ability, + new DiscardTargetCost(new TargetCardInHand(StaticFilters.FILTER_CARD_CREATURE)), + "Discard a creature card?" + )); + return true; + } + + @Override + public String getRule() { + return "Whenever {this} deals combat damage to a player, you may discard a creature card. " + + "When you do, destroy target creature or planeswalker that player controls."; + } +} diff --git a/Mage.Sets/src/mage/cards/d/DollhouseOfHorrors.java b/Mage.Sets/src/mage/cards/d/DollhouseOfHorrors.java new file mode 100644 index 00000000000..01d92282866 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DollhouseOfHorrors.java @@ -0,0 +1,100 @@ +package mage.cards.d; + +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.ExileFromGraveCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.dynamicvalue.common.ArtifactYouControlCount; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenCopyTargetEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.HasteAbility; +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.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.PermanentCard; +import mage.target.common.TargetCardInYourGraveyard; +import mage.target.targetpointer.FixedTargets; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DollhouseOfHorrors extends CardImpl { + + public DollhouseOfHorrors(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{5}"); + + // {1}, {T}, Exile a creature card from your graveyard: Create a token that's a copy of the exiled card, except it's a 0/0 Construct artifact in addition to its other types and it has "This creature gets +1/+1 for each Construct you control." It gains haste until end of turn. Activate only as a sorcery. + Ability ability = new ActivateAsSorceryActivatedAbility( + new DollhouseOfHorrorsEffect(), new GenericManaCost(1) + ); + ability.addCost(new TapSourceCost()); + ability.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard( + StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD + ), true)); + this.addAbility(ability); + } + + private DollhouseOfHorrors(final DollhouseOfHorrors card) { + super(card); + } + + @Override + public DollhouseOfHorrors copy() { + return new DollhouseOfHorrors(this); + } +} + +class DollhouseOfHorrorsEffect extends OneShotEffect { + + DollhouseOfHorrorsEffect() { + super(Outcome.Benefit); + staticText = "create a token that's a copy of the exiled card, except it's a 0/0 Construct artifact in " + + "addition to its other types and it has \"This creature gets +1/+1 for each Construct you control.\" " + + "It gains haste until end of turn"; + } + + private DollhouseOfHorrorsEffect(final DollhouseOfHorrorsEffect effect) { + super(effect); + } + + @Override + public DollhouseOfHorrorsEffect copy() { + return new DollhouseOfHorrorsEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Card card = game.getCard(getTargetPointer().getFirst(game, source)); + if (card == null) { + return false; + } + CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect( + source.getControllerId(), CardType.ARTIFACT, false, 1, false, + false, null, 0, 0, false + ); + effect.setSavedPermanent(new PermanentCard(card, source.getControllerId(), game)); + effect.setAdditionalSubType(SubType.CONSTRUCT); + effect.addAdditionalAbilities(new SimpleStaticAbility(new BoostSourceEffect( + ArtifactYouControlCount.instance, + ArtifactYouControlCount.instance, + Duration.WhileOnBattlefield + ).setText("This creature gets +1/+1 for each artifact you control"))); + effect.apply(game, source); + game.addEffect(new GainAbilityTargetEffect( + HasteAbility.getInstance(), Duration.EndOfTurn + ).setTargetPointer(new FixedTargets(effect.getAddedPermanents(), game)), source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/d/DominatingVampire.java b/Mage.Sets/src/mage/cards/d/DominatingVampire.java new file mode 100644 index 00000000000..08825f5dafd --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DominatingVampire.java @@ -0,0 +1,76 @@ +package mage.cards.d; + +import java.util.UUID; +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.UntapTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.continuous.GainControlTargetEffect; +import mage.abilities.keyword.HasteAbility; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author weirddan455 + */ +public final class DominatingVampire extends CardImpl { + + private static final FilterCreaturePermanent filter + = new FilterCreaturePermanent("creature with mana value less than or equal to the number of Vampires you control"); + + static { + filter.add(DominatingVampirePredicate.instance); + } + + public DominatingVampire(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}{R}"); + + this.subtype.add(SubType.VAMPIRE); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // When Dominating Vampire enters the battlefield, gain control of target creature with mana value less than or equal to the number of Vampires you control until end of turn. + // Untap that creature. It gains haste until end of turn. + Ability ability = new EntersBattlefieldTriggeredAbility(new GainControlTargetEffect(Duration.EndOfTurn)); + ability.addEffect(new UntapTargetEffect().setText("Untap that creature")); + ability.addEffect(new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn, "It gains haste until end of turn")); + ability.addTarget(new TargetCreaturePermanent(filter)); + this.addAbility(ability); + } + + private DominatingVampire(final DominatingVampire card) { + super(card); + } + + @Override + public DominatingVampire copy() { + return new DominatingVampire(this); + } +} + +enum DominatingVampirePredicate implements ObjectSourcePlayerPredicate { + instance; + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + int vampires = 0; + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(input.getPlayerId())) { + if (permanent.hasSubtype(SubType.VAMPIRE, game)) { + vampires++; + } + } + return input.getObject().getManaValue() <= vampires; + } +} diff --git a/Mage.Sets/src/mage/cards/d/DomriAnarchOfBolas.java b/Mage.Sets/src/mage/cards/d/DomriAnarchOfBolas.java index 295ccb16455..9dbe48f6fbc 100644 --- a/Mage.Sets/src/mage/cards/d/DomriAnarchOfBolas.java +++ b/Mage.Sets/src/mage/cards/d/DomriAnarchOfBolas.java @@ -3,7 +3,6 @@ package mage.cards.d; import mage.Mana; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CantBeCounteredControlledEffect; @@ -30,7 +29,7 @@ public final class DomriAnarchOfBolas extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.DOMRI); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // Creatures you control get +1/+0. this.addAbility(new SimpleStaticAbility( @@ -41,7 +40,7 @@ public final class DomriAnarchOfBolas extends CardImpl { this.addAbility(new LoyaltyAbility(new DomriAnarchOfBolasEffect(), 1)); // -2: Target creature you control fights target creature you don't control. - Ability ability = new LoyaltyAbility(new FightTargetsEffect(), -2); + Ability ability = new LoyaltyAbility(new FightTargetsEffect(false), -2); ability.addTarget(new TargetControlledCreaturePermanent()); ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/d/DomriChaosBringer.java b/Mage.Sets/src/mage/cards/d/DomriChaosBringer.java index 17e1e627510..f2e5334c50f 100644 --- a/Mage.Sets/src/mage/cards/d/DomriChaosBringer.java +++ b/Mage.Sets/src/mage/cards/d/DomriChaosBringer.java @@ -5,7 +5,6 @@ import mage.Mana; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.GetEmblemEffect; @@ -38,7 +37,7 @@ public final class DomriChaosBringer extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.DOMRI); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +1: Add {R} or {G}. If that mana is spent on a creature spell, it gains riot. this.addAbility(new LoyaltyAbility(new DomriChaosBringerEffect(), 1)); diff --git a/Mage.Sets/src/mage/cards/d/DomriCitySmasher.java b/Mage.Sets/src/mage/cards/d/DomriCitySmasher.java index 2475c5f7d1c..b719cb3d4c0 100644 --- a/Mage.Sets/src/mage/cards/d/DomriCitySmasher.java +++ b/Mage.Sets/src/mage/cards/d/DomriCitySmasher.java @@ -2,7 +2,6 @@ package mage.cards.d; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.continuous.BoostControlledEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; @@ -31,7 +30,7 @@ public final class DomriCitySmasher extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.DOMRI); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +2: Creatures you control get +1/+1 and gain haste until end of turn. Ability ability = new LoyaltyAbility(new BoostControlledEffect( diff --git a/Mage.Sets/src/mage/cards/d/DomriRade.java b/Mage.Sets/src/mage/cards/d/DomriRade.java index d7ce9c01007..631d3251ea4 100644 --- a/Mage.Sets/src/mage/cards/d/DomriRade.java +++ b/Mage.Sets/src/mage/cards/d/DomriRade.java @@ -1,11 +1,8 @@ - package mage.cards.d; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.FightTargetsEffect; import mage.abilities.effects.common.GetEmblemEffect; @@ -13,21 +10,17 @@ import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.cards.CardsImpl; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Outcome; -import mage.constants.SuperType; -import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.other.AnotherTargetPredicate; +import mage.constants.*; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.command.emblems.DomriRadeEmblem; import mage.players.Player; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class DomriRade extends CardImpl { @@ -37,27 +30,24 @@ public final class DomriRade extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.DOMRI); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +1: Look at the top card of your library. If it's a creature card, you may reveal it and put it into your hand. this.addAbility(new LoyaltyAbility(new DomriRadeEffect1(), 1)); // -2: Target creature you control fights another target creature. - LoyaltyAbility ability2 = new LoyaltyAbility(new FightTargetsEffect(), -2); + LoyaltyAbility ability2 = new LoyaltyAbility(new FightTargetsEffect(false), -2); TargetControlledCreaturePermanent target = new TargetControlledCreaturePermanent(); target.setTargetTag(1); ability2.addTarget(target); - FilterCreaturePermanent filter = new FilterCreaturePermanent("another creature to fight"); - filter.add(new AnotherTargetPredicate(2)); - TargetCreaturePermanent target2 = new TargetCreaturePermanent(filter); + TargetCreaturePermanent target2 = new TargetCreaturePermanent(StaticFilters.FILTER_ANOTHER_CREATURE_TARGET_2); target2.setTargetTag(2); ability2.addTarget(target2); this.addAbility(ability2); // -7: You get an emblem with "Creatures you control have double strike, trample, hexproof and haste." this.addAbility(new LoyaltyAbility(new GetEmblemEffect(new DomriRadeEmblem()), -7)); - } private DomriRade(final DomriRade card) { diff --git a/Mage.Sets/src/mage/cards/d/DonalHeraldOfWings.java b/Mage.Sets/src/mage/cards/d/DonalHeraldOfWings.java new file mode 100644 index 00000000000..2df6161f99f --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DonalHeraldOfWings.java @@ -0,0 +1,138 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterSpell; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.MageObjectReferencePredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.stack.Spell; +import mage.game.stack.StackObject; +import mage.players.Player; +import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; +import mage.util.functions.StackObjectCopyApplier; + +import java.util.UUID; + +/** + * @author Alex-Vasile + */ +public class DonalHeraldOfWings extends CardImpl { + + public DonalHeraldOfWings(UUID ownderId, CardSetInfo setInfo) { + super(ownderId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + + this.addSubType(SubType.HUMAN); + this.addSubType(SubType.WIZARD); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Whenever you cast a nonlegendary creature spell with flying, you may copy it, + // except the copy is a 1/1 Spirit in addition to its other types. + // Do this only once each turn. (The copy becomes a token.) + // TODO: This still triggers and asks if you wanna use it, even if you've used it once this turn. + this.addAbility(new DonalHeraldOfWingsTriggeredAbility()); + } + + private DonalHeraldOfWings(final DonalHeraldOfWings card) { super(card); } + + @Override + public DonalHeraldOfWings copy() { return new DonalHeraldOfWings(this); } +} + +class DonalHeraldOfWingsTriggeredAbility extends SpellCastControllerTriggeredAbility { + + private static final FilterSpell filterSpell = new FilterSpell("a nonlegendary creature spell with flying"); + static { + filterSpell.add(Predicates.not(SuperType.LEGENDARY.getPredicate())); + filterSpell.add(CardType.CREATURE.getPredicate()); + } + + DonalHeraldOfWingsTriggeredAbility() { + super(new DonalHeraldOfWingsEffect(), filterSpell, true, true); + } + + private DonalHeraldOfWingsTriggeredAbility(final DonalHeraldOfWingsTriggeredAbility ability) { super(ability); } + + @Override + public DonalHeraldOfWingsTriggeredAbility copy() { + return new DonalHeraldOfWingsTriggeredAbility(this); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return abilityAvailableThisTurn(game) && super.checkTrigger(event, game); + } + + @Override + public boolean resolve(Game game) { + if (!(abilityAvailableThisTurn(game) && super.resolve(game))) { return false; } + game.getState().setValue( + CardUtil.getCardZoneString("lastTurnResolved" + originalId, sourceId, game), + game.getTurnNum() + ); + + return true; + } + + private boolean abilityAvailableThisTurn(Game game) { + Integer lastTurnResolved = (Integer) game.getState().getValue( + CardUtil.getCardZoneString("lastTurnResolved" + originalId, sourceId, game) + ); + // A null result is assumed to mean the this ability has not been used yet. + return lastTurnResolved == null || lastTurnResolved != game.getTurnNum(); + } +} + +class DonalHeraldOfWingsEffect extends OneShotEffect { + + DonalHeraldOfWingsEffect() { + super(Outcome.Copy); + staticText = "you may copy it, except the copy is a 1/1 Spirit in addition to its other types. " + + "Do this only once each turn. (The copy becomes a token.)"; + } + + private DonalHeraldOfWingsEffect(final DonalHeraldOfWingsEffect effect) { super(effect); } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { return false; } + + // Get the card that was cast + if (this.getTargetPointer() == null) { return false; } + Spell originalSpell = game.getStack().getSpell(((FixedTarget) this.getTargetPointer()).getTarget()); + + // Create a token copy + originalSpell.createCopyOnStack(game, source, controller.getId(), false, 1, DonalHeraldOfWingsApplier.instance); + + return true; + } + + @Override + public Effect copy() { return new DonalHeraldOfWingsEffect(this); } +} + +enum DonalHeraldOfWingsApplier implements StackObjectCopyApplier { + instance; + + @Override + public void modifySpell(StackObject copiedSpell, Game game) { + copiedSpell.addSubType(SubType.SPIRIT); + copiedSpell.getPower().modifyBaseValue(1); + copiedSpell.getToughness().modifyBaseValue(1); + } + + @Override + public MageObjectReferencePredicate getNextNewTargetType(int copyNumber) { return null; } +} diff --git a/Mage.Sets/src/mage/cards/d/DongZhouTheTyrant.java b/Mage.Sets/src/mage/cards/d/DongZhouTheTyrant.java index eb3414af27f..4abc258e884 100644 --- a/Mage.Sets/src/mage/cards/d/DongZhouTheTyrant.java +++ b/Mage.Sets/src/mage/cards/d/DongZhouTheTyrant.java @@ -7,7 +7,7 @@ import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -20,12 +20,6 @@ import java.util.UUID; */ public final class DongZhouTheTyrant extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public DongZhouTheTyrant(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}"); addSuperType(SuperType.LEGENDARY); @@ -36,7 +30,7 @@ public final class DongZhouTheTyrant extends CardImpl { // When Dong Zhou, the Tyrant enters the battlefield, target creature an opponent controls deals damage equal to its power to that player. Ability ability = new EntersBattlefieldTriggeredAbility(new DongZhouTheTyrantEffect(), false); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/d/DoomBlade.java b/Mage.Sets/src/mage/cards/d/DoomBlade.java index 65e746f02a7..e406967e20d 100644 --- a/Mage.Sets/src/mage/cards/d/DoomBlade.java +++ b/Mage.Sets/src/mage/cards/d/DoomBlade.java @@ -1,16 +1,11 @@ - - package mage.cards.d; import java.util.UUID; -import mage.ObjectColor; import mage.abilities.effects.common.DestroyTargetEffect; 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; /** @@ -19,16 +14,10 @@ import mage.target.common.TargetCreaturePermanent; */ public final class DoomBlade extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public DoomBlade(UUID ownerId, CardSetInfo setInfo){ super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{B}"); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); this.getSpellAbility().addEffect(new DestroyTargetEffect()); } diff --git a/Mage.Sets/src/mage/cards/d/DoomWeaver.java b/Mage.Sets/src/mage/cards/d/DoomWeaver.java new file mode 100644 index 00000000000..c45b379efc1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DoomWeaver.java @@ -0,0 +1,55 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.SourcePermanentPowerCount; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.continuous.GainAbilityPairedEffect; +import mage.abilities.keyword.ReachAbility; +import mage.abilities.keyword.SoulbondAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DoomWeaver extends CardImpl { + + private static final DynamicValue xValue = new SourcePermanentPowerCount(); + + public DoomWeaver(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}{B}"); + + this.subtype.add(SubType.SPIDER); + this.subtype.add(SubType.HORROR); + this.power = new MageInt(1); + this.toughness = new MageInt(8); + + // Reach + this.addAbility(ReachAbility.getInstance()); + + // Soulbond + this.addAbility(new SoulbondAbility()); + + // As long as Doom Weaver is paired with another creature, each of those creatures has "When this creature dies, draw cards equal to its power." + this.addAbility(new SimpleStaticAbility(new GainAbilityPairedEffect(new DiesSourceTriggeredAbility( + new DrawCardSourceControllerEffect(xValue).setText("draw cards equal to its power") + ).setTriggerPhrase("When this creature dies, "), "As long as {this} is paired with another creature, " + + "each of those creatures has \"When this creature dies, draw cards equal to its power.\""))); + } + + private DoomWeaver(final DoomWeaver card) { + super(card); + } + + @Override + public DoomWeaver copy() { + return new DoomWeaver(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DoranTheSiegeTower.java b/Mage.Sets/src/mage/cards/d/DoranTheSiegeTower.java index 984b2342ec1..24918b36682 100644 --- a/Mage.Sets/src/mage/cards/d/DoranTheSiegeTower.java +++ b/Mage.Sets/src/mage/cards/d/DoranTheSiegeTower.java @@ -1,4 +1,3 @@ - package mage.cards.d; import mage.MageInt; @@ -9,7 +8,6 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; -import mage.constants.Zone; import mage.filter.StaticFilters; import java.util.UUID; @@ -37,12 +35,9 @@ public final class DoranTheSiegeTower extends CardImpl { this.toughness = new MageInt(5); // Each creature assigns combat damage equal to its toughness rather than its power. - this.addAbility(new SimpleStaticAbility( - Zone.BATTLEFIELD, - new CombatDamageByToughnessEffect( - StaticFilters.FILTER_PERMANENT_CREATURE, false - ) - )); + this.addAbility(new SimpleStaticAbility(new CombatDamageByToughnessEffect( + StaticFilters.FILTER_PERMANENT_CREATURE, false + ))); } private DoranTheSiegeTower(final DoranTheSiegeTower card) { diff --git a/Mage.Sets/src/mage/cards/d/DormantGrove.java b/Mage.Sets/src/mage/cards/d/DormantGrove.java new file mode 100644 index 00000000000..e2cf7d115e2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DormantGrove.java @@ -0,0 +1,75 @@ +package mage.cards.d; + +import java.util.UUID; + +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfCombatTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.TransformAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetControlledCreaturePermanent; + +/** + * + * @author weirddan455 + */ +public final class DormantGrove extends CardImpl { + + public DormantGrove(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{G}"); + + this.secondSideCardClazz = mage.cards.g.GnarledGrovestrider.class; + + // At the beginning of combat on your turn, put a +1/+1 counter on target creature you control. + // Then if that creature has toughness 6 or greater, transform Dormant Grove. + this.addAbility(new TransformAbility()); + + Ability ability = new BeginningOfCombatTriggeredAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance()), + TargetController.YOU, false + ); + ability.addEffect(new ConditionalOneShotEffect( + new TransformSourceEffect(), + DormatGroveCondition.instance, + "Then if that creature has toughness 6 or greater, transform {this}" + )); + ability.addTarget(new TargetControlledCreaturePermanent()); + this.addAbility(ability); + } + + private DormantGrove(final DormantGrove card) { + super(card); + } + + @Override + public DormantGrove copy() { + return new DormantGrove(this); + } +} + +enum DormatGroveCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent != null) { + return permanent.getToughness().getValue() >= 6; + } + return false; + } + + @Override + public String toString() { + return "that creature has toughness 6 or greater"; + } +} diff --git a/Mage.Sets/src/mage/cards/d/DorotheaVengefulVictim.java b/Mage.Sets/src/mage/cards/d/DorotheaVengefulVictim.java new file mode 100644 index 00000000000..24b3336c0d0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DorotheaVengefulVictim.java @@ -0,0 +1,53 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.common.AttacksOrBlocksTriggeredAbility; +import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.common.SacrificeSourceEffect; +import mage.abilities.keyword.DisturbAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DorotheaVengefulVictim extends CardImpl { + + public DorotheaVengefulVictim(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + this.secondSideCardClazz = mage.cards.d.DorotheasRetribution.class; + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When Dorothea, Vengeful Victim attacks or blocks, sacrifice it at end of combat. + this.addAbility(new AttacksOrBlocksTriggeredAbility(new CreateDelayedTriggeredAbilityEffect( + new AtTheEndOfCombatDelayedTriggeredAbility(new SacrificeSourceEffect()) + ).setText("sacrifice it at end of combat"), false)); + + // Disturb {1}{W}{U} + this.addAbility(new DisturbAbility(this, "{1}{W}{U}")); + } + + private DorotheaVengefulVictim(final DorotheaVengefulVictim card) { + super(card); + } + + @Override + public DorotheaVengefulVictim copy() { + return new DorotheaVengefulVictim(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DorotheasRetribution.java b/Mage.Sets/src/mage/cards/d/DorotheasRetribution.java new file mode 100644 index 00000000000..8a837315163 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DorotheasRetribution.java @@ -0,0 +1,98 @@ +package mage.cards.d; + +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.common.PutIntoGraveFromAnywhereSourceAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.ExileSourceEffect; +import mage.abilities.effects.common.SacrificeTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.game.Game; +import mage.game.permanent.token.DorotheasRetributionSpiritToken; +import mage.game.permanent.token.Token; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetpointer.FixedTargets; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DorotheasRetribution extends CardImpl { + + public DorotheasRetribution(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, ""); + + this.subtype.add(SubType.AURA); + this.color.setWhite(true); + this.color.setBlue(true); + this.nightCard = true; + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // Enchanted creature has "Whenever this creature attacks, create a 4/4 white Spirit creature token with flying that's tapped and attacking. Sacrifice that token at end of combat." + this.addAbility(new SimpleStaticAbility(new GainAbilityAttachedEffect( + new AttacksTriggeredAbility(new DorotheasRetributionEffect()) + .setTriggerPhrase("Whenever this creature attacks, "), + AttachmentType.AURA + ))); + + // If Dorothea's Retribution would be put into a graveyard from anywhere, exile it instead. + this.addAbility(new PutIntoGraveFromAnywhereSourceAbility(new ExileSourceEffect().setText("exile it instead"))); + } + + private DorotheasRetribution(final DorotheasRetribution card) { + super(card); + } + + @Override + public DorotheasRetribution copy() { + return new DorotheasRetribution(this); + } +} + +class DorotheasRetributionEffect extends OneShotEffect { + + DorotheasRetributionEffect() { + super(Outcome.Benefit); + staticText = "create a 4/4 white Spirit creature token with flying " + + "that's tapped and attacking. Sacrifice that token at end of combat"; + } + + private DorotheasRetributionEffect(final DorotheasRetributionEffect effect) { + super(effect); + } + + @Override + public DorotheasRetributionEffect copy() { + return new DorotheasRetributionEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Token token = new DorotheasRetributionSpiritToken(); + token.putOntoBattlefield(1, game, source, source.getControllerId(), true, true); + game.addDelayedTriggeredAbility(new AtTheEndOfCombatDelayedTriggeredAbility( + new SacrificeTargetEffect() + .setTargetPointer(new FixedTargets(token, game)) + .setText("sacrifce that token") + ), source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/d/DoublingChant.java b/Mage.Sets/src/mage/cards/d/DoublingChant.java index 1b12baf2332..9607085e063 100644 --- a/Mage.Sets/src/mage/cards/d/DoublingChant.java +++ b/Mage.Sets/src/mage/cards/d/DoublingChant.java @@ -15,6 +15,7 @@ import mage.filter.predicate.mageobject.NamePredicate; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInLibrary; +import mage.util.CardUtil; import java.util.*; import java.util.stream.Collectors; @@ -109,7 +110,7 @@ class DoublingChantTarget extends TargetCardInLibrary { } private void populateNameMap(Set names) { - names.stream().forEach(name -> this.nameMap.compute(name, (s, i) -> i == null ? 1 : Integer.sum(i, 1))); + names.stream().forEach(name -> this.nameMap.compute(name, CardUtil::setOrIncrementValue)); } @Override @@ -127,7 +128,7 @@ class DoublingChantTarget extends TargetCardInLibrary { .map(game::getCard) .filter(Objects::nonNull) .map(MageObject::getName) - .forEach(name -> alreadyChosen.compute(name, (s, i) -> i == null ? 1 : Integer.sum(i, 1))); + .forEach(name -> alreadyChosen.compute(name, CardUtil::setOrIncrementValue)); return nameMap.getOrDefault(card.getName(), 0) > alreadyChosen.getOrDefault(card.getName(), 0); } diff --git a/Mage.Sets/src/mage/cards/d/DovinArchitectOfLaw.java b/Mage.Sets/src/mage/cards/d/DovinArchitectOfLaw.java index 997df68c943..ee86a72467e 100644 --- a/Mage.Sets/src/mage/cards/d/DovinArchitectOfLaw.java +++ b/Mage.Sets/src/mage/cards/d/DovinArchitectOfLaw.java @@ -2,7 +2,6 @@ package mage.cards.d; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.common.*; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -25,7 +24,7 @@ public final class DovinArchitectOfLaw extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.DOVIN); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +1: You gain 2 life and draw a card. Ability ability = new LoyaltyAbility(new GainLifeEffect(2), 1); diff --git a/Mage.Sets/src/mage/cards/d/DovinBaan.java b/Mage.Sets/src/mage/cards/d/DovinBaan.java index e2c59790d5b..dd266aa886b 100644 --- a/Mage.Sets/src/mage/cards/d/DovinBaan.java +++ b/Mage.Sets/src/mage/cards/d/DovinBaan.java @@ -4,7 +4,6 @@ package mage.cards.d; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; import mage.abilities.effects.Effect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; @@ -21,7 +20,6 @@ import mage.constants.SuperType; import mage.game.Game; import mage.game.command.emblems.DovinBaanEmblem; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.target.common.TargetCreaturePermanent; /** @@ -35,7 +33,7 @@ public final class DovinBaan extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.DOVIN); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +1: Until your next turn, up to one target creature gets -3/-0 and its activated abilities can't be activated. Effect effect = new BoostTargetEffect(-3, 0, Duration.UntilYourNextTurn); diff --git a/Mage.Sets/src/mage/cards/d/DovinGrandArbiter.java b/Mage.Sets/src/mage/cards/d/DovinGrandArbiter.java index 4d7d713cfce..dae97514102 100644 --- a/Mage.Sets/src/mage/cards/d/DovinGrandArbiter.java +++ b/Mage.Sets/src/mage/cards/d/DovinGrandArbiter.java @@ -3,7 +3,6 @@ package mage.cards.d; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; import mage.abilities.effects.common.CreateTokenEffect; @@ -33,7 +32,7 @@ public final class DovinGrandArbiter extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.DOVIN); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +1: Until end of turn, whenever a creature you control deals combat damage to a player, put a loyalty counter on Dovin, Grand Arbiter. this.addAbility(new LoyaltyAbility(new CreateDelayedTriggeredAbilityEffect( diff --git a/Mage.Sets/src/mage/cards/d/DovinHandOfControl.java b/Mage.Sets/src/mage/cards/d/DovinHandOfControl.java index dc14c627a52..796e70c88f7 100644 --- a/Mage.Sets/src/mage/cards/d/DovinHandOfControl.java +++ b/Mage.Sets/src/mage/cards/d/DovinHandOfControl.java @@ -2,7 +2,6 @@ package mage.cards.d; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.PreventDamageByTargetEffect; import mage.abilities.effects.common.PreventDamageToTargetEffect; @@ -36,7 +35,7 @@ public final class DovinHandOfControl extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.DOVIN); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // Artifact, instant, and sorcery spells your opponents cast cost {1} more to cast. this.addAbility(new SimpleStaticAbility(new SpellsCostIncreasingAllEffect(1, filter, TargetController.OPPONENT))); diff --git a/Mage.Sets/src/mage/cards/d/DowsingDagger.java b/Mage.Sets/src/mage/cards/d/DowsingDagger.java index bbd5575d4db..eb4887c3495 100644 --- a/Mage.Sets/src/mage/cards/d/DowsingDagger.java +++ b/Mage.Sets/src/mage/cards/d/DowsingDagger.java @@ -33,7 +33,6 @@ public final class DowsingDagger extends CardImpl { this.subtype.add(SubType.EQUIPMENT); - this.transformable = true; this.secondSideCardClazz = mage.cards.l.LostVale.class; // When Dowsing Dagger enters the battlefield, target opponent creates two 0/2 green Plant creature tokens with defender. @@ -47,7 +46,7 @@ public final class DowsingDagger extends CardImpl { // Whenever equipped creature deals combat damage to a player, you may transform Dowsing Dagger. this.addAbility(new TransformAbility()); - this.addAbility(new DealsDamageToAPlayerAttachedTriggeredAbility(new TransformSourceEffect(true), "equipped", true)); + this.addAbility(new DealsDamageToAPlayerAttachedTriggeredAbility(new TransformSourceEffect(), "equipped", true)); // Equip 2 this.addAbility(new EquipAbility(Outcome.AddAbility, new GenericManaCost(2))); diff --git a/Mage.Sets/src/mage/cards/d/DragonflySuit.java b/Mage.Sets/src/mage/cards/d/DragonflySuit.java new file mode 100644 index 00000000000..8e089112255 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DragonflySuit.java @@ -0,0 +1,40 @@ +package mage.cards.d; + +import mage.MageInt; +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 java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DragonflySuit extends CardImpl { + + public DragonflySuit(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(2); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Crew 1 + this.addAbility(new CrewAbility(1)); + } + + private DragonflySuit(final DragonflySuit card) { + super(card); + } + + @Override + public DragonflySuit copy() { + return new DragonflySuit(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DragonlordAtarka.java b/Mage.Sets/src/mage/cards/d/DragonlordAtarka.java index de33a610004..22792fda8d4 100644 --- a/Mage.Sets/src/mage/cards/d/DragonlordAtarka.java +++ b/Mage.Sets/src/mage/cards/d/DragonlordAtarka.java @@ -22,7 +22,7 @@ import mage.target.common.TargetCreatureOrPlaneswalkerAmount; */ public final class DragonlordAtarka extends CardImpl { - private static final FilterCreatureOrPlaneswalkerPermanent filter = new FilterCreatureOrPlaneswalkerPermanent("target creatures and/or planeswalkers your opponents control"); + private static final FilterCreatureOrPlaneswalkerPermanent filter = new FilterCreatureOrPlaneswalkerPermanent("creatures and/or planeswalkers your opponents control"); static { filter.add(TargetController.OPPONENT.getControllerPredicate()); diff --git a/Mage.Sets/src/mage/cards/d/DragonlordOjutai.java b/Mage.Sets/src/mage/cards/d/DragonlordOjutai.java index a69a829911b..8d9c750fe63 100644 --- a/Mage.Sets/src/mage/cards/d/DragonlordOjutai.java +++ b/Mage.Sets/src/mage/cards/d/DragonlordOjutai.java @@ -42,7 +42,7 @@ public final class DragonlordOjutai extends CardImpl { // Dragonlord Ojutai has hexproof as long as it's untapped. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect( new GainAbilitySourceEffect(HexproofAbility.getInstance(), Duration.WhileOnBattlefield), - new InvertCondition(SourceTappedCondition.instance), + SourceTappedCondition.UNTAPPED, "{this} has hexproof as long as it's untapped"))); // Whenever Dragonlord Ojutai deals combat damage to a player, look at the top three cards of your library. Put one of them into your hand and the rest on the bottom of your library in any order. diff --git a/Mage.Sets/src/mage/cards/d/DragonlordSilumgar.java b/Mage.Sets/src/mage/cards/d/DragonlordSilumgar.java index d9645e8e05e..a83a6fb6a8b 100644 --- a/Mage.Sets/src/mage/cards/d/DragonlordSilumgar.java +++ b/Mage.Sets/src/mage/cards/d/DragonlordSilumgar.java @@ -1,14 +1,8 @@ package mage.cards.d; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.condition.CompoundCondition; -import mage.abilities.condition.common.SourceHasRemainedInSameZoneCondition; -import mage.abilities.condition.common.SourceOnBattlefieldControlUnchangedCondition; -import mage.abilities.decorator.ConditionalContinuousEffect; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.GainControlTargetEffect; import mage.abilities.keyword.DeathtouchAbility; import mage.abilities.keyword.FlyingAbility; @@ -16,19 +10,13 @@ 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.SuperType; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.common.TargetCreatureOrPlaneswalker; -import mage.util.CardUtil; -import mage.util.GameLog; -import mage.watchers.common.LostControlWatcher; + +import java.util.UUID; /** - * * @author jeffwadsworth */ public final class DragonlordSilumgar extends CardImpl { @@ -48,11 +36,9 @@ public final class DragonlordSilumgar extends CardImpl { this.addAbility(DeathtouchAbility.getInstance()); // When Dragonlord Silumgar enters the battlefield, gain control of target creature or planeswalker for as long as you control Dragonlord Silumgar. - Ability ability = new EntersBattlefieldTriggeredAbility(new DragonlordSilumgarEffect(), false); + Ability ability = new EntersBattlefieldTriggeredAbility(new GainControlTargetEffect(Duration.WhileControlled)); ability.addTarget(new TargetCreatureOrPlaneswalker()); - ability.addWatcher(new LostControlWatcher()); this.addAbility(ability); - } private DragonlordSilumgar(final DragonlordSilumgar card) { @@ -64,44 +50,3 @@ public final class DragonlordSilumgar extends CardImpl { return new DragonlordSilumgar(this); } } - -class DragonlordSilumgarEffect extends OneShotEffect { - - public DragonlordSilumgarEffect() { - super(Outcome.GainControl); - this.staticText = "gain control of target creature or planeswalker for as long as you control {this}"; - } - - public DragonlordSilumgarEffect(final DragonlordSilumgarEffect effect) { - super(effect); - } - - @Override - public DragonlordSilumgarEffect copy() { - return new DragonlordSilumgarEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent sourcePermanent = game.getPermanent(source.getSourceId()); - Player controller = game.getPlayer(source.getControllerId()); - Permanent target = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (controller != null && sourcePermanent != null) { - if (target != null && controller.getId().equals(sourcePermanent.getControllerId())) { - SourceHasRemainedInSameZoneCondition condition = new SourceHasRemainedInSameZoneCondition(sourcePermanent.getId()); - - game.addEffect(new ConditionalContinuousEffect( - new GainControlTargetEffect(Duration.Custom), - new CompoundCondition(new SourceOnBattlefieldControlUnchangedCondition(), condition), - null), - source); - if (!game.isSimulation()) { - game.informPlayers(sourcePermanent.getLogName() + ": " + controller.getLogName() + " gained control of " + target.getLogName()); - } - sourcePermanent.addInfo("gained control of", CardUtil.addToolTipMarkTags("Gained control of: " + GameLog.getColoredObjectIdNameForTooltip(target)), game); - } - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/d/DragonsparkReactor.java b/Mage.Sets/src/mage/cards/d/DragonsparkReactor.java new file mode 100644 index 00000000000..38c01c29f8e --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DragonsparkReactor.java @@ -0,0 +1,59 @@ +package mage.cards.d; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.CountersSourceCount; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.target.TargetPlayer; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DragonsparkReactor extends CardImpl { + + private static final DynamicValue xValue = new CountersSourceCount(CounterType.CHARGE); + + public DragonsparkReactor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}{R}"); + + // Whenever Dragonspark Reactor or another artifact enters the battlefield under your control, put a charge counter on Dragonspark Reactor. + this.addAbility(new EntersBattlefieldThisOrAnotherTriggeredAbility( + new AddCountersSourceEffect(CounterType.CHARGE.createInstance()), + StaticFilters.FILTER_PERMANENT_ARTIFACT, false, true + )); + + // {4}, Sacrifice Dragonspark Reactor: It deals damage equal to the number of charge counters on it to target player and that much damage to up to one target creature. + Ability ability = new SimpleActivatedAbility( + new DamageTargetEffect(xValue).setText( + "it deals damage equal to the number of charge counters on it to target player " + + "and that much damage to up to one target creature" + ), new GenericManaCost(4) + ); + ability.addCost(new SacrificeSourceCost()); + ability.addTarget(new TargetPlayer()); + ability.addTarget(new TargetCreaturePermanent(0, 1)); + this.addAbility(ability); + } + + private DragonsparkReactor(final DragonsparkReactor card) { + super(card); + } + + @Override + public DragonsparkReactor copy() { + return new DragonsparkReactor(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DrakeFamiliar.java b/Mage.Sets/src/mage/cards/d/DrakeFamiliar.java index 9f9c46743e6..dd81c36763c 100644 --- a/Mage.Sets/src/mage/cards/d/DrakeFamiliar.java +++ b/Mage.Sets/src/mage/cards/d/DrakeFamiliar.java @@ -64,7 +64,7 @@ class DrakeFamiliarEffect extends OneShotEffect { if (controller == null) { return false; } - TargetPermanent target = new TargetPermanent(StaticFilters.FILTER_ENCHANTMENT_PERMANENT); + TargetPermanent target = new TargetPermanent(StaticFilters.FILTER_PERMANENT_ENCHANTMENT); target.setNotTarget(true); if (target.canChoose(source.getSourceId(), controller.getId(), game) && controller.chooseUse(outcome, "Return an enchantment to its owner's hand?", source, game)) { diff --git a/Mage.Sets/src/mage/cards/d/DramatistsPuppet.java b/Mage.Sets/src/mage/cards/d/DramatistsPuppet.java new file mode 100644 index 00000000000..fa9f33f85e1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DramatistsPuppet.java @@ -0,0 +1,94 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.counters.Counter; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; + +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class DramatistsPuppet extends CardImpl { + + public DramatistsPuppet(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{4}"); + + this.subtype.add(SubType.CONSTRUCT); + this.power = new MageInt(2); + this.toughness = new MageInt(4); + + // When Dramatist's Puppet enters the battlefield, for each kind of counter on target permanent, put another counter of that kind on it or remove one from it. + Ability ability = new EntersBattlefieldTriggeredAbility(new DramatistsPuppetEffect()); + ability.addTarget(new TargetPermanent()); + this.addAbility(ability); + } + + private DramatistsPuppet(final DramatistsPuppet card) { + super(card); + } + + @Override + public DramatistsPuppet copy() { + return new DramatistsPuppet(this); + } +} + +class DramatistsPuppetEffect extends OneShotEffect { + + DramatistsPuppetEffect() { + super(Outcome.Benefit); + this.staticText = "for each kind of counter on target permanent, " + + "put another counter of that kind on it or remove one from it"; + } + + private DramatistsPuppetEffect(final DramatistsPuppetEffect effect) { + super(effect); + } + + @Override + public DramatistsPuppetEffect copy() { + return new DramatistsPuppetEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (controller == null || permanent == null) { + return false; + } + List counterNames = permanent + .getCounters(game) + .values() + .stream() + .map(Counter::getName) + .collect(Collectors.toList()); + for (String counterName : counterNames) { + Counter newCounter = CounterType.findByName(counterName).createInstance(); + if (controller.chooseUse( + Outcome.BoostCreature, "Add or remove a " + counterName + " counter?", + null, "Add", "Remove", source, game + )) { + permanent.addCounters(newCounter, source.getControllerId(), source, game); + } else { + permanent.removeCounters(newCounter, source, game); + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/d/DranaLiberatorOfMalakir.java b/Mage.Sets/src/mage/cards/d/DranaLiberatorOfMalakir.java index 77ebf47494e..53d27bf8d8a 100644 --- a/Mage.Sets/src/mage/cards/d/DranaLiberatorOfMalakir.java +++ b/Mage.Sets/src/mage/cards/d/DranaLiberatorOfMalakir.java @@ -1,4 +1,3 @@ - package mage.cards.d; import java.util.UUID; @@ -22,7 +21,7 @@ import mage.filter.common.FilterAttackingCreature; */ public final class DranaLiberatorOfMalakir extends CardImpl { - private static final FilterAttackingCreature filter = new FilterAttackingCreature("each attacking creature you control"); + private static final FilterAttackingCreature filter = new FilterAttackingCreature("attacking creature you control"); static { filter.add(TargetController.YOU.getControllerPredicate()); diff --git a/Mage.Sets/src/mage/cards/d/DraugrsHelm.java b/Mage.Sets/src/mage/cards/d/DraugrsHelm.java index 0946ff51f3b..bb4bf547580 100644 --- a/Mage.Sets/src/mage/cards/d/DraugrsHelm.java +++ b/Mage.Sets/src/mage/cards/d/DraugrsHelm.java @@ -38,7 +38,10 @@ public final class DraugrsHelm extends CardImpl { // Equipped creature gets +2/+2 and has menace. Ability ability = new SimpleStaticAbility(new BoostEquippedEffect(2, 2)); - ability.addEffect(new GainAbilityAttachedEffect(new MenaceAbility(), AttachmentType.EQUIPMENT).setText("and has menace")); + ability.addEffect(new GainAbilityAttachedEffect( + new MenaceAbility(true), + AttachmentType.EQUIPMENT).setText("and has menace. " + + "(It can't be blocked except by two or more creatures.)")); this.addAbility(ability); // Equip {4} diff --git a/Mage.Sets/src/mage/cards/d/DreadFugue.java b/Mage.Sets/src/mage/cards/d/DreadFugue.java new file mode 100644 index 00000000000..1ef552f24fa --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DreadFugue.java @@ -0,0 +1,49 @@ +package mage.cards.d; + +import java.util.UUID; + +import mage.abilities.Ability; +import mage.abilities.effects.common.discard.DiscardCardYouChooseTargetEffect; +import mage.abilities.keyword.CleaveAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.filter.common.FilterNonlandCard; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.target.TargetPlayer; + +/** + * + * @author weirddan455 + */ +public final class DreadFugue extends CardImpl { + + private static final FilterNonlandCard filter = new FilterNonlandCard("nonland card from it [with mana value 2 or less]"); + + static { + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 3)); + } + + public DreadFugue(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{B}"); + + // Cleave {2}{B} + Ability ability = new CleaveAbility(this, new DiscardCardYouChooseTargetEffect(StaticFilters.FILTER_CARD_NON_LAND, TargetController.ANY), "{2}{B}"); + ability.addTarget(new TargetPlayer()); + this.addAbility(ability); + + // Target player reveals their hand. Choose a nonland card from it [with mana value 2 or less]. That player discards that card. + this.getSpellAbility().addTarget(new TargetPlayer()); + this.getSpellAbility().addEffect(new DiscardCardYouChooseTargetEffect(filter, TargetController.ANY)); + } + + private DreadFugue(final DreadFugue card) { + super(card); + } + + @Override + public DreadFugue copy() { + return new DreadFugue(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DreadSlaver.java b/Mage.Sets/src/mage/cards/d/DreadSlaver.java index 3ec80e1a9c2..286e7c56a4a 100644 --- a/Mage.Sets/src/mage/cards/d/DreadSlaver.java +++ b/Mage.Sets/src/mage/cards/d/DreadSlaver.java @@ -1,4 +1,3 @@ - package mage.cards.d; import java.util.UUID; @@ -69,7 +68,7 @@ class DreadSlaverEffect extends OneShotEffect { if (card != null) { if (controller.moveCards(card, Zone.BATTLEFIELD, source, game)) { ContinuousEffect effect = new BecomesBlackZombieAdditionEffect(); - effect.setTargetPointer(new FixedTarget(card.getId())); + effect.setTargetPointer(new FixedTarget(card.getId(), game)); game.addEffect(effect, source); return true; } diff --git a/Mage.Sets/src/mage/cards/d/DreadSpecter.java b/Mage.Sets/src/mage/cards/d/DreadSpecter.java index fab0464d449..46ea7fd1b12 100644 --- a/Mage.Sets/src/mage/cards/d/DreadSpecter.java +++ b/Mage.Sets/src/mage/cards/d/DreadSpecter.java @@ -1,9 +1,7 @@ - package mage.cards.d; import java.util.UUID; import mage.MageInt; -import mage.ObjectColor; import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility; import mage.abilities.effects.Effect; @@ -13,9 +11,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ColorPredicate; +import mage.filter.StaticFilters; /** * @@ -23,12 +19,6 @@ import mage.filter.predicate.mageobject.ColorPredicate; */ public final class DreadSpecter extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public DreadSpecter(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); this.subtype.add(SubType.SPECTER); @@ -38,7 +28,7 @@ public final class DreadSpecter extends CardImpl { // Whenever Dread Specter blocks or becomes blocked by a nonblack creature, destroy that creature at end of combat. Effect effect = new CreateDelayedTriggeredAbilityEffect(new AtTheEndOfCombatDelayedTriggeredAbility(new DestroyTargetEffect()), true); effect.setText("destroy that creature at end of combat"); - this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(effect, filter, false)); + this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(effect, StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK, false)); } private DreadSpecter(final DreadSpecter card) { diff --git a/Mage.Sets/src/mage/cards/d/DreadWight.java b/Mage.Sets/src/mage/cards/d/DreadWight.java index 132ce1a0e6c..9c2d509f31b 100644 --- a/Mage.Sets/src/mage/cards/d/DreadWight.java +++ b/Mage.Sets/src/mage/cards/d/DreadWight.java @@ -29,7 +29,6 @@ import mage.constants.Zone; import mage.counters.CounterType; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; @@ -88,11 +87,11 @@ class DreadWightTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { if (event.getSourceId().equals(getSourceId())) { // Dread Wight is the blocker - getAllEffects().setTargetPointer(new FixedTarget(event.getTargetId())); + getAllEffects().setTargetPointer(new FixedTarget(event.getTargetId(), game)); return true; } if (event.getTargetId().equals(getSourceId())) { // Dread Wight is the attacker - getAllEffects().setTargetPointer(new FixedTarget(event.getSourceId())); + getAllEffects().setTargetPointer(new FixedTarget(event.getSourceId(), game)); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/d/DreadbringerLampads.java b/Mage.Sets/src/mage/cards/d/DreadbringerLampads.java index c2680f32efb..1c9e5972336 100644 --- a/Mage.Sets/src/mage/cards/d/DreadbringerLampads.java +++ b/Mage.Sets/src/mage/cards/d/DreadbringerLampads.java @@ -1,10 +1,10 @@ - package mage.cards.d; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.abilityword.ConstellationAbility; +import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.keyword.IntimidateAbility; import mage.cards.CardImpl; @@ -28,7 +28,9 @@ public final class DreadbringerLampads extends CardImpl { this.toughness = new MageInt(2); // Constellation - Whenever Dreadbringer Lampads or another enchantment enters the battlefield under your control, target creature gains intimidate until end of turn. - Ability ability = new ConstellationAbility(new GainAbilityTargetEffect(IntimidateAbility.getInstance(), Duration.EndOfTurn), false); + Effect effect = new GainAbilityTargetEffect(IntimidateAbility.getInstance(), Duration.EndOfTurn); + effect.setText("target creature gains intimidate until end of turn. (It can't be blocked except by artifact creatures and/or creatures that share a color with it.)"); + Ability ability = new ConstellationAbility(effect); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/d/DreadfeastDemon.java b/Mage.Sets/src/mage/cards/d/DreadfeastDemon.java new file mode 100644 index 00000000000..df5ee28e867 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DreadfeastDemon.java @@ -0,0 +1,60 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.effects.CreateTokenCopySourceEffect; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DreadfeastDemon extends CardImpl { + + private static final FilterControlledPermanent filter + = new FilterControlledCreaturePermanent("non-Demon creature"); + + static { + filter.add(Predicates.not(SubType.DEMON.getPredicate())); + } + + public DreadfeastDemon(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{B}{B}"); + + this.subtype.add(SubType.DEMON); + this.power = new MageInt(6); + this.toughness = new MageInt(6); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // At the beginning of your end step, sacrifice a non-Demon creature. If you do, create a token that's a copy of Dreadfeast Demon. + this.addAbility(new BeginningOfEndStepTriggeredAbility(new DoIfCostPaid( + new CreateTokenCopySourceEffect(), + new SacrificeTargetCost( + new TargetControlledPermanent(filter) + ), null, false + ), TargetController.YOU, false)); + } + + private DreadfeastDemon(final DreadfeastDemon card) { + super(card); + } + + @Override + public DreadfeastDemon copy() { + return new DreadfeastDemon(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DreadlightMonstrosity.java b/Mage.Sets/src/mage/cards/d/DreadlightMonstrosity.java new file mode 100644 index 00000000000..b4cdbe27ed4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DreadlightMonstrosity.java @@ -0,0 +1,78 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.ActivateIfConditionActivatedAbility; +import mage.abilities.condition.Condition; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.combat.CantBeBlockedSourceEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.abilities.keyword.WardAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.Game; + +import java.util.Collection; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DreadlightMonstrosity extends CardImpl { + + private static final Hint hint = new ConditionHint( + DreadlightMonstrosityCondition.instance, "You own a card in exile" + ); + + public DreadlightMonstrosity(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}{U}"); + + this.subtype.add(SubType.CRAB); + this.subtype.add(SubType.HORROR); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + + // Ward {2} + this.addAbility(new WardAbility(new ManaCostsImpl<>("{2}"))); + + // {3}{U}{U}: Dreadlight Monstrosity can't be blocked this turn. Activate only if you own a card in exile. + this.addAbility(new ActivateIfConditionActivatedAbility( + Zone.BATTLEFIELD, new CantBeBlockedSourceEffect(Duration.EndOfTurn), + new ManaCostsImpl<>("{3}{U}{U}"), DreadlightMonstrosityCondition.instance + ).addHint(hint)); + } + + private DreadlightMonstrosity(final DreadlightMonstrosity card) { + super(card); + } + + @Override + public DreadlightMonstrosity copy() { + return new DreadlightMonstrosity(this); + } +} + +enum DreadlightMonstrosityCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + return game + .getExile() + .getExileZones() + .stream() + .flatMap(Collection::stream) + .map(game::getOwnerId) + .anyMatch(source::isControlledBy); + } + + @Override + public String toString() { + return "you own a card in exile"; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/d/DreamHalls.java b/Mage.Sets/src/mage/cards/d/DreamHalls.java index 19928b062e8..51b608af534 100644 --- a/Mage.Sets/src/mage/cards/d/DreamHalls.java +++ b/Mage.Sets/src/mage/cards/d/DreamHalls.java @@ -46,7 +46,7 @@ class DreamHallsEffect extends ContinuousEffectImpl { static { filter.add(new AnotherCardPredicate()); - filter.add(new SharesColorWithSourcePredicate()); + filter.add(SharesColorWithSourcePredicate.instance); } private final AlternativeCostSourceAbility alternativeCastingCostAbility = new AlternativeCostSourceAbility(new DiscardCardCost(filter), SourceIsSpellCondition.instance); diff --git a/Mage.Sets/src/mage/cards/d/DreamLeash.java b/Mage.Sets/src/mage/cards/d/DreamLeash.java index f0f15cdb1b7..5b600319397 100644 --- a/Mage.Sets/src/mage/cards/d/DreamLeash.java +++ b/Mage.Sets/src/mage/cards/d/DreamLeash.java @@ -29,6 +29,7 @@ public final class DreamLeash extends CardImpl { // Enchant permanent TargetPermanent auraTarget = new TargetTappedPermanentAsYouCast(); + auraTarget.withChooseHint("must be tapped"); this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.GainControl)); Ability ability = new EnchantAbility(auraTarget.getTargetName()); diff --git a/Mage.Sets/src/mage/cards/d/Dreamcatcher.java b/Mage.Sets/src/mage/cards/d/Dreamcatcher.java index 7d956d2bc51..50bb02f00fd 100644 --- a/Mage.Sets/src/mage/cards/d/Dreamcatcher.java +++ b/Mage.Sets/src/mage/cards/d/Dreamcatcher.java @@ -27,7 +27,7 @@ public final class Dreamcatcher extends CardImpl { this.toughness = new MageInt(1); // Whenever you cast a Spirit or Arcane spell, you may sacrifice Dreamcatcher. If you do, draw a card. - Ability ability = new SpellCastControllerTriggeredAbility(new SacrificeSourceEffect(), StaticFilters.SPIRIT_OR_ARCANE_CARD, true, + Ability ability = new SpellCastControllerTriggeredAbility(new SacrificeSourceEffect(), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true, "Whenever you cast a Spirit or Arcane spell, you may sacrifice {this}. If you do, draw a card."); ability.addEffect(new DrawCardSourceControllerEffect(1)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/d/DreamrootCascade.java b/Mage.Sets/src/mage/cards/d/DreamrootCascade.java new file mode 100644 index 00000000000..a6aa9df2986 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DreamrootCascade.java @@ -0,0 +1,49 @@ +package mage.cards.d; + +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.TapSourceEffect; +import mage.abilities.mana.BlueManaAbility; +import mage.abilities.mana.GreenManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DreamrootCascade extends CardImpl { + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition( + StaticFilters.FILTER_LANDS, ComparisonType.FEWER_THAN, 2 + ); + + public DreamrootCascade(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // Dreamroot Cascade enters the battlefield tapped unless you control two or more other lands. + this.addAbility(new EntersBattlefieldAbility( + new ConditionalOneShotEffect(new TapSourceEffect(), condition), + "tapped unless you control two or more other lands" + )); + + // {T}: Add {G} or {U}. + this.addAbility(new GreenManaAbility()); + this.addAbility(new BlueManaAbility()); + } + + private DreamrootCascade(final DreamrootCascade card) { + super(card); + } + + @Override + public DreamrootCascade copy() { + return new DreamrootCascade(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DreamshackleGeist.java b/Mage.Sets/src/mage/cards/d/DreamshackleGeist.java new file mode 100644 index 00000000000..d09ba16cba1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DreamshackleGeist.java @@ -0,0 +1,58 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.BeginningOfCombatTriggeredAbility; +import mage.abilities.effects.common.DontUntapInControllersNextUntapStepTargetEffect; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DreamshackleGeist extends CardImpl { + + public DreamshackleGeist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{U}"); + + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(3); + this.toughness = new MageInt(1); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // At the beginning of combat on your turn, choose up to one — + // • Tap target creature. + Ability ability = new BeginningOfCombatTriggeredAbility( + new TapTargetEffect(), TargetController.YOU, false + ); + ability.addTarget(new TargetCreaturePermanent()); + ability.getModes().setMinModes(0); + ability.getModes().setMaxModes(1); + + // • Target creature doesn't untap during its controller's next untap step. + Mode mode = new Mode(new DontUntapInControllersNextUntapStepTargetEffect("target creature")); + mode.addTarget(new TargetCreaturePermanent()); + ability.addMode(mode); + this.addAbility(ability); + } + + private DreamshackleGeist(final DreamshackleGeist card) { + super(card); + } + + @Override + public DreamshackleGeist copy() { + return new DreamshackleGeist(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/Dreamstealer.java b/Mage.Sets/src/mage/cards/d/Dreamstealer.java index 1258c57c59d..7fea7af4f36 100644 --- a/Mage.Sets/src/mage/cards/d/Dreamstealer.java +++ b/Mage.Sets/src/mage/cards/d/Dreamstealer.java @@ -32,7 +32,7 @@ public final class Dreamstealer extends CardImpl { this.toughness = new MageInt(2); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // When Dreamstealer deals combat damage to a player, that player discards that many cards. this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new DreamstealerDiscardEffect(), false, true)); diff --git a/Mage.Sets/src/mage/cards/d/DregsOfSorrow.java b/Mage.Sets/src/mage/cards/d/DregsOfSorrow.java index 4861a8bae28..15d6e4b949c 100644 --- a/Mage.Sets/src/mage/cards/d/DregsOfSorrow.java +++ b/Mage.Sets/src/mage/cards/d/DregsOfSorrow.java @@ -1,6 +1,5 @@ package mage.cards.d; -import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.dynamicvalue.common.ManacostVariableValue; import mage.abilities.effects.common.DestroyTargetEffect; @@ -8,9 +7,7 @@ import mage.abilities.effects.common.DrawCardSourceControllerEffect; 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.game.Game; import mage.target.common.TargetCreaturePermanent; import mage.target.targetadjustment.TargetAdjuster; @@ -25,7 +22,6 @@ public final class DregsOfSorrow extends CardImpl { public DregsOfSorrow(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{4}{B}"); - // Destroy X target nonblack creatures. Draw X cards. this.getSpellAbility().addEffect(new DestroyTargetEffect("Destroy X target nonblack creatures")); this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(ManacostVariableValue.REGULAR)); @@ -44,16 +40,11 @@ public final class DregsOfSorrow extends CardImpl { enum DregsOfSorrowAdjuster implements TargetAdjuster { instance; - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creatures"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } @Override public void adjustTargets(Ability ability, Game game) { ability.getTargets().clear(); int xValue = ability.getManaCostsToPay().getX(); - ability.addTarget(new TargetCreaturePermanent(xValue, xValue, filter, false)); + ability.addTarget(new TargetCreaturePermanent(xValue, xValue, StaticFilters.FILTER_PERMANENT_CREATURES_NON_BLACK, false)); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/d/DrogskolArmaments.java b/Mage.Sets/src/mage/cards/d/DrogskolArmaments.java new file mode 100644 index 00000000000..6cc3230caf2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DrogskolArmaments.java @@ -0,0 +1,54 @@ +package mage.cards.d; + +import mage.abilities.Ability; +import mage.abilities.common.PutIntoGraveFromAnywhereSourceAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.ExileSourceEffect; +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.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DrogskolArmaments extends CardImpl { + + public DrogskolArmaments(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, ""); + + this.subtype.add(SubType.AURA); + this.color.setWhite(true); + this.nightCard = true; + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // Enchanted creature gets +2/+2. + this.addAbility(new SimpleStaticAbility(new BoostEnchantedEffect(2, 2))); + + // If Drogskol Armaments would be put into a graveyard from anywhere, exile it instead. + this.addAbility(new PutIntoGraveFromAnywhereSourceAbility(new ExileSourceEffect().setText("exile it instead"))); + } + + private DrogskolArmaments(final DrogskolArmaments card) { + super(card); + } + + @Override + public DrogskolArmaments copy() { + return new DrogskolArmaments(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DrogskolCaptain.java b/Mage.Sets/src/mage/cards/d/DrogskolCaptain.java index 9ec6bd1fb88..33ff8aefc19 100644 --- a/Mage.Sets/src/mage/cards/d/DrogskolCaptain.java +++ b/Mage.Sets/src/mage/cards/d/DrogskolCaptain.java @@ -22,7 +22,7 @@ import mage.filter.common.FilterCreaturePermanent; */ public final class DrogskolCaptain extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(SubType.SPIRIT, "Spirits"); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(SubType.SPIRIT, "Spirit creatures"); public DrogskolCaptain(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{U}"); diff --git a/Mage.Sets/src/mage/cards/d/DrogskolInfantry.java b/Mage.Sets/src/mage/cards/d/DrogskolInfantry.java new file mode 100644 index 00000000000..c83c5ccffe5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DrogskolInfantry.java @@ -0,0 +1,39 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.keyword.DisturbAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DrogskolInfantry extends CardImpl { + + public DrogskolInfantry(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.subtype.add(SubType.SPIRIT); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + this.secondSideCardClazz = mage.cards.d.DrogskolArmaments.class; + + // Disturb {3}{W} + this.addAbility(new DisturbAbility(this, "{3}{W}")); + } + + private DrogskolInfantry(final DrogskolInfantry card) { + super(card); + } + + @Override + public DrogskolInfantry copy() { + return new DrogskolInfantry(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DrogskolReinforcements.java b/Mage.Sets/src/mage/cards/d/DrogskolReinforcements.java new file mode 100644 index 00000000000..50fe982f001 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DrogskolReinforcements.java @@ -0,0 +1,56 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.PreventAllNonCombatDamageToAllEffect; +import mage.abilities.effects.common.continuous.GainAbilityAllEffect; +import mage.abilities.keyword.MeleeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DrogskolReinforcements extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledPermanent(SubType.SPIRIT, "Spirits you control"); + + public DrogskolReinforcements(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); + + this.subtype.add(SubType.SPIRIT); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Melee + this.addAbility(new MeleeAbility()); + + // Other Spirits you control have melee. + this.addAbility(new SimpleStaticAbility(new GainAbilityAllEffect( + new MeleeAbility(), Duration.WhileOnBattlefield, filter, true + ).setText("other Spirits you control have melee"))); + + // Prevent all noncombat damage that would be dealt to Spirits you control. + this.addAbility(new SimpleStaticAbility( + new PreventAllNonCombatDamageToAllEffect(Duration.WhileOnBattlefield, filter) + )); + } + + private DrogskolReinforcements(final DrogskolReinforcements card) { + super(card); + } + + @Override + public DrogskolReinforcements copy() { + return new DrogskolReinforcements(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DroolingOgre.java b/Mage.Sets/src/mage/cards/d/DroolingOgre.java index 6c681bf5736..9f913643786 100644 --- a/Mage.Sets/src/mage/cards/d/DroolingOgre.java +++ b/Mage.Sets/src/mage/cards/d/DroolingOgre.java @@ -62,9 +62,11 @@ public final class DroolingOgre extends CardImpl { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); Player newController = game.getPlayer(this.getTargetPointer().getFirst(game, source)); - if (newController != null && controller != null && !controller.equals(newController)) { + if (newController != null + && controller != null + && !controller.equals(newController)) { ContinuousEffect effect = new GainControlTargetEffect(Duration.Custom, newController.getId()); - effect.setTargetPointer(new FixedTarget(source.getSourceId())); + effect.setTargetPointer(new FixedTarget(source.getSourceId(), game)); game.addEffect(effect, source); return true; } diff --git a/Mage.Sets/src/mage/cards/d/DrudgeSpell.java b/Mage.Sets/src/mage/cards/d/DrudgeSpell.java index d559bace2e7..c4632f2ca09 100644 --- a/Mage.Sets/src/mage/cards/d/DrudgeSpell.java +++ b/Mage.Sets/src/mage/cards/d/DrudgeSpell.java @@ -1,4 +1,3 @@ - package mage.cards.d; import java.util.UUID; @@ -6,17 +5,18 @@ import mage.abilities.Ability; import mage.abilities.common.LeavesBattlefieldTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.ExileFromGraveCost; -import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.costs.mana.ColoredManaCost; import mage.abilities.effects.Effect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.DestroyAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.ColoredManaSymbol; import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterPermanent; -import mage.filter.common.FilterCreatureCard; +import mage.filter.StaticFilters; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.permanent.token.SkeletonRegenerateToken; import mage.target.common.TargetCardInYourGraveyard; @@ -40,8 +40,8 @@ public final class DrudgeSpell extends CardImpl { // {B}, Exile two creature cards from your graveyard: Create a 1/1 black Skeleton creature token. It has "{B}: Regenerate this creature." Effect effect = new CreateTokenEffect(new SkeletonRegenerateToken()); effect.setText("create a 1/1 black Skeleton creature token. It has \"{B}: Regenerate this creature.\""); - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl("{B}")); - ability.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard(2, 2, new FilterCreatureCard("creature cards from your graveyard")))); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new ColoredManaCost(ColoredManaSymbol.B)); + ability.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard(2, 2, StaticFilters.FILTER_CARD_CREATURES_YOUR_GRAVEYARD))); this.addAbility(ability); // When Drudge Spell leaves the battlefield, destroy all Skeleton tokens. They can't be regenerated. diff --git a/Mage.Sets/src/mage/cards/d/Drumbellower.java b/Mage.Sets/src/mage/cards/d/Drumbellower.java new file mode 100644 index 00000000000..4a401682379 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/Drumbellower.java @@ -0,0 +1,42 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.continuous.UntapAllDuringEachOtherPlayersUntapStepEffect; +import mage.abilities.keyword.FlyingAbility; +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 Drumbellower extends CardImpl { + + public Drumbellower(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Untap all creatures you control during each other player's untap step. + this.addAbility(new SimpleStaticAbility(new UntapAllDuringEachOtherPlayersUntapStepEffect(StaticFilters.FILTER_CONTROLLED_CREATURES))); + } + + private Drumbellower(final Drumbellower card) { + super(card); + } + + @Override + public Drumbellower copy() { + return new Drumbellower(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DualNature.java b/Mage.Sets/src/mage/cards/d/DualNature.java index 8da8ffbb1df..fa69caff5f9 100644 --- a/Mage.Sets/src/mage/cards/d/DualNature.java +++ b/Mage.Sets/src/mage/cards/d/DualNature.java @@ -97,7 +97,7 @@ class DualNatureCreateTokenEffect extends OneShotEffect { } else { tokensCreated = new HashSet<>(); } - for (Permanent perm : effect.getAddedPermanent()) { + for (Permanent perm : effect.getAddedPermanents()) { if (perm != null) { tokensCreated.add(perm.getId()); } diff --git a/Mage.Sets/src/mage/cards/d/DueRespect.java b/Mage.Sets/src/mage/cards/d/DueRespect.java index a80de2ce016..a24a7039517 100644 --- a/Mage.Sets/src/mage/cards/d/DueRespect.java +++ b/Mage.Sets/src/mage/cards/d/DueRespect.java @@ -1,33 +1,30 @@ - package mage.cards.d; -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.PermanentsEnterBattlefieldTappedEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Outcome; -import mage.game.Game; -import mage.game.events.EntersTheBattlefieldEvent; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * * @author North */ public final class DueRespect extends CardImpl { public DueRespect(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}"); // Permanents enter the battlefield tapped this turn. - this.getSpellAbility().addEffect(new DueRespectEffect()); + this.getSpellAbility().addEffect(new PermanentsEnterBattlefieldTappedEffect( + StaticFilters.FILTER_PERMANENTS, Duration.EndOfTurn + )); + // Draw a card. - this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("
")); } private DueRespect(final DueRespect card) { @@ -39,40 +36,3 @@ public final class DueRespect extends CardImpl { return new DueRespect(this); } } - -class DueRespectEffect extends ReplacementEffectImpl { - - DueRespectEffect() { - super(Duration.EndOfTurn, Outcome.Tap); - staticText = "Permanents enter the battlefield tapped this turn"; - } - - DueRespectEffect(final DueRespectEffect effect) { - super(effect); - } - - @Override - public DueRespectEffect copy() { - return new DueRespectEffect(this); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget(); - if (permanent != null) { - permanent.tap(source, game); - } - return false; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - return true; - } - -} diff --git a/Mage.Sets/src/mage/cards/d/DuelingCoach.java b/Mage.Sets/src/mage/cards/d/DuelingCoach.java index 61ef28bf2e8..1ebf027d3c4 100644 --- a/Mage.Sets/src/mage/cards/d/DuelingCoach.java +++ b/Mage.Sets/src/mage/cards/d/DuelingCoach.java @@ -13,8 +13,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.counters.CounterType; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; import java.util.UUID; @@ -24,13 +23,6 @@ import java.util.UUID; */ public final class DuelingCoach extends CardImpl { - private static final FilterPermanent filter - = new FilterControlledCreaturePermanent("creature you control with a +1/+1 counter on it"); - - static { - filter.add(CounterType.P1P1.getPredicate()); - } - public DuelingCoach(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); @@ -48,7 +40,10 @@ public final class DuelingCoach extends CardImpl { // {4}{W}, {T}: Put a +1/+1 counter on each creature you control with a +1/+1 counter on it. ability = new SimpleActivatedAbility( - new AddCountersAllEffect(CounterType.P1P1.createInstance(), filter), new ManaCostsImpl("{4}{W}") + new AddCountersAllEffect( + CounterType.P1P1.createInstance(), + StaticFilters.FILTER_CONTROLLED_CREATURE_P1P1), + new ManaCostsImpl("{4}{W}") ); ability.addCost(new TapSourceCost()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/d/DuergarMineCaptain.java b/Mage.Sets/src/mage/cards/d/DuergarMineCaptain.java index e2594870874..5b2f3ae823f 100644 --- a/Mage.Sets/src/mage/cards/d/DuergarMineCaptain.java +++ b/Mage.Sets/src/mage/cards/d/DuergarMineCaptain.java @@ -1,4 +1,3 @@ - package mage.cards.d; import java.util.UUID; @@ -13,8 +12,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.Zone; -import mage.filter.common.FilterAttackingCreature; +import mage.filter.StaticFilters; /** * @@ -32,8 +30,8 @@ public final class DuergarMineCaptain extends CardImpl { this.toughness = new MageInt(1); // {1}{RW}, {untap}: Attacking creatures get +1/+0 until end of turn. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, - new BoostAllEffect(1, 0, Duration.EndOfTurn, new FilterAttackingCreature("attacking creatures"), false), + Ability ability = new SimpleActivatedAbility( + new BoostAllEffect(1, 0, Duration.EndOfTurn, StaticFilters.FILTER_ATTACKING_CREATURES, false), new ManaCostsImpl("{1}{R/W}") ); ability.addCost(new UntapSourceCost()); diff --git a/Mage.Sets/src/mage/cards/d/DungeonGeists.java b/Mage.Sets/src/mage/cards/d/DungeonGeists.java index 00d6c192736..f15cecd6eb5 100644 --- a/Mage.Sets/src/mage/cards/d/DungeonGeists.java +++ b/Mage.Sets/src/mage/cards/d/DungeonGeists.java @@ -1,40 +1,33 @@ - package mage.cards.d; -import java.util.UUID; import mage.MageInt; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; +import mage.abilities.effects.common.DontUntapInControllersUntapStepTargetEffect; import mage.abilities.effects.common.TapTargetEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.filter.common.FilterCreaturePermanent; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; -import mage.target.Target; import mage.target.common.TargetCreaturePermanent; -import mage.watchers.Watcher; + +import java.util.UUID; /** - * * @author BetaSteward */ public final class DungeonGeists extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public DungeonGeists(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}{U}"); this.subtype.add(SubType.SPIRIT); this.power = new MageInt(3); @@ -44,11 +37,9 @@ public final class DungeonGeists extends CardImpl { // When Dungeon Geists enters the battlefield, tap target creature an opponent controls. That creature doesn't untap during its controller's untap step for as long as you control Dungeon Geists. Ability ability = new EntersBattlefieldTriggeredAbility(new TapTargetEffect(), false); - ability.addEffect(new DungeonGeistsEffect()); - Target target = new TargetCreaturePermanent(filter); - ability.addTarget(target); - this.addAbility(ability, new DungeonGeistsWatcher()); - // watcher needed to send normal events to Dungeon Geists ReplacementEffect + ability.addEffect(new DontUntapInControllersUntapStepTargetEffect(Duration.WhileControlled)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); + this.addAbility(ability); } private DungeonGeists(final DungeonGeists card) { @@ -60,101 +51,3 @@ public final class DungeonGeists extends CardImpl { return new DungeonGeists(this); } } - -class DungeonGeistsEffect extends ContinuousRuleModifyingEffectImpl { - - public DungeonGeistsEffect() { - super(Duration.Custom, Outcome.Detriment, false, false); - this.staticText = "That creature doesn't untap during its controller's untap step for as long as you control {this}"; - } - - public DungeonGeistsEffect(final DungeonGeistsEffect effect) { - super(effect); - } - - @Override - public DungeonGeistsEffect copy() { - return new DungeonGeistsEffect(this); - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.UNTAP - || event.getType() == GameEvent.EventType.ZONE_CHANGE - || event.getType() == GameEvent.EventType.LOST_CONTROL; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - // Source must be on the battlefield (it's neccessary to check here because if as response to the enter - // the battlefield triggered ability the source dies (or will be exiled), then the ZONE_CHANGE or LOST_CONTROL - // event will happen before this effect is applied ever) - MageObject sourceObject = source.getSourceObjectIfItStillExists(game); - if (!(sourceObject instanceof Permanent) || !((Permanent) sourceObject).isControlledBy(source.getControllerId())) { - discard(); - return false; - } - switch (event.getType()) { - case ZONE_CHANGE: - // end effect if source does a zone move - if (event.getTargetId().equals(source.getSourceId())) { - ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - if (zEvent.getFromZone() == Zone.BATTLEFIELD) { - discard(); - return false; - } - } - break; - case UNTAP: - // prevent to untap the target creature - if (game.getTurn().getStepType() == PhaseStep.UNTAP && event.getTargetId().equals(targetPointer.getFirst(game, source))) { - Permanent targetCreature = game.getPermanent(targetPointer.getFirst(game, source)); - if (targetCreature != null) { - return targetCreature.isControlledBy(game.getActivePlayerId()); - } else { - discard(); - return false; - } - } - break; - case LOST_CONTROL: - // end effect if source control is changed - if (event.getTargetId().equals(source.getSourceId())) { - discard(); - return false; - } - break; - } - return false; - } -} - -class DungeonGeistsWatcher extends Watcher { - - DungeonGeistsWatcher() { - super(WatcherScope.CARD); - } - - @Override - public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.LOST_CONTROL - && event.getPlayerId().equals(controllerId) - && event.getTargetId().equals(sourceId)) { - condition = true; - game.replaceEvent(event); - return; - } - if (event.getType() == GameEvent.EventType.ZONE_CHANGE && event.getTargetId().equals(sourceId)) { - ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - if (zEvent.getFromZone() == Zone.BATTLEFIELD) { - condition = true; - game.replaceEvent(event); - } - } - } - - @Override - public void reset() { - //don't reset condition each turn - only when this leaves the battlefield - } -} diff --git a/Mage.Sets/src/mage/cards/d/DurableHandicraft.java b/Mage.Sets/src/mage/cards/d/DurableHandicraft.java index 16b8d2e76b4..1ea08e05aff 100644 --- a/Mage.Sets/src/mage/cards/d/DurableHandicraft.java +++ b/Mage.Sets/src/mage/cards/d/DurableHandicraft.java @@ -1,14 +1,11 @@ - package mage.cards.d; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.DoIfCostPaid; import mage.abilities.effects.common.counter.AddCountersAllEffect; import mage.abilities.effects.common.counter.AddCountersTargetEffect; @@ -18,27 +15,33 @@ import mage.constants.CardType; import mage.constants.SetTargetPointer; import mage.constants.Zone; import mage.counters.CounterType; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * * @author spjspj */ public final class DurableHandicraft extends CardImpl { public DurableHandicraft(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{G}"); // Whenever a creature enters the battlefield under your control, you may pay {1}. If you do, put a +1/+1 counter on that creature. - Effect effect = new AddCountersTargetEffect(CounterType.P1P1.createInstance()); - effect.setText("put a +1/+1 counter on that creature"); - DoIfCostPaid doIfCostPaid = new DoIfCostPaid(effect, new GenericManaCost(1)); - this.addAbility(new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD, doIfCostPaid, new FilterControlledCreaturePermanent(), false, SetTargetPointer.PERMANENT, null)); + this.addAbility(new EntersBattlefieldControlledTriggeredAbility( + Zone.BATTLEFIELD, + new DoIfCostPaid( + new AddCountersTargetEffect(CounterType.P1P1.createInstance()) + .setText("put a +1/+1 counter on that creature"), + new GenericManaCost(1) + ), StaticFilters.FILTER_PERMANENT_A_CREATURE, + false, SetTargetPointer.PERMANENT, null + )); // {5}{G}, Sacrifice Durable Handicraft: Put a +1/+1 counter on each creature you control. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, - new AddCountersAllEffect(CounterType.P1P1.createInstance(), new FilterControlledCreaturePermanent()), - new ManaCostsImpl("{5}{G}")); + Ability ability = new SimpleActivatedAbility(new AddCountersAllEffect( + CounterType.P1P1.createInstance(), StaticFilters.FILTER_CONTROLLED_CREATURE + ), new ManaCostsImpl<>("{5}{G}")); ability.addCost(new SacrificeSourceCost()); this.addAbility(ability); } @@ -52,4 +55,3 @@ public final class DurableHandicraft extends CardImpl { return new DurableHandicraft(this); } } - diff --git a/Mage.Sets/src/mage/cards/d/DuskmantleSeer.java b/Mage.Sets/src/mage/cards/d/DuskmantleSeer.java index 598a361a174..db205d42081 100644 --- a/Mage.Sets/src/mage/cards/d/DuskmantleSeer.java +++ b/Mage.Sets/src/mage/cards/d/DuskmantleSeer.java @@ -1,24 +1,21 @@ - package mage.cards.d; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.FlyingAbility; -import mage.cards.*; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.SubType; -import mage.constants.TargetController; -import mage.constants.Zone; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; +import mage.constants.*; import mage.game.Game; -import mage.game.permanent.Permanent; import mage.players.Player; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class DuskmantleSeer extends CardImpl { @@ -33,9 +30,11 @@ public final class DuskmantleSeer extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); - // At the beginning of your upkeep, each player reveals the top card of their library, loses life equal to that card's converted mana cost, then puts it into their hand. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new DuskmantleSeerEffect(), TargetController.YOU, false, false)); + // At the beginning of your upkeep, each player reveals the top card of their library, loses life equal to that card's converted mana cost, then puts it into their hand. + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new DuskmantleSeerEffect(), TargetController.YOU, false + )); } private DuskmantleSeer(final DuskmantleSeer card) { @@ -52,7 +51,8 @@ class DuskmantleSeerEffect extends OneShotEffect { public DuskmantleSeerEffect() { super(Outcome.Detriment); - this.staticText = "each player reveals the top card of their library, loses life equal to that card's mana value, then puts it into their hand"; + this.staticText = "each player reveals the top card of their library, " + + "loses life equal to that card's mana value, then puts it into their hand"; } public DuskmantleSeerEffect(final DuskmantleSeerEffect effect) { @@ -66,19 +66,18 @@ class DuskmantleSeerEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent sourceCard = game.getPermanentOrLKIBattlefield(source.getSourceId()); - if (sourceCard == null) { - return false; - } - for (Player player : game.getPlayers().values()) { - if (player.getLibrary().hasCards()) { - Card card = player.getLibrary().getFromTop(game); - if (card != null) { - player.revealCards(source, ": Revealed by " + player.getName(), new CardsImpl(card), game); - player.loseLife(card.getManaValue(), game, source, false); - player.moveCards(card, Zone.HAND, source, game); - } + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + if (player == null) { + continue; } + Card card = player.getLibrary().getFromTop(game); + if (card == null) { + continue; + } + player.revealCards(source, new CardsImpl(card), game); + player.loseLife(card.getManaValue(), game, source, false); + player.moveCards(card, Zone.HAND, source, game); } return true; } diff --git a/Mage.Sets/src/mage/cards/d/DuskshellCrawler.java b/Mage.Sets/src/mage/cards/d/DuskshellCrawler.java index 25a79751481..97e828ce304 100644 --- a/Mage.Sets/src/mage/cards/d/DuskshellCrawler.java +++ b/Mage.Sets/src/mage/cards/d/DuskshellCrawler.java @@ -5,7 +5,7 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.effects.common.continuous.GainAbilityAllEffect; import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.abilities.keyword.TrampleAbility; import mage.constants.Duration; @@ -14,7 +14,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.counters.CounterType; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; /** @@ -23,12 +23,6 @@ import mage.target.common.TargetCreaturePermanent; */ public final class DuskshellCrawler extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); - - static { - filter.add(CounterType.P1P1.getPredicate()); - } - public DuskshellCrawler(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); @@ -43,9 +37,12 @@ public final class DuskshellCrawler extends CardImpl { // Each creature you control with a +1/+1 counter on it has trample. this.addAbility(new SimpleStaticAbility( - new GainAbilityControlledEffect(TrampleAbility.getInstance(), Duration.WhileOnBattlefield, filter) - .setText("Each creature you control with a +1/+1 counter on it has trample") - )); + new GainAbilityAllEffect( + TrampleAbility.getInstance(), + Duration.WhileOnBattlefield, + StaticFilters.FILTER_EACH_CONTROLLED_CREATURE_P1P1) + ) + ); } private DuskshellCrawler(final DuskshellCrawler card) { diff --git a/Mage.Sets/src/mage/cards/d/DuskwatchRecruiter.java b/Mage.Sets/src/mage/cards/d/DuskwatchRecruiter.java index ec4aa4d065a..b7ea9403b6a 100644 --- a/Mage.Sets/src/mage/cards/d/DuskwatchRecruiter.java +++ b/Mage.Sets/src/mage/cards/d/DuskwatchRecruiter.java @@ -3,21 +3,15 @@ package mage.cards.d; import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.WerewolfFrontTriggeredAbility; -import mage.abilities.condition.common.NoSpellsWereCastLastTurnCondition; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.LookLibraryAndPickControllerEffect; -import mage.abilities.effects.common.TransformSourceEffect; import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.common.FilterCreatureCard; @@ -34,7 +28,6 @@ public final class DuskwatchRecruiter extends CardImpl { this.subtype.add(SubType.WARRIOR); this.subtype.add(SubType.WEREWOLF); - this.transformable = true; this.secondSideCardClazz = mage.cards.k.KrallenhordeHowler.class; this.power = new MageInt(2); diff --git a/Mage.Sets/src/mage/cards/d/DutifulReturn.java b/Mage.Sets/src/mage/cards/d/DutifulReturn.java index 33577242618..8bd52baff68 100644 --- a/Mage.Sets/src/mage/cards/d/DutifulReturn.java +++ b/Mage.Sets/src/mage/cards/d/DutifulReturn.java @@ -1,4 +1,3 @@ - package mage.cards.d; import java.util.UUID; @@ -6,7 +5,7 @@ import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterCreatureCard; +import mage.filter.StaticFilters; import mage.target.common.TargetCardInYourGraveyard; /** @@ -18,10 +17,9 @@ public final class DutifulReturn extends CardImpl { public DutifulReturn(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{B}"); - // Return up to two target creature cards from your graveyard to your hand. this.getSpellAbility().addEffect(new ReturnFromGraveyardToHandTargetEffect()); - this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, 2, new FilterCreatureCard("creature cards from your graveyard"))); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, 2, StaticFilters.FILTER_CARD_CREATURES_YOUR_GRAVEYARD)); } private DutifulReturn(final DutifulReturn card) { diff --git a/Mage.Sets/src/mage/cards/d/DwarvenArmorer.java b/Mage.Sets/src/mage/cards/d/DwarvenArmorer.java index 97b1350ec10..804dc67357b 100644 --- a/Mage.Sets/src/mage/cards/d/DwarvenArmorer.java +++ b/Mage.Sets/src/mage/cards/d/DwarvenArmorer.java @@ -91,7 +91,7 @@ class DwarvenArmorerEffect extends OneShotEffect { if (controller.choose(outcome, choice, game)) { Counter counter = choice.getChoice().equals("+0/+1") ? CounterType.P0P1.createInstance() : CounterType.P1P0.createInstance(); Effect effect = new AddCountersTargetEffect(counter); - effect.setTargetPointer(new FixedTarget(this.getTargetPointer().getFirst(game, source))); + effect.setTargetPointer(new FixedTarget(this.getTargetPointer().getFirst(game, source), game)); return effect.apply(game, source); } } diff --git a/Mage.Sets/src/mage/cards/d/DwarvenHold.java b/Mage.Sets/src/mage/cards/d/DwarvenHold.java index b3544f5b91b..822e82d82eb 100644 --- a/Mage.Sets/src/mage/cards/d/DwarvenHold.java +++ b/Mage.Sets/src/mage/cards/d/DwarvenHold.java @@ -39,7 +39,7 @@ public final class DwarvenHold extends CardImpl { this.addAbility(new SkipUntapOptionalAbility()); // At the beginning of your upkeep, if Dwarven Hold is tapped, put a storage counter on it. OneShotEffect addStorageCounter = new AddCountersSourceEffect(CounterType.STORAGE.createInstance()); - Effect effect = new ConditionalOneShotEffect(addStorageCounter, SourceTappedCondition.instance, "if {this} is tapped, put a storage counter on it"); + Effect effect = new ConditionalOneShotEffect(addStorageCounter, SourceTappedCondition.TAPPED, "if {this} is tapped, put a storage counter on it"); this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, effect, TargetController.YOU, false)); // {tap}, Remove any number of storage counters from Dwarven Hold: Add {R} for each storage counter removed this way. Ability ability = new DynamicManaAbility( diff --git a/Mage.Sets/src/mage/cards/d/DwynenGiltLeafDaen.java b/Mage.Sets/src/mage/cards/d/DwynenGiltLeafDaen.java index 58cf085cc49..0e81a67b938 100644 --- a/Mage.Sets/src/mage/cards/d/DwynenGiltLeafDaen.java +++ b/Mage.Sets/src/mage/cards/d/DwynenGiltLeafDaen.java @@ -42,7 +42,7 @@ public final class DwynenGiltLeafDaen extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield, new FilterCreaturePermanent(SubType.ELF, "Elf creatures"), true))); // Whenever Dwynen, Gilt-Leaf Daen attacks, you gain 1 life for each attacking Elf you control. - this.addAbility(new AttacksTriggeredAbility(new GainLifeEffect(new PermanentsOnBattlefieldCount(filter)), false)); + this.addAbility(new AttacksTriggeredAbility(new GainLifeEffect(new PermanentsOnBattlefieldCount(filter)).setText("you gain 1 life for each attacking Elf you control"), false)); } private DwynenGiltLeafDaen(final DwynenGiltLeafDaen card) { diff --git a/Mage.Sets/src/mage/cards/d/DyingToServe.java b/Mage.Sets/src/mage/cards/d/DyingToServe.java new file mode 100644 index 00000000000..e741c9a44c2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DyingToServe.java @@ -0,0 +1,34 @@ +package mage.cards.d; + +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DiscardCardControllerTriggeredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.game.permanent.token.ZombieToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DyingToServe extends CardImpl { + + public DyingToServe(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}"); + + // Whenever you discard one or more cards, create a tapped 2/2 black Zombie creature token. This ability triggers only once each turn. + this.addAbility(new DiscardCardControllerTriggeredAbility(new CreateTokenEffect( + new ZombieToken(), 1, true, false), false + ).setTriggerPhrase("Whenever you discard one or more cards, ").setTriggersOnce(true)); + } + + private DyingToServe(final DyingToServe card) { + super(card); + } + + @Override + public DyingToServe copy() { + return new DyingToServe(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DynavoltTower.java b/Mage.Sets/src/mage/cards/d/DynavoltTower.java index b0ef2837dad..33bec510004 100644 --- a/Mage.Sets/src/mage/cards/d/DynavoltTower.java +++ b/Mage.Sets/src/mage/cards/d/DynavoltTower.java @@ -1,7 +1,5 @@ - package mage.cards.d; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility; @@ -12,24 +10,27 @@ import mage.abilities.effects.common.counter.GetEnergyCountersControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Zone; -import mage.filter.common.FilterInstantOrSorcerySpell; +import mage.filter.StaticFilters; import mage.target.common.TargetAnyTarget; +import java.util.UUID; + /** - * * @author emerald000 */ public final class DynavoltTower extends CardImpl { public DynavoltTower(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); // Whenever you cast an instant or sorcery spell, you get {E}{E}. - this.addAbility(new SpellCastControllerTriggeredAbility(new GetEnergyCountersControllerEffect(2), new FilterInstantOrSorcerySpell(), false)); + this.addAbility(new SpellCastControllerTriggeredAbility( + new GetEnergyCountersControllerEffect(2), + StaticFilters.FILTER_SPELL_AN_INSTANT_OR_SORCERY, false + )); // {T}, Pay {E}{E}{E}{E}{E}: Dynavolt Tower deals 3 damage to any target. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(3), new TapSourceCost()); + Ability ability = new SimpleActivatedAbility(new DamageTargetEffect(3), new TapSourceCost()); ability.addCost(new PayEnergyCost(5)); ability.addTarget(new TargetAnyTarget()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/e/EHondaSumoChampion.java b/Mage.Sets/src/mage/cards/e/EHondaSumoChampion.java new file mode 100644 index 00000000000..711cabfee2e --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EHondaSumoChampion.java @@ -0,0 +1,60 @@ +package mage.cards.e; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.MyTurnCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.ruleModifying.CombatDamageByToughnessEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.StaticFilters; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EHondaSumoChampion extends CardImpl { + + public EHondaSumoChampion(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{W}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(0); + this.toughness = new MageInt(7); + + // Sumo Spirit—As long as it's your turn, each creature assigns combat damage equal to its toughness rather than its power. + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new CombatDamageByToughnessEffect(StaticFilters.FILTER_PERMANENT_CREATURE, false), + MyTurnCondition.instance, "as long as it's your turn, each creature " + + "assigns combat damage equal to its toughness rather than its power" + )).withFlavorWord("Sumo Spirit")); + + // Hundred Hand Slap—Whenever E. Honda, Sumo Champion attacks, up to one hundred target creatures each get +0/+X until end of turn, where X is the number of cards in your hand. + Ability ability = new AttacksTriggeredAbility(new BoostTargetEffect( + StaticValue.get(0), CardsInTargetPlayerHandCount.instance, Duration.EndOfTurn + ).setText("up to one hundred target creatures each get +0/+X until end of turn, where X is the number of cards in your hand")); + ability.addTarget(new TargetCreaturePermanent(0, 100)); + this.addAbility(ability.withFlavorWord("Hundred Hand Slap")); + } + + private EHondaSumoChampion(final EHondaSumoChampion card) { + super(card); + } + + @Override + public EHondaSumoChampion copy() { + return new EHondaSumoChampion(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/Earthshaker.java b/Mage.Sets/src/mage/cards/e/Earthshaker.java index bf94d902078..f1d7f811176 100644 --- a/Mage.Sets/src/mage/cards/e/Earthshaker.java +++ b/Mage.Sets/src/mage/cards/e/Earthshaker.java @@ -34,7 +34,7 @@ public final class Earthshaker extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(5); // Whenever you cast a Spirit or Arcane spell, Earthshaker deals 2 damage to each creature without flying. - this.addAbility(new SpellCastControllerTriggeredAbility(new DamageAllEffect(StaticValue.get(2) , creatureFilter), StaticFilters.SPIRIT_OR_ARCANE_CARD, false)); + this.addAbility(new SpellCastControllerTriggeredAbility(new DamageAllEffect(StaticValue.get(2) , creatureFilter), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false)); } private Earthshaker(final Earthshaker card) { diff --git a/Mage.Sets/src/mage/cards/e/EaterOfVirtue.java b/Mage.Sets/src/mage/cards/e/EaterOfVirtue.java new file mode 100644 index 00000000000..8af937ff619 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EaterOfVirtue.java @@ -0,0 +1,193 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.cards.e; + +import java.util.Set; +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.DiesAttachedTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostEquippedEffect; +import mage.abilities.keyword.DeathtouchAbility; +import mage.abilities.keyword.DoubleStrikeAbility; +import mage.abilities.keyword.EquipAbility; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.HexproofAbility; +import mage.abilities.keyword.IndestructibleAbility; +import mage.abilities.keyword.LifelinkAbility; +import mage.abilities.keyword.MenaceAbility; +import mage.abilities.keyword.ProtectionAbility; +import mage.abilities.keyword.ReachAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Layer; +import mage.constants.Outcome; +import mage.constants.SetTargetPointer; +import mage.constants.SubLayer; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.util.CardUtil; + +/** + * @author jeffwadsworth + */ +public final class EaterOfVirtue extends CardImpl { + + public EaterOfVirtue(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}"); + + addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.EQUIPMENT); + + // Whenever equipped creature dies, exile it. + this.addAbility(new DiesAttachedTriggeredAbility(new EaterOfVirtueExileEffect(), "equipped creature", false, true, SetTargetPointer.CARD)); + + // Equipped creature gets +2/+0. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(2, 0, Duration.WhileOnBattlefield))); + + // As long as a card exiled with Eater of Virtue has flying, equipped creature has flying. The same is true for first strike, double strike, deathtouch, haste, hexproof, indestructible, lifelink, menace, protection, reach, trample, and vigilance. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new EaterOfVirtueGainAbilityAttachedEffect())); + + // Equip {1} + this.addAbility(new EquipAbility(Outcome.BoostCreature, new GenericManaCost(1))); + + } + + private EaterOfVirtue(final EaterOfVirtue card) { + super(card); + } + + @Override + public EaterOfVirtue copy() { + return new EaterOfVirtue(this); + } +} + +class EaterOfVirtueExileEffect extends OneShotEffect { + + EaterOfVirtueExileEffect() { + super(Outcome.Neutral); + this.staticText = "exile it"; + } + + EaterOfVirtueExileEffect(final EaterOfVirtueExileEffect effect) { + super(effect); + } + + @Override + public EaterOfVirtueExileEffect copy() { + return new EaterOfVirtueExileEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent eaterOfVirtue = game.getPermanent(source.getSourceId()); + Card exiledCard = game.getCard(targetPointer.getFirst(game, source)); + if (controller != null + && eaterOfVirtue != null + && exiledCard != null) { + UUID exileId = CardUtil.getExileZoneId(source.getSourceId().toString() + "cards exiled by Eater of Virtue", game); + controller.moveCardsToExile(exiledCard, source, game, true, exileId, eaterOfVirtue.getIdName()); + return true; + } + return false; + } +} + +class EaterOfVirtueGainAbilityAttachedEffect extends ContinuousEffectImpl { + + public EaterOfVirtueGainAbilityAttachedEffect() { + super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); + staticText = "As long as a card exiled with Eater of Virtue has flying, equipped creature has flying. The same is true for first strike, double strike, deathtouch, haste, hexproof, indestructible, lifelink, menace, protection, reach, trample, and vigilance"; + } + + public EaterOfVirtueGainAbilityAttachedEffect(final EaterOfVirtueGainAbilityAttachedEffect effect) { + super(effect); + } + + @Override + public EaterOfVirtueGainAbilityAttachedEffect copy() { + return new EaterOfVirtueGainAbilityAttachedEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent eaterOfVirtue = game.getPermanent(source.getSourceId()); + if (eaterOfVirtue != null + && eaterOfVirtue.getAttachedTo() != null) { + Permanent permanent = game.getPermanent(eaterOfVirtue.getAttachedTo()); + if (permanent != null) { + UUID exileId = CardUtil.getExileZoneId(source.getSourceId().toString() + "cards exiled by Eater of Virtue", game); + if (game.getState().getExile().getExileZone(exileId) != null + && game.getState().getExile().getExileZone(exileId).size() > 0) { + Set cardsInExile = game.getState().getExile().getExileZone(exileId).getCards(game); + for (Card card : cardsInExile) { + for (Ability a : card.getAbilities()) { + if (a instanceof FlyingAbility) { + permanent.addAbility(a, source.getSourceId(), game); + } + if (a instanceof FirstStrikeAbility) { + permanent.addAbility(a, source.getSourceId(), game); + } + if (a instanceof DoubleStrikeAbility) { + permanent.addAbility(a, source.getSourceId(), game); + } + if (a instanceof DeathtouchAbility) { + permanent.addAbility(a, source.getSourceId(), game); + } + if (a instanceof HasteAbility) { + permanent.addAbility(a, source.getSourceId(), game); + } + if (a instanceof HexproofAbility) { + permanent.addAbility(a, source.getSourceId(), game); + } + if (a instanceof IndestructibleAbility) { + permanent.addAbility(a, source.getSourceId(), game); + } + if (a instanceof LifelinkAbility) { + permanent.addAbility(a, source.getSourceId(), game); + } + if (a instanceof MenaceAbility) { + permanent.addAbility(a, source.getSourceId(), game); + } + if (a instanceof ProtectionAbility) { + permanent.addAbility(a, source.getSourceId(), game); + } + if (a instanceof IndestructibleAbility) { + permanent.addAbility(a, source.getSourceId(), game); + } + if (a instanceof ReachAbility) { + permanent.addAbility(a, source.getSourceId(), game); + } + if (a instanceof TrampleAbility) { + permanent.addAbility(a, source.getSourceId(), game); + } + if (a instanceof VigilanceAbility) { + permanent.addAbility(a, source.getSourceId(), game); + } + } + } + } + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/e/EchoChamber.java b/Mage.Sets/src/mage/cards/e/EchoChamber.java index 18d24b34143..a06208c4b42 100644 --- a/Mage.Sets/src/mage/cards/e/EchoChamber.java +++ b/Mage.Sets/src/mage/cards/e/EchoChamber.java @@ -71,7 +71,7 @@ class EchoChamberCreateTokenEffect extends OneShotEffect { if (copiedPermanent != null) { CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(null, CardType.CREATURE, true); if (effect.apply(game, source)) { - for (Permanent copyPermanent : effect.getAddedPermanent()) { + for (Permanent copyPermanent : effect.getAddedPermanents()) { ExileTargetEffect exileEffect = new ExileTargetEffect(); exileEffect.setTargetPointer(new FixedTarget(copyPermanent, game)); DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); diff --git a/Mage.Sets/src/mage/cards/e/EchoOfDeathsWail.java b/Mage.Sets/src/mage/cards/e/EchoOfDeathsWail.java new file mode 100644 index 00000000000..299618e0783 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EchoOfDeathsWail.java @@ -0,0 +1,67 @@ +package mage.cards.e; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.continuous.GainControlAllEffect; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.predicate.permanent.TokenPredicate; + +/** + * + * @author weirddan455 + */ +public final class EchoOfDeathsWail extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent(SubType.RAT, "all Rat tokens"); + + static { + filter.add(TokenPredicate.TRUE); + } + + public EchoOfDeathsWail(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, ""); + + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + this.color.setBlack(true); + this.nightCard = true; + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // When Echo of Death's Wail enters the battlefield, gain control of all Rat tokens. + this.addAbility(new EntersBattlefieldTriggeredAbility(new GainControlAllEffect(Duration.Custom, filter))); + + // Whenever Echo of Death's Wail attacks, you may sacrifice another creature. If you do, draw a card. + this.addAbility(new AttacksTriggeredAbility(new DoIfCostPaid( + new DrawCardSourceControllerEffect(1), + new SacrificeTargetCost(StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE) + ))); + } + + private EchoOfDeathsWail(final EchoOfDeathsWail card) { + super(card); + } + + @Override + public EchoOfDeathsWail copy() { + return new EchoOfDeathsWail(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/EcologistsTerrarium.java b/Mage.Sets/src/mage/cards/e/EcologistsTerrarium.java new file mode 100644 index 00000000000..4b2c0dae3e3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EcologistsTerrarium.java @@ -0,0 +1,52 @@ +package mage.cards.e; + +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.target.common.TargetCardInLibrary; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EcologistsTerrarium extends CardImpl { + + public EcologistsTerrarium(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); + + // When Ecologist's Terrarium enters the battlefield, you may search your library for a basic land card, reveal it, put it into your hand, then shuffle. + this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryPutInHandEffect( + new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), true + ), true)); + + // {2}, {T}, Sacrifice Ecologist's Terrarium: Put a +1/+1 counter on target creature. Activate only as a sorcery. + Ability ability = new ActivateAsSorceryActivatedAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance()), new GenericManaCost(2) + ); + ability.addCost(new TapSourceCost()); + ability.addCost(new SacrificeSourceCost()); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + private EcologistsTerrarium(final EcologistsTerrarium card) { + super(card); + } + + @Override + public EcologistsTerrarium copy() { + return new EcologistsTerrarium(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/EcstaticAwakener.java b/Mage.Sets/src/mage/cards/e/EcstaticAwakener.java index 50f4541680b..200f79689a2 100644 --- a/Mage.Sets/src/mage/cards/e/EcstaticAwakener.java +++ b/Mage.Sets/src/mage/cards/e/EcstaticAwakener.java @@ -30,7 +30,6 @@ public final class EcstaticAwakener extends CardImpl { this.subtype.add(SubType.WIZARD); this.power = new MageInt(1); this.toughness = new MageInt(1); - this.transformable = true; this.secondSideCardClazz = mage.cards.a.AwokenDemon.class; // {2}{B}, Sacrifice another creature: Draw a card, then transform Ecstatic Awakener. Activate only once each turn. @@ -38,7 +37,7 @@ public final class EcstaticAwakener extends CardImpl { Ability ability = new LimitedTimesPerTurnActivatedAbility( Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), new ManaCostsImpl<>("{2}{B}") ); - ability.addEffect(new TransformSourceEffect(true).concatBy(", then")); + ability.addEffect(new TransformSourceEffect().concatBy(", then")); ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE))); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/e/EddytrailHawk.java b/Mage.Sets/src/mage/cards/e/EddytrailHawk.java index d89c7edb8e2..2fa949d573c 100644 --- a/Mage.Sets/src/mage/cards/e/EddytrailHawk.java +++ b/Mage.Sets/src/mage/cards/e/EddytrailHawk.java @@ -1,4 +1,3 @@ - package mage.cards.e; import java.util.UUID; @@ -14,8 +13,8 @@ import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; +import mage.constants.SubType; import mage.filter.common.FilterAttackingCreature; import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; @@ -26,7 +25,7 @@ import mage.target.common.TargetCreaturePermanent; */ public final class EddytrailHawk extends CardImpl { - private static final FilterAttackingCreature filter = new FilterAttackingCreature(); + private static final FilterAttackingCreature filter = new FilterAttackingCreature("another target attacking creature"); static { filter.add(AnotherPredicate.instance); @@ -42,10 +41,8 @@ public final class EddytrailHawk extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Eddytail Hawk enters the battlefield, you get {E}{E}. this.addAbility(new EntersBattlefieldTriggeredAbility(new GetEnergyCountersControllerEffect(2))); - // When Eddytail Hawk attacks you pay {E}. If you do, another target attacking creature gains flying until end of turn. - DoIfCostPaid doIfCostPaidEffect = new DoIfCostPaid(new GainAbilityTargetEffect(FlyingAbility.getInstance(), Duration.EndOfTurn), new PayEnergyCost(1), null, true); - Ability ability = new AttacksTriggeredAbility(doIfCostPaidEffect, false, - "Whenever {this} attacks you pay {E}. If you do, another target attacking creature gains flying until end of turn."); + // Whenever Eddytrail Hawk attacks, you may pay {E}. If you do, another target attacking creature gains flying until end of turn. + Ability ability = new AttacksTriggeredAbility(new DoIfCostPaid(new GainAbilityTargetEffect(FlyingAbility.getInstance(), Duration.EndOfTurn), new PayEnergyCost(1))); ability.addTarget(new TargetCreaturePermanent(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/e/EdgarCharmedGroom.java b/Mage.Sets/src/mage/cards/e/EdgarCharmedGroom.java new file mode 100644 index 00000000000..799a837050c --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EdgarCharmedGroom.java @@ -0,0 +1,88 @@ +package mage.cards.e; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.abilities.keyword.TransformAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.Game; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EdgarCharmedGroom extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(SubType.VAMPIRE, "Vampires"); + + public EdgarCharmedGroom(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.VAMPIRE); + this.subtype.add(SubType.NOBLE); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + this.secondSideCardClazz = mage.cards.e.EdgarMarkovsCoffin.class; + + // Other Vampires you control get +1/+1. + this.addAbility(new SimpleStaticAbility(new BoostControlledEffect( + 1, 1, Duration.WhileOnBattlefield, filter, true + ))); + + // When Edgar, Charmed Groom dies, return it to the battlefield transformed under its owner's control. + this.addAbility(new TransformAbility()); + this.addAbility(new DiesSourceTriggeredAbility(new EdgarCharmedGroomEffect())); + } + + private EdgarCharmedGroom(final EdgarCharmedGroom card) { + super(card); + } + + @Override + public EdgarCharmedGroom copy() { + return new EdgarCharmedGroom(this); + } +} + +class EdgarCharmedGroomEffect extends OneShotEffect { + + EdgarCharmedGroomEffect() { + super(Outcome.Benefit); + staticText = "return it to the battlefield transformed under its owner's control"; + } + + private EdgarCharmedGroomEffect(final EdgarCharmedGroomEffect effect) { + super(effect); + } + + @Override + public EdgarCharmedGroomEffect copy() { + return new EdgarCharmedGroomEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + MageObject sourceObject = source.getSourceObjectIfItStillExists(game); + if (!(sourceObject instanceof Card)) { + return false; + } + game.getState().setValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + source.getSourceId(), Boolean.TRUE); + controller.moveCards((Card) sourceObject, Zone.BATTLEFIELD, source, game, false, false, true, null); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/e/EdgarMarkovsCoffin.java b/Mage.Sets/src/mage/cards/e/EdgarMarkovsCoffin.java new file mode 100644 index 00000000000..8065378ef46 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EdgarMarkovsCoffin.java @@ -0,0 +1,86 @@ +package mage.cards.e; + +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SuperType; +import mage.constants.TargetController; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.EdgarMarkovsCoffinVampireToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EdgarMarkovsCoffin extends CardImpl { + + public EdgarMarkovsCoffin(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, ""); + + this.addSuperType(SuperType.LEGENDARY); + this.color.setWhite(true); + this.color.setBlack(true); + this.nightCard = true; + + // At the beginning of your upkeep, create a 1/1 white and black Vampire creature token with lifelink and put a bloodline counter on Edgar Markov's Coffin. Then if there are three or more bloodline counters on it, remove those counters and transform it. + Ability ability = new BeginningOfUpkeepTriggeredAbility( + new CreateTokenEffect(new EdgarMarkovsCoffinVampireToken()), + TargetController.YOU, false + ); + ability.addEffect(new AddCountersSourceEffect( + CounterType.BLOODLINE.createInstance() + ).concatBy("and")); + ability.addEffect(new EdgarMarkovsCoffinEffect()); + this.addAbility(ability); + } + + private EdgarMarkovsCoffin(final EdgarMarkovsCoffin card) { + super(card); + } + + @Override + public EdgarMarkovsCoffin copy() { + return new EdgarMarkovsCoffin(this); + } +} + +class EdgarMarkovsCoffinEffect extends OneShotEffect { + + EdgarMarkovsCoffinEffect() { + super(Outcome.Benefit); + staticText = "Then if there are three or more bloodline counters on it, remove those counters and transform it"; + } + + private EdgarMarkovsCoffinEffect(final EdgarMarkovsCoffinEffect effect) { + super(effect); + } + + @Override + public EdgarMarkovsCoffinEffect copy() { + return new EdgarMarkovsCoffinEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (permanent == null) { + return false; + } + int counters = permanent.getCounters(game).getCount(CounterType.BLOODLINE); + if (counters < 3) { + return false; + } + permanent.removeCounters(CounterType.BLOODLINE.createInstance(counters), source, game); + permanent.transform(source, game); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/e/EdgarsAwakening.java b/Mage.Sets/src/mage/cards/e/EdgarsAwakening.java new file mode 100644 index 00000000000..5e97ea7cb09 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EdgarsAwakening.java @@ -0,0 +1,85 @@ +package mage.cards.e; + +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DoWhenCostPaid; +import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EdgarsAwakening extends CardImpl { + + public EdgarsAwakening(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{B}{B}"); + + // Return target creature card from your graveyard to the battlefield. + this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect()); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); + + // When you discard Edgar's Awakening, you may pay {B}. When you do, return target creature card from your graveyard to your hand. + this.addAbility(new EdgarsAwakeningTriggeredAbility()); + } + + private EdgarsAwakening(final EdgarsAwakening card) { + super(card); + } + + @Override + public EdgarsAwakening copy() { + return new EdgarsAwakening(this); + } +} + +class EdgarsAwakeningTriggeredAbility extends TriggeredAbilityImpl { + + private static final ReflexiveTriggeredAbility makeAbility() { + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new ReturnFromGraveyardToHandTargetEffect(), false, + "return target creature card from your graveyard to your hand" + ); + ability.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); + return ability; + } + + EdgarsAwakeningTriggeredAbility() { + super(Zone.ALL, new DoWhenCostPaid(makeAbility(), new ManaCostsImpl<>("{B}"), "Pay {B}?")); + } + + private EdgarsAwakeningTriggeredAbility(final EdgarsAwakeningTriggeredAbility ability) { + super(ability); + } + + @Override + public EdgarsAwakeningTriggeredAbility copy() { + return new EdgarsAwakeningTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DISCARDED_CARD; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return this.getSourceId().equals(event.getTargetId()); + } + + @Override + public String getRule() { + return "When you discard {this}, you may pay {B}. " + + "When you do, return target creature card from your graveyard to your hand."; + } +} diff --git a/Mage.Sets/src/mage/cards/e/EiganjoExemplar.java b/Mage.Sets/src/mage/cards/e/EiganjoExemplar.java new file mode 100644 index 00000000000..0095559189f --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EiganjoExemplar.java @@ -0,0 +1,42 @@ +package mage.cards.e; + +import mage.MageInt; +import mage.abilities.common.AttacksAloneControlledTriggeredAbility; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +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 EiganjoExemplar extends CardImpl { + + public EiganjoExemplar(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{1}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SAMURAI); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Whenever a Samurai or Warrior you control attacks alone, it gets +1/+1 until end of turn. + this.addAbility(new AttacksAloneControlledTriggeredAbility( + new BoostTargetEffect(1, 1).setText("it gets +1/+1 until end of turn"), + StaticFilters.FILTER_CONTROLLED_SAMURAI_OR_WARRIOR, true, false + )); + } + + private EiganjoExemplar(final EiganjoExemplar card) { + super(card); + } + + @Override + public EiganjoExemplar copy() { + return new EiganjoExemplar(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/EiganjoSeatOfTheEmpire.java b/Mage.Sets/src/mage/cards/e/EiganjoSeatOfTheEmpire.java new file mode 100644 index 00000000000..a4f063bf4ce --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EiganjoSeatOfTheEmpire.java @@ -0,0 +1,50 @@ +package mage.cards.e; + +import mage.abilities.Ability; +import mage.abilities.costs.costadjusters.LegendaryCreatureCostAdjuster; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.InfoEffect; +import mage.abilities.keyword.ChannelAbility; +import mage.abilities.mana.WhiteManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SuperType; +import mage.target.common.TargetAttackingOrBlockingCreature; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EiganjoSeatOfTheEmpire extends CardImpl { + + public EiganjoSeatOfTheEmpire(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + this.addSuperType(SuperType.LEGENDARY); + + // {T}: Add {W}. + this.addAbility(new WhiteManaAbility()); + + // Channel — {2}{W}, Discard Eiganjo, Seat of the Empire: It deals 4 damage to target attacking or blocking creature. This ability costs {1} less to activate for each legendary creature you control. + Ability ability = new ChannelAbility( + "{2}{W}", new DamageTargetEffect(4, "it") + ); + ability.addEffect(new InfoEffect( + "This ability costs {1} less to activate for each legendary creature you control" + )); + ability.addTarget(new TargetAttackingOrBlockingCreature()); + ability.setCostAdjuster(LegendaryCreatureCostAdjuster.instance); + this.addAbility(ability); + } + + private EiganjoSeatOfTheEmpire(final EiganjoSeatOfTheEmpire card) { + super(card); + } + + @Override + public EiganjoSeatOfTheEmpire copy() { + return new EiganjoSeatOfTheEmpire(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/EiganjoUprising.java b/Mage.Sets/src/mage/cards/e/EiganjoUprising.java new file mode 100644 index 00000000000..eb19f579af4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EiganjoUprising.java @@ -0,0 +1,80 @@ +package mage.cards.e; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.permanent.token.SamuraiToken; +import mage.game.permanent.token.Token; +import mage.target.targetpointer.FixedTargets; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EiganjoUprising extends CardImpl { + + public EiganjoUprising(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{R}{W}"); + + // Create X 2/2 white Samurai creature tokens with vigilance. They gain menace and haste until end of turn. + // Each opponent creates X minus one 2/2 white Samurai creature tokens with vigilance. + this.getSpellAbility().addEffect(new EiganjoUprisingEffect()); + } + + private EiganjoUprising(final EiganjoUprising card) { + super(card); + } + + @Override + public EiganjoUprising copy() { + return new EiganjoUprising(this); + } +} + +class EiganjoUprisingEffect extends OneShotEffect { + + EiganjoUprisingEffect() { + super(Outcome.Benefit); + staticText = "create X 2/2 white Samurai creature tokens with vigilance. " + + "They gain menace and haste until end of turn.
Each opponent " + + "creates X minus one 2/2 white Samurai creature tokens with vigilance"; + } + + private EiganjoUprisingEffect(final EiganjoUprisingEffect effect) { + super(effect); + } + + @Override + public EiganjoUprisingEffect copy() { + return new EiganjoUprisingEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + int amount = source.getManaCostsToPay().getX(); + if (amount < 1) { + return false; + } + Token token = new SamuraiToken(); + token.putOntoBattlefield(amount, game, source); + game.addEffect(new GainAbilityTargetEffect(new MenaceAbility(false)) + .setTargetPointer(new FixedTargets(token, game)), source); + game.addEffect(new GainAbilityTargetEffect(HasteAbility.getInstance()) + .setTargetPointer(new FixedTargets(token, game)), source); + if (amount < 2) { + return true; + } + for (UUID opponentId : game.getOpponents(source.getControllerId())) { + token.putOntoBattlefield(amount - 1, game, source, opponentId); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/e/ElbrusTheBindingBlade.java b/Mage.Sets/src/mage/cards/e/ElbrusTheBindingBlade.java index 591d5dddfe4..ee01415f79f 100644 --- a/Mage.Sets/src/mage/cards/e/ElbrusTheBindingBlade.java +++ b/Mage.Sets/src/mage/cards/e/ElbrusTheBindingBlade.java @@ -1,4 +1,3 @@ - package mage.cards.e; import java.util.UUID; @@ -31,7 +30,6 @@ public final class ElbrusTheBindingBlade extends CardImpl { addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.EQUIPMENT); - this.transformable = true; this.secondSideCardClazz = mage.cards.w.WithengarUnbound.class; this.addAbility(new TransformAbility()); @@ -70,9 +68,7 @@ class ElbrusTheBindingBladeEffect extends OneShotEffect { Permanent attachedTo = game.getPermanent(equipment.getAttachedTo()); if (attachedTo != null) { attachedTo.removeAttachment(equipment.getId(), source, game); - equipment.transform(game); - game.informPlayers(equipment.getName() + " transforms into " + equipment.getSecondCardFace().getName()); - + equipment.transform(source, game); } } return false; diff --git a/Mage.Sets/src/mage/cards/e/ElderPineOfJukai.java b/Mage.Sets/src/mage/cards/e/ElderPineOfJukai.java index 0363c022d52..78a76519a6c 100644 --- a/Mage.Sets/src/mage/cards/e/ElderPineOfJukai.java +++ b/Mage.Sets/src/mage/cards/e/ElderPineOfJukai.java @@ -12,7 +12,6 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; import mage.filter.StaticFilters; -import mage.filter.common.FilterLandCard; /** * @@ -28,7 +27,7 @@ public final class ElderPineOfJukai extends CardImpl { this.toughness = new MageInt(1); // Whenever you cast a Spirit or Arcane spell, reveal the top three cards of your library. Put all land cards revealed this way into your hand and the rest on the bottom of your library in any order. - this.addAbility(new SpellCastControllerTriggeredAbility(new RevealLibraryPutIntoHandEffect(3, StaticFilters.FILTER_CARD_LANDS, Zone.LIBRARY), StaticFilters.SPIRIT_OR_ARCANE_CARD, false)); + this.addAbility(new SpellCastControllerTriggeredAbility(new RevealLibraryPutIntoHandEffect(3, StaticFilters.FILTER_CARD_LANDS, Zone.LIBRARY), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false)); // Soulshift 2 this.addAbility(new SoulshiftAbility(2)); diff --git a/Mage.Sets/src/mage/cards/e/EldraziMimic.java b/Mage.Sets/src/mage/cards/e/EldraziMimic.java index 938f6f1205e..e195622869d 100644 --- a/Mage.Sets/src/mage/cards/e/EldraziMimic.java +++ b/Mage.Sets/src/mage/cards/e/EldraziMimic.java @@ -1,4 +1,3 @@ - package mage.cards.e; import java.util.UUID; @@ -33,7 +32,7 @@ public final class EldraziMimic extends CardImpl { } public EldraziMimic(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}"); this.subtype.add(SubType.ELDRAZI); this.power = new MageInt(2); this.toughness = new MageInt(1); @@ -76,7 +75,7 @@ class EldraziMimicEffect extends OneShotEffect { Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (permanent != null) { ContinuousEffect effect = new SetPowerToughnessTargetEffect(permanent.getPower().getValue(), permanent.getToughness().getValue(), Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(source.getSourceId())); + effect.setTargetPointer(new FixedTarget(source.getSourceId(), game)); game.addEffect(effect, source); return true; } diff --git a/Mage.Sets/src/mage/cards/e/EldraziMonument.java b/Mage.Sets/src/mage/cards/e/EldraziMonument.java index 84dc4608e64..72c29f69cf2 100644 --- a/Mage.Sets/src/mage/cards/e/EldraziMonument.java +++ b/Mage.Sets/src/mage/cards/e/EldraziMonument.java @@ -1,14 +1,11 @@ - package mage.cards.e; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.common.OnEventTriggeredAbility; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.effects.common.SacrificeSourceUnlessPaysEffect; import mage.abilities.effects.common.continuous.BoostControlledEffect; -import mage.abilities.effects.common.continuous.GainAbilityAllEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.IndestructibleAbility; @@ -16,20 +13,13 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.TargetController; import mage.filter.StaticFilters; -import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.common.FilterCreaturePermanent; -import mage.game.Game; -import mage.game.events.GameEvent.EventType; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetControlledPermanent; +import java.util.UUID; + /** - * * @author BetaSteward_at_googlemail.com */ public final class EldraziMonument extends CardImpl { @@ -38,13 +28,26 @@ public final class EldraziMonument extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{5}"); // Creatures you control get +1/+1, have flying, and are indestructible. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE, false))); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledEffect(FlyingAbility.getInstance(), Duration.WhileOnBattlefield, new FilterCreaturePermanent()))); - Effect effect = new GainAbilityAllEffect(IndestructibleAbility.getInstance(), Duration.WhileOnBattlefield, new FilterControlledCreaturePermanent("Creatures you control"), false); - effect.setText("Creatures you control are indestructible"); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); + Ability ability = new SimpleStaticAbility(new BoostControlledEffect( + 1, 1, Duration.WhileOnBattlefield, + StaticFilters.FILTER_PERMANENT_CREATURES, false + )); + ability.addEffect(new GainAbilityControlledEffect( + FlyingAbility.getInstance(), Duration.WhileOnBattlefield, + StaticFilters.FILTER_PERMANENT_CREATURES + ).setText("and have flying")); + ability.addEffect(new GainAbilityControlledEffect( + IndestructibleAbility.getInstance(), Duration.WhileOnBattlefield, + StaticFilters.FILTER_PERMANENT_CREATURES + ).setText("and indestructible")); + this.addAbility(ability); + // At the beginning of your upkeep, sacrifice a creature. If you can't, sacrifice Eldrazi Monument. - this.addAbility(new OnEventTriggeredAbility(EventType.UPKEEP_STEP_PRE, "beginning of your upkeep", new EldraziMonumentEffect())); + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new SacrificeSourceUnlessPaysEffect(new SacrificeTargetCost( + new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT) + )).setText("sacrifice a creature. If you can't, sacrifice {this}"), TargetController.YOU, false + )); } private EldraziMonument(final EldraziMonument card) { @@ -55,42 +58,4 @@ public final class EldraziMonument extends CardImpl { public EldraziMonument copy() { return new EldraziMonument(this); } - -} - -class EldraziMonumentEffect extends OneShotEffect { - - public EldraziMonumentEffect() { - super(Outcome.Sacrifice); - staticText = "sacrifice a creature. If you can't, sacrifice {this}"; - } - - public EldraziMonumentEffect(final EldraziMonumentEffect ability) { - super(ability); - } - - @Override - public EldraziMonumentEffect copy() { - return new EldraziMonumentEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - TargetControlledPermanent target = new TargetControlledCreaturePermanent(); - Player player = game.getPlayer(source.getControllerId()); - if (target.canChoose(source.getSourceId(), source.getControllerId(), game)) { - player.choose(this.outcome, target, source.getSourceId(), game); - Permanent permanent = game.getPermanent(target.getFirstTarget()); - if (permanent != null) { - return permanent.sacrifice(source, game); - } - } - Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent != null) { - return permanent.sacrifice(source, game); - } - return false; - - } - } diff --git a/Mage.Sets/src/mage/cards/e/Electrolyze.java b/Mage.Sets/src/mage/cards/e/Electrolyze.java index 9e5540d9129..3590b7067c3 100644 --- a/Mage.Sets/src/mage/cards/e/Electrolyze.java +++ b/Mage.Sets/src/mage/cards/e/Electrolyze.java @@ -1,8 +1,6 @@ - package mage.cards.e; import java.util.UUID; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.DamageMultiEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; @@ -19,11 +17,8 @@ public final class Electrolyze extends CardImpl { public Electrolyze(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{U}{R}"); - - // Electrolyze deals 2 damage divided as you choose among one or two target creatures and/or players. - Effect effect = new DamageMultiEffect(2); - effect.setText("{this} deals 2 damage divided as you choose among one or two targets"); - this.getSpellAbility().addEffect(effect); + // Electrolyze deals 2 damage divided as you choose among one or two targets. + this.getSpellAbility().addEffect(new DamageMultiEffect(2)); this.getSpellAbility().addTarget(new TargetAnyTargetAmount(2)); // Draw a card. this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("
")); diff --git a/Mage.Sets/src/mage/cards/e/ElectrostaticPummeler.java b/Mage.Sets/src/mage/cards/e/ElectrostaticPummeler.java index 751da4b24c4..d339d06c9ad 100644 --- a/Mage.Sets/src/mage/cards/e/ElectrostaticPummeler.java +++ b/Mage.Sets/src/mage/cards/e/ElectrostaticPummeler.java @@ -1,29 +1,31 @@ package mage.cards.e; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.PayEnergyCost; +import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.SourcePermanentPowerCount; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.effects.common.counter.GetEnergyCountersControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.SubType; + +import java.util.UUID; /** - * * @author emerald000 */ public final class ElectrostaticPummeler extends CardImpl { + private static final DynamicValue xValue = new SourcePermanentPowerCount(false); + public ElectrostaticPummeler(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{3}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}"); this.subtype.add(SubType.CONSTRUCT); this.power = new MageInt(1); this.toughness = new MageInt(1); @@ -32,7 +34,9 @@ public final class ElectrostaticPummeler extends CardImpl { this.addAbility(new EntersBattlefieldTriggeredAbility(new GetEnergyCountersControllerEffect(3))); // Pay {E}{E}{E}: Electrostatic Pummeler gets +X/+X until end of turn, where X is its power. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(new SourcePermanentPowerCount(), new SourcePermanentPowerCount(), Duration.EndOfTurn, true), new PayEnergyCost(3))); + this.addAbility(new SimpleActivatedAbility(new BoostSourceEffect( + xValue, xValue, Duration.EndOfTurn, true + ).setText("{this} gets +X/+X until end of turn, where X is its power"), new PayEnergyCost(3))); } private ElectrostaticPummeler(final ElectrostaticPummeler card) { diff --git a/Mage.Sets/src/mage/cards/e/EliteScaleguard.java b/Mage.Sets/src/mage/cards/e/EliteScaleguard.java index 4a7770154ce..3b871ebe303 100644 --- a/Mage.Sets/src/mage/cards/e/EliteScaleguard.java +++ b/Mage.Sets/src/mage/cards/e/EliteScaleguard.java @@ -12,8 +12,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.counters.CounterType; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.game.Game; @@ -26,12 +25,6 @@ import mage.target.targetpointer.FirstTargetPointer; */ public final class EliteScaleguard extends CardImpl { - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("creature you control with a +1/+1 counter on it"); - - static { - filter.add(CounterType.P1P1.getPredicate()); - } - public EliteScaleguard(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{W}"); this.subtype.add(SubType.HUMAN); @@ -43,7 +36,11 @@ public final class EliteScaleguard extends CardImpl { this.addAbility(new EntersBattlefieldTriggeredAbility(new BolsterEffect(2))); // Whenever a creature you control with a +1/+1 counter on it attacks, tap target creature defending player controls. - Ability ability = new AttacksCreatureYouControlTriggeredAbility(new TapTargetEffect(), false, filter, true); + Ability ability = new AttacksCreatureYouControlTriggeredAbility( + new TapTargetEffect(), + false, + StaticFilters.FILTER_CONTROLLED_CREATURE_P1P1, + true); ability.addTarget(new TargetCreaturePermanent(new FilterCreaturePermanent("creature defending player controls"))); ability.setTargetAdjuster(EliteScaleguardTargetAdjuster.instance); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/e/ElkinBottle.java b/Mage.Sets/src/mage/cards/e/ElkinBottle.java index c9e6f32c1af..0fb1583933c 100644 --- a/Mage.Sets/src/mage/cards/e/ElkinBottle.java +++ b/Mage.Sets/src/mage/cards/e/ElkinBottle.java @@ -1,4 +1,3 @@ - package mage.cards.e; import java.util.UUID; @@ -67,7 +66,7 @@ class ElkinBottleExileEffect extends OneShotEffect { if (card != null) { controller.moveCardsToExile(card, source, game, true, source.getSourceId(), CardUtil.createObjectRealtedWindowTitle(source, game, null)); ContinuousEffect effect = new ElkinBottleCastFromExileEffect(); - effect.setTargetPointer(new FixedTarget(card.getId())); + effect.setTargetPointer(new FixedTarget(card.getId(), game)); game.addEffect(effect, source); } return true; diff --git a/Mage.Sets/src/mage/cards/e/EllywickTumblestrum.java b/Mage.Sets/src/mage/cards/e/EllywickTumblestrum.java index e7634ce174d..f85be8f7636 100644 --- a/Mage.Sets/src/mage/cards/e/EllywickTumblestrum.java +++ b/Mage.Sets/src/mage/cards/e/EllywickTumblestrum.java @@ -2,7 +2,6 @@ package mage.cards.e; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.GetEmblemEffect; import mage.abilities.effects.keyword.VentureIntoTheDungeonEffect; @@ -27,7 +26,7 @@ public final class EllywickTumblestrum extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.ELLYWICK); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Venture into the dungeon. this.addAbility(new LoyaltyAbility(new VentureIntoTheDungeonEffect(), 1)); diff --git a/Mage.Sets/src/mage/cards/e/ElmarUlvenwaldInformant.java b/Mage.Sets/src/mage/cards/e/ElmarUlvenwaldInformant.java new file mode 100644 index 00000000000..30d12c2bf11 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/ElmarUlvenwaldInformant.java @@ -0,0 +1,53 @@ +package mage.cards.e; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.CastSecondSpellTriggeredAbility; +import mage.abilities.effects.common.UntapTargetEffect; +import mage.abilities.effects.keyword.InvestigateEffect; +import mage.abilities.keyword.FriendsForeverAbility; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ElmarUlvenwaldInformant extends CardImpl { + + public ElmarUlvenwaldInformant(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // Whenever you cast your second spell each turn, untap target creature, then investigate. + Ability ability = new CastSecondSpellTriggeredAbility(new UntapTargetEffect()); + ability.addEffect(new InvestigateEffect().concatBy(", then")); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + + // Friends forever + this.addAbility(FriendsForeverAbility.getInstance()); + } + + private ElmarUlvenwaldInformant(final ElmarUlvenwaldInformant card) { + super(card); + } + + @Override + public ElmarUlvenwaldInformant copy() { + return new ElmarUlvenwaldInformant(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/ElspethConquersDeath.java b/Mage.Sets/src/mage/cards/e/ElspethConquersDeath.java index 8ba487dee25..d420c4a7eee 100644 --- a/Mage.Sets/src/mage/cards/e/ElspethConquersDeath.java +++ b/Mage.Sets/src/mage/cards/e/ElspethConquersDeath.java @@ -49,7 +49,7 @@ public final class ElspethConquersDeath extends CardImpl { this.subtype.add(SubType.SAGA); // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) - SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); + SagaAbility sagaAbility = new SagaAbility(this); // I - Exile target permanent an opponent controls with converted mana cost 3 or greater. sagaAbility.addChapterEffect( diff --git a/Mage.Sets/src/mage/cards/e/ElspethKnightErrant.java b/Mage.Sets/src/mage/cards/e/ElspethKnightErrant.java index 5a24864ad49..d1a7b7671c4 100644 --- a/Mage.Sets/src/mage/cards/e/ElspethKnightErrant.java +++ b/Mage.Sets/src/mage/cards/e/ElspethKnightErrant.java @@ -3,7 +3,6 @@ package mage.cards.e; import java.util.UUID; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.Effects; import mage.abilities.effects.common.CreateTokenEffect; @@ -33,7 +32,7 @@ public final class ElspethKnightErrant extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.ELSPETH); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Create a 1/1 white Soldier creature token. Token token = new SoldierToken(); diff --git a/Mage.Sets/src/mage/cards/e/ElspethSunsChampion.java b/Mage.Sets/src/mage/cards/e/ElspethSunsChampion.java index 5f6454a3d0b..0eedf89b8df 100644 --- a/Mage.Sets/src/mage/cards/e/ElspethSunsChampion.java +++ b/Mage.Sets/src/mage/cards/e/ElspethSunsChampion.java @@ -3,7 +3,6 @@ package mage.cards.e; import java.util.UUID; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.DestroyAllEffect; import mage.abilities.effects.common.GetEmblemEffect; @@ -35,7 +34,7 @@ public final class ElspethSunsChampion extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.ELSPETH); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Create three 1/1 white Soldier creature tokens. this.addAbility(new LoyaltyAbility(new CreateTokenEffect(new SoldierToken(), 3), 1)); diff --git a/Mage.Sets/src/mage/cards/e/ElspethSunsNemesis.java b/Mage.Sets/src/mage/cards/e/ElspethSunsNemesis.java index 22c8b5ef11b..6c3901a4216 100644 --- a/Mage.Sets/src/mage/cards/e/ElspethSunsNemesis.java +++ b/Mage.Sets/src/mage/cards/e/ElspethSunsNemesis.java @@ -2,7 +2,6 @@ package mage.cards.e; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; @@ -27,7 +26,7 @@ public final class ElspethSunsNemesis extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.ELSPETH); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // −1: Up to two target creatures you control each get +2/+1 until end of turn. Ability ability = new LoyaltyAbility(new BoostTargetEffect(2, 1) diff --git a/Mage.Sets/src/mage/cards/e/ElspethTirel.java b/Mage.Sets/src/mage/cards/e/ElspethTirel.java index ee656aefad9..b2607851490 100644 --- a/Mage.Sets/src/mage/cards/e/ElspethTirel.java +++ b/Mage.Sets/src/mage/cards/e/ElspethTirel.java @@ -4,7 +4,6 @@ package mage.cards.e; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; @@ -31,7 +30,7 @@ public final class ElspethTirel extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.ELSPETH); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); this.addAbility(new LoyaltyAbility(new ElspethTirelFirstEffect(), 2)); this.addAbility(new LoyaltyAbility(new CreateTokenEffect(new SoldierToken(), 3), -2)); diff --git a/Mage.Sets/src/mage/cards/e/ElspethUndauntedHero.java b/Mage.Sets/src/mage/cards/e/ElspethUndauntedHero.java index d04129b3531..cb21d7facd4 100644 --- a/Mage.Sets/src/mage/cards/e/ElspethUndauntedHero.java +++ b/Mage.Sets/src/mage/cards/e/ElspethUndauntedHero.java @@ -2,7 +2,6 @@ package mage.cards.e; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.dynamicvalue.common.DevotionCount; import mage.abilities.effects.common.continuous.BoostControlledEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; @@ -19,7 +18,7 @@ import mage.counters.CounterType; import mage.filter.FilterCard; import mage.filter.StaticFilters; import mage.filter.predicate.mageobject.NamePredicate; -import mage.target.common.TargetCreaturePermanent; +import mage.target.TargetPermanent; import java.util.UUID; @@ -39,11 +38,11 @@ public final class ElspethUndauntedHero extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.ELSPETH); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +2: Put a +1/+1 counter on each of up to two target creatures. Ability ability = new LoyaltyAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance()), 2); - ability.addTarget(new TargetCreaturePermanent(0, 2)); + ability.addTarget(new TargetPermanent(0, 2, StaticFilters.FILTER_PERMANENT_CREATURES)); this.addAbility(ability); // −2: Search your library and/or graveyard for a card named Sunlit Hoplite and put it onto the battlefield. If you search your library this way, shuffle it. diff --git a/Mage.Sets/src/mage/cards/e/ElspethsNightmare.java b/Mage.Sets/src/mage/cards/e/ElspethsNightmare.java index 1635eb25306..96071aa6af5 100644 --- a/Mage.Sets/src/mage/cards/e/ElspethsNightmare.java +++ b/Mage.Sets/src/mage/cards/e/ElspethsNightmare.java @@ -41,7 +41,7 @@ public final class ElspethsNightmare extends CardImpl { this.subtype.add(SubType.SAGA); // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) - SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); + SagaAbility sagaAbility = new SagaAbility(this); // I - Destroy target creature an opponent controls with power 2 or less. sagaAbility.addChapterEffect( diff --git a/Mage.Sets/src/mage/cards/e/ElusiveTormentor.java b/Mage.Sets/src/mage/cards/e/ElusiveTormentor.java index 592a56393b0..8e60ab80195 100644 --- a/Mage.Sets/src/mage/cards/e/ElusiveTormentor.java +++ b/Mage.Sets/src/mage/cards/e/ElusiveTormentor.java @@ -28,12 +28,11 @@ public final class ElusiveTormentor extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(4); - this.transformable = true; this.secondSideCardClazz = mage.cards.i.InsidiousMist.class; // {1}, Discard a card: Transform Elusive Tormentor. this.addAbility(new TransformAbility()); - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new TransformSourceEffect(true), new GenericManaCost(1)); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new TransformSourceEffect(), new GenericManaCost(1)); ability.addCost(new DiscardCardCost()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/e/EmbodimentOfFlame.java b/Mage.Sets/src/mage/cards/e/EmbodimentOfFlame.java index 765c538f037..7b06303bf68 100644 --- a/Mage.Sets/src/mage/cards/e/EmbodimentOfFlame.java +++ b/Mage.Sets/src/mage/cards/e/EmbodimentOfFlame.java @@ -34,7 +34,6 @@ public final class EmbodimentOfFlame extends CardImpl { this.toughness = new MageInt(3); this.color.setRed(true); this.nightCard = true; - this.transformable = true; // Whenever a spell you control deals damage, put a flame counter on Embodiment of Flame. this.addAbility(new EmbodimentOfFlameTriggeredAbility()); @@ -83,7 +82,7 @@ class EmbodimentOfFlameTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { Spell spell = game.getSpellOrLKIStack(event.getSourceId()); - return spell != null && isControlledBy(spell.getControllerId()); + return spell != null && isControlledBy(spell.getControllerId()) && spell.isInstantOrSorcery(game); } @Override diff --git a/Mage.Sets/src/mage/cards/e/EmergentSequence.java b/Mage.Sets/src/mage/cards/e/EmergentSequence.java index 6d24719b886..a4016017b40 100644 --- a/Mage.Sets/src/mage/cards/e/EmergentSequence.java +++ b/Mage.Sets/src/mage/cards/e/EmergentSequence.java @@ -18,6 +18,7 @@ import mage.game.permanent.token.FractalToken; import mage.players.Player; import mage.target.common.TargetCardInLibrary; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import mage.watchers.Watcher; import java.util.HashMap; @@ -114,7 +115,7 @@ class EmergentSequenceWatcher extends Watcher { public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD && ((EntersTheBattlefieldEvent) event).getTarget().isLand(game)) { - playerMap.compute(event.getPlayerId(), (u, i) -> i == null ? 1 : Integer.sum(i, 1)); + playerMap.compute(event.getPlayerId(), CardUtil::setOrIncrementValue); } } diff --git a/Mage.Sets/src/mage/cards/e/EmpoweredAutogenerator.java b/Mage.Sets/src/mage/cards/e/EmpoweredAutogenerator.java index 2ac91e2883b..bbb1a078c78 100644 --- a/Mage.Sets/src/mage/cards/e/EmpoweredAutogenerator.java +++ b/Mage.Sets/src/mage/cards/e/EmpoweredAutogenerator.java @@ -71,7 +71,7 @@ class EmpoweredAutogeneratorManaEffect extends ManaEffect { public List getNetMana(Game game, Ability source) { List netMana = new ArrayList<>(); if (game != null) { - Permanent sourcePermanent = game.getState().getPermanent(source.getSourceId()); + Permanent sourcePermanent = game.getPermanent(source.getSourceId()); if (sourcePermanent != null) { int counters = sourcePermanent.getCounters(game).getCount(CounterType.CHARGE) + 1; // one counter will be added on real mana call if (counters > 0) { @@ -89,7 +89,7 @@ class EmpoweredAutogeneratorManaEffect extends ManaEffect { return mana; } game.getState().processAction(game); - Permanent sourcePermanent = game.getState().getPermanent(source.getSourceId()); + Permanent sourcePermanent = game.getPermanent(source.getSourceId()); if (sourcePermanent == null) { return mana; } diff --git a/Mage.Sets/src/mage/cards/e/EmpyrealVoyager.java b/Mage.Sets/src/mage/cards/e/EmpyrealVoyager.java index 9505b19c6d2..abe68125655 100644 --- a/Mage.Sets/src/mage/cards/e/EmpyrealVoyager.java +++ b/Mage.Sets/src/mage/cards/e/EmpyrealVoyager.java @@ -52,7 +52,7 @@ class EmpyrealVoyagerEffect extends OneShotEffect { public EmpyrealVoyagerEffect() { super(Outcome.Benefit); - this.staticText = "get that many {E} counters"; + this.staticText = "you get that many {E}"; } public EmpyrealVoyagerEffect(final EmpyrealVoyagerEffect effect) { diff --git a/Mage.Sets/src/mage/cards/e/Encroach.java b/Mage.Sets/src/mage/cards/e/Encroach.java index fdaebefef34..b15c28dfdba 100644 --- a/Mage.Sets/src/mage/cards/e/Encroach.java +++ b/Mage.Sets/src/mage/cards/e/Encroach.java @@ -7,9 +7,10 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SuperType; +import mage.constants.TargetController; import mage.filter.FilterCard; import mage.filter.predicate.Predicates; -import mage.target.common.TargetOpponent; +import mage.target.TargetPlayer; /** * @@ -28,8 +29,8 @@ public final class Encroach extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{B}"); // Target player reveals their hand. You choose a nonbasic land card from it. That player discards that card. - this.getSpellAbility().addEffect(new DiscardCardYouChooseTargetEffect(filter)); - this.getSpellAbility().addTarget(new TargetOpponent()); + this.getSpellAbility().addEffect(new DiscardCardYouChooseTargetEffect(filter, TargetController.ANY)); + this.getSpellAbility().addTarget(new TargetPlayer()); } private Encroach(final Encroach card) { diff --git a/Mage.Sets/src/mage/cards/e/EndTheFestivities.java b/Mage.Sets/src/mage/cards/e/EndTheFestivities.java new file mode 100644 index 00000000000..20bac0e27f9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EndTheFestivities.java @@ -0,0 +1,42 @@ +package mage.cards.e; + +import mage.abilities.effects.common.DamageAllEffect; +import mage.abilities.effects.common.DamagePlayersEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EndTheFestivities extends CardImpl { + + private static final FilterPermanent filter = new FilterCreatureOrPlaneswalkerPermanent(); + + static { + filter.add(TargetController.OPPONENT.getControllerPredicate()); + } + + public EndTheFestivities(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{R}"); + + // End the Festivities deals 1 damage to each opponent and each creature and planeswalker they control. + this.getSpellAbility().addEffect(new DamagePlayersEffect(1, TargetController.OPPONENT)); + this.getSpellAbility().addEffect(new DamageAllEffect(1, filter) + .setText("and each creature and planeswalker they control")); + } + + private EndTheFestivities(final EndTheFestivities card) { + super(card); + } + + @Override + public EndTheFestivities copy() { + return new EndTheFestivities(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/EndlessHorizons.java b/Mage.Sets/src/mage/cards/e/EndlessHorizons.java index faee405ca90..99eb91e8a45 100644 --- a/Mage.Sets/src/mage/cards/e/EndlessHorizons.java +++ b/Mage.Sets/src/mage/cards/e/EndlessHorizons.java @@ -8,7 +8,6 @@ import mage.cards.*; import mage.constants.*; import mage.filter.FilterCard; import mage.game.Game; -import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetCard; import mage.target.common.TargetCardInExile; @@ -68,8 +67,7 @@ class EndlessHorizonsEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - Permanent permanent = source.getSourcePermanentOrLKI(game); - if (permanent == null || player == null) { + if (player == null) { return false; } TargetCardInLibrary target = new TargetCardInLibrary(0, Integer.MAX_VALUE, filter); @@ -80,7 +78,7 @@ class EndlessHorizonsEffect extends OneShotEffect { .map(uuid -> player.getLibrary().getCard(uuid, game)) .filter(Objects::nonNull) .forEach(cards::add); - player.moveCardsToExile(cards.getCards(game), source, game, true, CardUtil.getExileZoneId(game, source), permanent.getIdName()); + player.moveCardsToExile(cards.getCards(game), source, game, true, CardUtil.getExileZoneId(game, source), CardUtil.getSourceName(game, source)); player.shuffleLibrary(source, game); return true; } diff --git a/Mage.Sets/src/mage/cards/e/EndlessSands.java b/Mage.Sets/src/mage/cards/e/EndlessSands.java index 24abac6087c..c66e7ef52e8 100644 --- a/Mage.Sets/src/mage/cards/e/EndlessSands.java +++ b/Mage.Sets/src/mage/cards/e/EndlessSands.java @@ -1,40 +1,45 @@ package mage.cards.e; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.common.ExileTargetEffect; -import mage.abilities.effects.common.ReturnCreaturesFromExileEffect; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ExileTargetForSourceEffect; import mage.abilities.mana.ColorlessManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.Zone; +import mage.game.ExileZone; +import mage.game.Game; +import mage.players.Player; import mage.target.common.TargetControlledCreaturePermanent; +import mage.util.CardUtil; + +import java.util.UUID; public final class EndlessSands extends CardImpl { public EndlessSands(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); - + this.subtype.add(SubType.DESERT); - + // {T}: Add {C}. this.addAbility(new ColorlessManaAbility()); - + // {2}, {T}: Exile target creature you control. - Ability exileAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileTargetEffect(this.getId(), this.getIdName()), new ManaCostsImpl("{2}")); + Ability exileAbility = new SimpleActivatedAbility(new ExileTargetForSourceEffect(), new GenericManaCost(2)); exileAbility.addCost(new TapSourceCost()); exileAbility.addTarget(new TargetControlledCreaturePermanent()); this.addAbility(exileAbility); - + // {4}, {T}, Sacrifice Endless Sands: Return each creature card exiled with Endless Sands to the battlefield under its owner's control. - ReturnCreaturesFromExileEffect returnFromExileEffect = new ReturnCreaturesFromExileEffect(this.getId(), true, "Return each creature card exiled with {this} to the battlefield under its owner's control."); - Ability returnAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, returnFromExileEffect, new ManaCostsImpl("{4}")); + Ability returnAbility = new SimpleActivatedAbility(new EndlessSandsEffect(), new GenericManaCost(4)); returnAbility.addCost(new TapSourceCost()); returnAbility.addCost(new SacrificeSourceCost()); this.addAbility(returnAbility); @@ -49,3 +54,34 @@ public final class EndlessSands extends CardImpl { return new EndlessSands(this); } } + +class EndlessSandsEffect extends OneShotEffect { + + EndlessSandsEffect() { + super(Outcome.Benefit); + staticText = "return each creature card exiled with {this} to the battlefield under its owner's control"; + } + + private EndlessSandsEffect(final EndlessSandsEffect effect) { + super(effect); + } + + @Override + public EndlessSandsEffect copy() { + return new EndlessSandsEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source)); + return player != null && exileZone != null + && player.moveCards( + exileZone.getCards(game), Zone.BATTLEFIELD, source, game, + false, false, true, null + ); + } +} diff --git a/Mage.Sets/src/mage/cards/e/Endling.java b/Mage.Sets/src/mage/cards/e/Endling.java index f09d3a8cc4b..651c88a92f6 100644 --- a/Mage.Sets/src/mage/cards/e/Endling.java +++ b/Mage.Sets/src/mage/cards/e/Endling.java @@ -38,7 +38,7 @@ public final class Endling extends CardImpl { // {B}: Endling gains menace until end of turn. this.addAbility(new SimpleActivatedAbility(new GainAbilitySourceEffect( - new MenaceAbility(), Duration.EndOfTurn + new MenaceAbility(false), Duration.EndOfTurn ), new ManaCostsImpl<>("{B}"))); // {B}: Endling gains deathtouch until end of turn. diff --git a/Mage.Sets/src/mage/cards/e/Endoskeleton.java b/Mage.Sets/src/mage/cards/e/Endoskeleton.java index 1ec7c386f62..8651036f3ff 100644 --- a/Mage.Sets/src/mage/cards/e/Endoskeleton.java +++ b/Mage.Sets/src/mage/cards/e/Endoskeleton.java @@ -30,7 +30,7 @@ public final class Endoskeleton extends CardImpl { this.addAbility(new SkipUntapOptionalAbility()); // {2}, {tap}: Target creature gets +0/+3 for as long as Endoskeleton remains tapped. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect( - new BoostTargetEffect(0, 3, Duration.Custom), SourceTappedCondition.instance, + new BoostTargetEffect(0, 3, Duration.Custom), SourceTappedCondition.TAPPED, "target creature gets +0/+3 for as long as {this} remains tapped"), new ManaCostsImpl("{2}")); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent()); diff --git a/Mage.Sets/src/mage/cards/e/EnduringAngel.java b/Mage.Sets/src/mage/cards/e/EnduringAngel.java new file mode 100644 index 00000000000..cb4b85471a4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EnduringAngel.java @@ -0,0 +1,110 @@ +package mage.cards.e; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.continuous.GainAbilityControllerEffect; +import mage.abilities.keyword.DoubleStrikeAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.HexproofAbility; +import mage.abilities.keyword.TransformAbility; +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.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EnduringAngel extends CardImpl { + + public EnduringAngel(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{W}{W}"); + + this.subtype.add(SubType.ANGEL); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + this.secondSideCardClazz = mage.cards.a.AngelicEnforcer.class; + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Double strike + this.addAbility(DoubleStrikeAbility.getInstance()); + + // You have hexproof. + this.addAbility(new SimpleStaticAbility(new GainAbilityControllerEffect(HexproofAbility.getInstance()))); + + // If your life total would be reduced to 0 or less, instead transform Enduring Angel and your life total becomes 3. Then if Enduring Angel didn't transform this way, you lose the game. + this.addAbility(new TransformAbility()); + this.addAbility(new SimpleStaticAbility(new EnduringAngelEffect())); + } + + private EnduringAngel(final EnduringAngel card) { + super(card); + } + + @Override + public EnduringAngel copy() { + return new EnduringAngel(this); + } +} + +class EnduringAngelEffect extends ReplacementEffectImpl { + + EnduringAngelEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "if your life total would be reduced to 0 or less, instead transform {this} " + + "and your life total becomes 3. Then if {this} didn't transform this way, you lose the game"; + } + + private EnduringAngelEffect(final EnduringAngelEffect effect) { + super(effect); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Player player = game.getPlayer(source.getControllerId()); + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (player == null || permanent == null || player.getLife() - event.getAmount() > 0) { + return false; + } + boolean transformed = permanent.transform(source, game); + if (player.getLife() > 3) { + event.setAmount(player.getLife() - 3); + } else if (player.getLife() < 3) { + event.setAmount(0); + player.setLife(3, game, source); + } else { + event.setAmount(0); + } + if (!transformed) { + player.lost(game); + } + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.LOSE_LIFE; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return source.isControlledBy(event.getPlayerId()); + } + + @Override + public EnduringAngelEffect copy() { + return new EnduringAngelEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/EnduringRenewal.java b/Mage.Sets/src/mage/cards/e/EnduringRenewal.java index e55ceb57044..c8003cd4e8a 100644 --- a/Mage.Sets/src/mage/cards/e/EnduringRenewal.java +++ b/Mage.Sets/src/mage/cards/e/EnduringRenewal.java @@ -8,8 +8,7 @@ import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; import mage.abilities.effects.common.continuous.PlayWithHandRevealedEffect; import mage.cards.*; import mage.constants.*; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent; import mage.players.Player; @@ -21,8 +20,6 @@ import java.util.UUID; */ public final class EnduringRenewal extends CardImpl { - private static final FilterPermanent filter = new FilterCreaturePermanent("a creature"); - public EnduringRenewal(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}{W}"); @@ -35,7 +32,7 @@ public final class EnduringRenewal extends CardImpl { // Whenever a creature is put into your graveyard from the battlefield, return it to your hand. this.addAbility(new PutIntoGraveFromBattlefieldAllTriggeredAbility( new ReturnFromGraveyardToHandTargetEffect().setText("return it to your hand"), - false, filter, true, true + false, StaticFilters.FILTER_PERMANENT_A_CREATURE, true, true )); } diff --git a/Mage.Sets/src/mage/cards/e/EnduringSliver.java b/Mage.Sets/src/mage/cards/e/EnduringSliver.java index 953bbfe1b58..076553ca76b 100644 --- a/Mage.Sets/src/mage/cards/e/EnduringSliver.java +++ b/Mage.Sets/src/mage/cards/e/EnduringSliver.java @@ -32,7 +32,7 @@ public final class EnduringSliver extends CardImpl { // Other sliver creatures you control have outlast {2}. this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( new OutlastAbility(new ManaCostsImpl<>("{2}")), Duration.WhileOnBattlefield, - StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS, true + StaticFilters.FILTER_PERMANENT_ALL_SLIVERS, true ).setText("Other Sliver creatures you control have outlast {2}."))); } diff --git a/Mage.Sets/src/mage/cards/e/EnormousEnergyBlade.java b/Mage.Sets/src/mage/cards/e/EnormousEnergyBlade.java new file mode 100644 index 00000000000..a1e10103aa5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EnormousEnergyBlade.java @@ -0,0 +1,48 @@ +package mage.cards.e; + +import mage.abilities.common.AttachedToCreatureSourceTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.TapEnchantedEffect; +import mage.abilities.effects.common.continuous.BoostEquippedEffect; +import mage.abilities.keyword.EquipAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * + * @author Addictiveme + */ +public final class EnormousEnergyBlade extends CardImpl { + + public EnormousEnergyBlade(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{B}"); + + this.subtype.add(SubType.EQUIPMENT); + + // Equipped creature gets +4/+0 + this.addAbility(new SimpleStaticAbility(new BoostEquippedEffect(4, 0))); + + // Whenever Enormous Energy Blade becomes attached to a creature, tap that creature. + TapEnchantedEffect effect = new TapEnchantedEffect(); + effect.setText("tap that creature"); + AttachedToCreatureSourceTriggeredAbility attachTrigger = new AttachedToCreatureSourceTriggeredAbility(effect, false); + attachTrigger.setTriggerPhrase("Whenever {this} becomes attached to a creature, "); + this.addAbility(attachTrigger); + + // Equip {2} + this.addAbility(new EquipAbility(2)); + } + + private EnormousEnergyBlade(final EnormousEnergyBlade card) { + super(card); + } + + @Override + public EnormousEnergyBlade copy() { + return new EnormousEnergyBlade(this); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/e/EnthrallingHold.java b/Mage.Sets/src/mage/cards/e/EnthrallingHold.java index 94ff85687b6..3caa4a63764 100644 --- a/Mage.Sets/src/mage/cards/e/EnthrallingHold.java +++ b/Mage.Sets/src/mage/cards/e/EnthrallingHold.java @@ -30,6 +30,7 @@ public final class EnthrallingHold extends CardImpl { // Enchant creature TargetPermanent auraTarget = new TargetTappedPermanentAsYouCast(StaticFilters.FILTER_PERMANENT_CREATURE); + auraTarget.withChooseHint("must be tapped"); this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.GainControl)); Ability ability = new EnchantAbility(auraTarget.getTargetName()); diff --git a/Mage.Sets/src/mage/cards/e/EnthusiasticMechanaut.java b/Mage.Sets/src/mage/cards/e/EnthusiasticMechanaut.java new file mode 100644 index 00000000000..f973488be37 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EnthusiasticMechanaut.java @@ -0,0 +1,46 @@ +package mage.cards.e; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; +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.common.FilterArtifactCard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EnthusiasticMechanaut extends CardImpl { + + private static final FilterCard filter = new FilterArtifactCard("artifact spells"); + + public EnthusiasticMechanaut(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{U}{R}"); + + this.subtype.add(SubType.GOBLIN); + this.subtype.add(SubType.ARTIFICER); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Artifact spells you cast cost {1} less to cast. + this.addAbility(new SimpleStaticAbility(new SpellsCostReductionControllerEffect(filter, 1))); + } + + private EnthusiasticMechanaut(final EnthusiasticMechanaut card) { + super(card); + } + + @Override + public EnthusiasticMechanaut copy() { + return new EnthusiasticMechanaut(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/EntomberExarch.java b/Mage.Sets/src/mage/cards/e/EntomberExarch.java index e7cec5acdfa..c616dec979e 100644 --- a/Mage.Sets/src/mage/cards/e/EntomberExarch.java +++ b/Mage.Sets/src/mage/cards/e/EntomberExarch.java @@ -1,23 +1,16 @@ - package mage.cards.e; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ReturnToHandTargetEffect; -import mage.cards.Card; +import mage.abilities.effects.common.discard.DiscardCardYouChooseTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; import mage.filter.StaticFilters; -import mage.game.Game; -import mage.players.Player; -import mage.target.TargetCard; import mage.target.common.TargetCardInYourGraveyard; import mage.target.common.TargetOpponent; @@ -36,11 +29,14 @@ public final class EntomberExarch extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); - // When Entomber Exarch enters the battlefield, choose one - Return target creature card from your graveyard to your hand; or target opponent reveals their hand, you choose a noncreature card from it, then that player discards that card. + // When Entomber Exarch enters the battlefield, choose one — + // • Return target creature card from your graveyard to your hand Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnToHandTargetEffect(), false); ability.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); + + // • Target opponent reveals their hand. You choose a noncreature card from it. That player discards that card. Mode mode = new Mode(); - mode.addEffect(new EntomberExarchEffect()); + mode.addEffect(new DiscardCardYouChooseTargetEffect(StaticFilters.FILTER_CARD_NON_CREATURE)); mode.addTarget(new TargetOpponent()); ability.addMode(mode); this.addAbility(ability); @@ -55,38 +51,3 @@ public final class EntomberExarch extends CardImpl { return new EntomberExarch(this); } } - -class EntomberExarchEffect extends OneShotEffect { - - EntomberExarchEffect() { - super(Outcome.Discard); - staticText = "target opponent reveals their hand, you choose a noncreature card from it, then that player discards that card"; - } - - EntomberExarchEffect(final EntomberExarchEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getFirstTarget()); - if (player != null) { - player.revealCards("Entomber Exarch", player.getHand(), game); - Player you = game.getPlayer(source.getControllerId()); - if (you != null) { - TargetCard target = new TargetCard(Zone.HAND, StaticFilters.FILTER_CARD_A_NON_CREATURE); - if (you.choose(Outcome.Benefit, player.getHand(), target, game)) { - Card card = player.getHand().get(target.getFirstTarget(), game); - return player.discard(card, false, source, game); - - } - } - } - return false; - } - - @Override - public EntomberExarchEffect copy() { - return new EntomberExarchEffect(this); - } -} diff --git a/Mage.Sets/src/mage/cards/e/EpharasEnlightenment.java b/Mage.Sets/src/mage/cards/e/EpharasEnlightenment.java index 0b4db0ea677..633da39cbea 100644 --- a/Mage.Sets/src/mage/cards/e/EpharasEnlightenment.java +++ b/Mage.Sets/src/mage/cards/e/EpharasEnlightenment.java @@ -20,7 +20,7 @@ import mage.constants.SubType; import mage.constants.Outcome; import mage.constants.Zone; import mage.counters.CounterType; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; @@ -47,8 +47,8 @@ public final class EpharasEnlightenment extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(FlyingAbility.getInstance(), AttachmentType.AURA))); // Whenever a creature enters the battlefield under your control, you may return Ephara's Enlightenment to its owner's hand. this.addAbility(new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD, - new ReturnToHandSourceEffect(true), - new FilterCreaturePermanent("a creature"), + new ReturnToHandSourceEffect(true), + StaticFilters.FILTER_PERMANENT_A_CREATURE, true)); } diff --git a/Mage.Sets/src/mage/cards/e/EpicConfrontation.java b/Mage.Sets/src/mage/cards/e/EpicConfrontation.java index 8fe1d32f2e3..a4588b71687 100644 --- a/Mage.Sets/src/mage/cards/e/EpicConfrontation.java +++ b/Mage.Sets/src/mage/cards/e/EpicConfrontation.java @@ -26,7 +26,8 @@ public final class EpicConfrontation extends CardImpl { Effect effect = new BoostTargetEffect(1, 2, Duration.EndOfTurn); this.getSpellAbility().addEffect(effect); effect = new FightTargetsEffect(); - effect.setText("It fights target creature you don't control"); + effect.setText("It fights target creature you don't control. " + + "(Each deals damage equal to its power to the other.)"); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); Target target = new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL); diff --git a/Mage.Sets/src/mage/cards/e/EraOfEnlightenment.java b/Mage.Sets/src/mage/cards/e/EraOfEnlightenment.java new file mode 100644 index 00000000000..d7a5761a94e --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EraOfEnlightenment.java @@ -0,0 +1,51 @@ +package mage.cards.e; + +import mage.abilities.common.SagaAbility; +import mage.abilities.effects.common.ExileSagaAndReturnTransformedEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.keyword.ScryEffect; +import mage.abilities.keyword.TransformAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SagaChapter; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EraOfEnlightenment extends CardImpl { + + public EraOfEnlightenment(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); + + this.subtype.add(SubType.SAGA); + this.secondSideCardClazz = mage.cards.h.HandOfEnlightenment.class; + + // (As this Saga enters and after your draw step, add a lore counter.) + SagaAbility sagaAbility = new SagaAbility(this); + + // I — Scry 2. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, new ScryEffect(2)); + + // II — You gain 2 life. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_II, new GainLifeEffect(2)); + + // III — Exile this Saga, then return it to the battlefield transformed under your control. + this.addAbility(new TransformAbility()); + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, new ExileSagaAndReturnTransformedEffect()); + + this.addAbility(sagaAbility); + } + + private EraOfEnlightenment(final EraOfEnlightenment card) { + super(card); + } + + @Override + public EraOfEnlightenment copy() { + return new EraOfEnlightenment(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/EraOfInnovation.java b/Mage.Sets/src/mage/cards/e/EraOfInnovation.java index fc8cf276e50..4d52e9eac1a 100644 --- a/Mage.Sets/src/mage/cards/e/EraOfInnovation.java +++ b/Mage.Sets/src/mage/cards/e/EraOfInnovation.java @@ -1,14 +1,11 @@ - package mage.cards.e; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; +import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.PayEnergyCost; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.DoIfCostPaid; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.counter.GetEnergyCountersControllerEffect; @@ -16,17 +13,18 @@ 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.common.FilterControlledPermanent; import mage.filter.predicate.Predicates; +import java.util.UUID; + /** - * * @author fireshoes */ public final class EraOfInnovation extends CardImpl { - private static final FilterControlledPermanent filter = new FilterControlledPermanent("an artifact or Artificer"); + private static final FilterPermanent filter = new FilterControlledPermanent("an artifact or Artificer"); static { filter.add(Predicates.or(CardType.ARTIFACT.getPredicate(), @@ -34,15 +32,15 @@ public final class EraOfInnovation extends CardImpl { } public EraOfInnovation(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}"); // Whenever an artifact or Artificer enters the battlefield under you control, you may pay {1}. If you do, you get {E}{E}. - Effect effect = new DoIfCostPaid(new GetEnergyCountersControllerEffect(2), new GenericManaCost(1)); - this.addAbility(new EntersBattlefieldAllTriggeredAbility(effect, filter, - "Whenever an artifact or Artificer enters the battlefield under you control, you may pay {1}. If you do, you get {E}{E}.")); + this.addAbility(new EntersBattlefieldControlledTriggeredAbility(new DoIfCostPaid( + new GetEnergyCountersControllerEffect(2), new GenericManaCost(1) + ), filter)); // {E}{E}{E}{E}{E}{E}, Sacrifice Era of Innovation: Draw three cards. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(3), new PayEnergyCost(6)); + Ability ability = new SimpleActivatedAbility(new DrawCardSourceControllerEffect(3), new PayEnergyCost(6)); ability.addCost(new SacrificeSourceCost()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/e/Eradicate.java b/Mage.Sets/src/mage/cards/e/Eradicate.java index e7c34ee9be2..6f28647789a 100644 --- a/Mage.Sets/src/mage/cards/e/Eradicate.java +++ b/Mage.Sets/src/mage/cards/e/Eradicate.java @@ -1,15 +1,11 @@ - package mage.cards.e; import java.util.UUID; -import mage.ObjectColor; import mage.abilities.effects.common.ExileTargetAndSearchGraveyardHandLibraryEffect; 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; /** @@ -17,19 +13,13 @@ import mage.target.common.TargetCreaturePermanent; * @author LevelX2 */ public final class Eradicate extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } public Eradicate(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{B}{B}"); - // Exile target nonblack creature. Search its controller's graveyard, hand, and library for all cards // with the same name as that creature and exile them. Then that player shuffles their library. - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); this.getSpellAbility().addEffect(new ExileTargetAndSearchGraveyardHandLibraryEffect(false, "its controller's","all cards with the same name as that creature")); } @@ -42,5 +32,3 @@ public final class Eradicate extends CardImpl { return new Eradicate(this); } } - - \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/e/ErraticMutation.java b/Mage.Sets/src/mage/cards/e/ErraticMutation.java index a7231d920ca..cade3c25a7e 100644 --- a/Mage.Sets/src/mage/cards/e/ErraticMutation.java +++ b/Mage.Sets/src/mage/cards/e/ErraticMutation.java @@ -1,4 +1,3 @@ - package mage.cards.e; import java.util.UUID; @@ -82,7 +81,7 @@ class ErraticMutationEffect extends OneShotEffect { int boostValue = nonLandCard.getManaValue(); // unboost target ContinuousEffect effect = new BoostTargetEffect(boostValue, -boostValue, Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(this.getTargetPointer().getFirst(game, source))); + effect.setTargetPointer(new FixedTarget(this.getTargetPointer().getFirst(game, source), game)); game.addEffect(effect, source); } // put the cards on the bottom of the library in any order diff --git a/Mage.Sets/src/mage/cards/e/EruthTormentedProphet.java b/Mage.Sets/src/mage/cards/e/EruthTormentedProphet.java new file mode 100644 index 00000000000..26b95c9d1b5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EruthTormentedProphet.java @@ -0,0 +1,92 @@ +package mage.cards.e; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.players.Player; +import mage.util.CardUtil; + +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EruthTormentedProphet extends CardImpl { + + public EruthTormentedProphet(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{R}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(2); + this.toughness = new MageInt(4); + + // If you would draw a card, exile the top two cards of your library instead. You may play those cards this turn. + this.addAbility(new SimpleStaticAbility(new EruthTormentedProphetEffect())); + } + + private EruthTormentedProphet(final EruthTormentedProphet card) { + super(card); + } + + @Override + public EruthTormentedProphet copy() { + return new EruthTormentedProphet(this); + } +} + +class EruthTormentedProphetEffect extends ReplacementEffectImpl { + + EruthTormentedProphetEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "if you would draw a card, exile the top two cards " + + "of your library instead. You may play those cards this turn"; + } + + private EruthTormentedProphetEffect(final EruthTormentedProphetEffect effect) { + super(effect); + } + + @Override + public EruthTormentedProphetEffect copy() { + return new EruthTormentedProphetEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Player player = game.getPlayer(event.getPlayerId()); + if (player == null) { + return true; + } + Set cards = player.getLibrary().getTopCards(game, 2); + player.moveCards(cards, Zone.EXILED, source, game); + for (Card card : cards) { + CardUtil.makeCardPlayable(game, source, card, Duration.EndOfTurn, false); + } + return true; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DRAW_CARD; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return source.isControlledBy(event.getPlayerId()); + } +} diff --git a/Mage.Sets/src/mage/cards/e/EscapeProtocol.java b/Mage.Sets/src/mage/cards/e/EscapeProtocol.java index 4aa3a6b6027..a67aebae611 100644 --- a/Mage.Sets/src/mage/cards/e/EscapeProtocol.java +++ b/Mage.Sets/src/mage/cards/e/EscapeProtocol.java @@ -38,7 +38,7 @@ public final class EscapeProtocol extends CardImpl { ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( new ExileTargetForSourceEffect(), false, "exile target artifact or creature you control, " + - "then return it to the battlefield under its owner's control." + "then return it to the battlefield under its owner's control" ); ability.addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false)); ability.addTarget(new TargetPermanent(filter)); diff --git a/Mage.Sets/src/mage/cards/e/EscapeToTheWilds.java b/Mage.Sets/src/mage/cards/e/EscapeToTheWilds.java index 7b42487b16f..0fbc4cd237b 100644 --- a/Mage.Sets/src/mage/cards/e/EscapeToTheWilds.java +++ b/Mage.Sets/src/mage/cards/e/EscapeToTheWilds.java @@ -41,9 +41,9 @@ class EscapeToTheWildsEffect extends OneShotEffect { EscapeToTheWildsEffect() { super(Outcome.PlayForFree); - this.staticText = "Exile the top five cards of your library. " + - "You may play cards exiled this way until the end of your next turn.
" + - "You may play an additional land this turn."; + this.staticText = "Exile the top five cards of your library. " + + "You may play cards exiled this way until the end of your next turn.
" + + "You may play an additional land this turn."; } private EscapeToTheWildsEffect(final EscapeToTheWildsEffect effect) { @@ -67,7 +67,7 @@ class EscapeToTheWildsEffect extends OneShotEffect { cards.getCards(game).stream().forEach(card -> { ContinuousEffect effect = new EscapeToTheWildsMayPlayEffect(); - effect.setTargetPointer(new FixedTarget(card.getId())); + effect.setTargetPointer(new FixedTarget(card.getId(), game)); game.addEffect(effect, source); }); game.addEffect(new PlayAdditionalLandsControllerEffect(1, Duration.EndOfTurn), source); diff --git a/Mage.Sets/src/mage/cards/e/EssenceSliver.java b/Mage.Sets/src/mage/cards/e/EssenceSliver.java index 058998c2874..461faaa28a9 100644 --- a/Mage.Sets/src/mage/cards/e/EssenceSliver.java +++ b/Mage.Sets/src/mage/cards/e/EssenceSliver.java @@ -83,7 +83,7 @@ class DealsDamageAllTriggeredAbility extends TriggeredAbilityImpl { @Override public String getTriggerPhrase() { - return "Whenever a Sliver deals damage, its controller" ; + return "Whenever a Sliver deals damage, " ; } } diff --git a/Mage.Sets/src/mage/cards/e/EstridTheMasked.java b/Mage.Sets/src/mage/cards/e/EstridTheMasked.java index 06d4ff56016..796383b3ea0 100644 --- a/Mage.Sets/src/mage/cards/e/EstridTheMasked.java +++ b/Mage.Sets/src/mage/cards/e/EstridTheMasked.java @@ -4,7 +4,6 @@ import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.common.CanBeYourCommanderAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.MillCardsControllerEffect; @@ -46,7 +45,7 @@ public final class EstridTheMasked extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.ESTRID); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +2: Untap each enchanted permanent you control. this.addAbility(new LoyaltyAbility(new UntapAllControllerEffect( diff --git a/Mage.Sets/src/mage/cards/e/EstwaldShieldbasher.java b/Mage.Sets/src/mage/cards/e/EstwaldShieldbasher.java new file mode 100644 index 00000000000..c20399c0747 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EstwaldShieldbasher.java @@ -0,0 +1,44 @@ +package mage.cards.e; + +import mage.MageInt; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.keyword.IndestructibleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EstwaldShieldbasher extends CardImpl { + + public EstwaldShieldbasher(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(4); + this.toughness = new MageInt(2); + + // Whenever Estwald Shieldbasher attacks, you may pay {1}. If you do, it gains indestructible until end of turn. + this.addAbility(new AttacksTriggeredAbility(new DoIfCostPaid(new GainAbilitySourceEffect( + IndestructibleAbility.getInstance(), Duration.EndOfTurn + ).setText("it gains indestructible until end of turn"), new GenericManaCost(1)))); + } + + private EstwaldShieldbasher(final EstwaldShieldbasher card) { + super(card); + } + + @Override + public EstwaldShieldbasher copy() { + return new EstwaldShieldbasher(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/EtchingOfKumano.java b/Mage.Sets/src/mage/cards/e/EtchingOfKumano.java new file mode 100644 index 00000000000..55da585e7cf --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EtchingOfKumano.java @@ -0,0 +1,88 @@ +package mage.cards.e; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.constants.*; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.watchers.common.DamagedByControlledWatcher; + +/** + * + * @author weirddan455 + */ +public final class EtchingOfKumano extends CardImpl { + + public EtchingOfKumano(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, ""); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SHAMAN); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + this.color.setRed(true); + this.nightCard = true; + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // If a creature dealt damage this turn by a source you controlled would die, exile it instead. + this.addAbility(new SimpleStaticAbility(new EtchingOfKumanoReplacementEffect()), new DamagedByControlledWatcher()); + } + + private EtchingOfKumano(final EtchingOfKumano card) { + super(card); + } + + @Override + public EtchingOfKumano copy() { + return new EtchingOfKumano(this); + } +} + +class EtchingOfKumanoReplacementEffect extends ReplacementEffectImpl { + + public EtchingOfKumanoReplacementEffect() { + super(Duration.WhileOnBattlefield, Outcome.Exile); + this.staticText = "If a creature dealt damage this turn by a source you controlled would die, exile it instead"; + } + + private EtchingOfKumanoReplacementEffect(final EtchingOfKumanoReplacementEffect effect) { + super(effect); + } + + @Override + public EtchingOfKumanoReplacementEffect copy() { + return new EtchingOfKumanoReplacementEffect(this); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + ((ZoneChangeEvent) event).setToZone(Zone.EXILED); + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + ZoneChangeEvent zce = (ZoneChangeEvent) event; + if (zce.isDiesEvent()) { + DamagedByControlledWatcher watcher = game.getState().getWatcher(DamagedByControlledWatcher.class, source.getControllerId()); + if (watcher != null) { + return watcher.wasDamaged(zce.getTarget(), game); + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/e/EternalThirst.java b/Mage.Sets/src/mage/cards/e/EternalThirst.java index 135d415bffd..48dea680727 100644 --- a/Mage.Sets/src/mage/cards/e/EternalThirst.java +++ b/Mage.Sets/src/mage/cards/e/EternalThirst.java @@ -15,7 +15,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.counters.CounterType; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; @@ -24,17 +24,11 @@ import mage.target.common.TargetCreaturePermanent; * @author emerald000 */ public final class EternalThirst extends CardImpl { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature an opponent controls"); - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } public EternalThirst(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{B}"); this.subtype.add(SubType.AURA); - // Enchant creature TargetPermanent auraTarget = new TargetCreaturePermanent(); this.getSpellAbility().addTarget(auraTarget); @@ -47,12 +41,10 @@ public final class EternalThirst extends CardImpl { effect.setText("Enchanted creature has lifelink"); ability = new SimpleStaticAbility(Zone.BATTLEFIELD, effect); // and "Whenever a creature an opponent controls dies, put a +1/+1 counter on this creature." - effect = new GainAbilityAttachedEffect(new DiesCreatureTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false, filter), AttachmentType.AURA); + effect = new GainAbilityAttachedEffect(new DiesCreatureTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false, StaticFilters.FILTER_OPPONENTS_PERMANENT_A_CREATURE), AttachmentType.AURA); ability.addEffect(effect); effect.setText("and \"Whenever a creature an opponent controls dies, put a +1/+1 counter on this creature.\""); this.addAbility(ability); - - } private EternalThirst(final EternalThirst card) { diff --git a/Mage.Sets/src/mage/cards/e/EtherealInvestigator.java b/Mage.Sets/src/mage/cards/e/EtherealInvestigator.java new file mode 100644 index 00000000000..a42ce321bd3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EtherealInvestigator.java @@ -0,0 +1,50 @@ +package mage.cards.e; + +import mage.MageInt; +import mage.abilities.common.DrawSecondCardTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.dynamicvalue.common.OpponentsCount; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.keyword.InvestigateEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.SpiritWhiteToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EtherealInvestigator extends CardImpl { + + public EtherealInvestigator(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); + + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When Ethereal Investigator enters the battlefield, investigate X times, where X is the number of opponents you have. + this.addAbility(new EntersBattlefieldTriggeredAbility(new InvestigateEffect(OpponentsCount.instance))); + + // Whenever you draw your second card each turn, create a 1/1 white Spirit creature token with flying. + this.addAbility(new DrawSecondCardTriggeredAbility( + new CreateTokenEffect(new SpiritWhiteToken()), false + )); + } + + private EtherealInvestigator(final EtherealInvestigator card) { + super(card); + } + + @Override + public EtherealInvestigator copy() { + return new EtherealInvestigator(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/EtherealValkyrie.java b/Mage.Sets/src/mage/cards/e/EtherealValkyrie.java index 1da51c68698..e936f23f42a 100644 --- a/Mage.Sets/src/mage/cards/e/EtherealValkyrie.java +++ b/Mage.Sets/src/mage/cards/e/EtherealValkyrie.java @@ -98,7 +98,7 @@ class EtherealValkyrieTriggeredAbility extends TriggeredAbilityImpl { @Override public String getTriggerPhrase() { - return "Whenever {this} enters the battlefield or attacks, " ; + return "Whenever {this} enters the battlefield or attacks, "; } } @@ -167,6 +167,11 @@ class EtherealValkyrieEffect extends OneShotEffect { // all done pre-processing so stick the foretell cost effect onto the main card // note that the card is not foretell'd into exile, it is put into exile and made foretold if (foretellAbility != null) { + // copy source and use it for the foretold effect on the exiled card + // bug #8673 + Ability copiedSource = source.copy(); + copiedSource.newId(); + copiedSource.setSourceId(exileCard.getId()); game.getState().setValue(exileCard.getMainCard().getId().toString() + "Foretell Turn Number", game.getTurnNum()); UUID exileId = CardUtil.getExileZoneId(exileCard.getMainCard().getId().toString() + "foretellAbility", game); controller.moveCardsToExile(exileCard, source, game, true, exileId, " Foretell Turn Number: " + game.getTurnNum()); @@ -176,7 +181,7 @@ class EtherealValkyrieEffect extends OneShotEffect { game.getState().addOtherAbility(exileCard, foretellAbility); foretellAbility.activate(game, true); ContinuousEffect effect = foretellAbility.new ForetellAddCostEffect(new MageObjectReference(exileCard, game)); - game.addEffect(effect, source); + game.addEffect(effect, copiedSource); game.fireEvent(GameEvent.getEvent(GameEvent.EventType.FORETOLD, exileCard.getId(), null, null)); return true; } diff --git a/Mage.Sets/src/mage/cards/e/EtrataTheSilencer.java b/Mage.Sets/src/mage/cards/e/EtrataTheSilencer.java index 6ec4e55eb9b..5c905c75b98 100644 --- a/Mage.Sets/src/mage/cards/e/EtrataTheSilencer.java +++ b/Mage.Sets/src/mage/cards/e/EtrataTheSilencer.java @@ -101,7 +101,7 @@ class EtrataTheSilencerTriggeredAbility extends TriggeredAbilityImpl { + "and put a hit counter on that card. " + "That player loses the game if they own three or more " + "exiled cards with hit counters on them. " - + "{this}'s owner shuffles {this} into their library"; + + "{this}'s owner shuffles {this} into their library."; } } diff --git a/Mage.Sets/src/mage/cards/e/EvangelOfHeliod.java b/Mage.Sets/src/mage/cards/e/EvangelOfHeliod.java index 008265216f5..195da71958a 100644 --- a/Mage.Sets/src/mage/cards/e/EvangelOfHeliod.java +++ b/Mage.Sets/src/mage/cards/e/EvangelOfHeliod.java @@ -28,6 +28,7 @@ public final class EvangelOfHeliod extends CardImpl { // When Evangel of Heliod enters the battlefield, create a number of 1/1 white Soldier creature tokens equal to your devotion to white. this.addAbility(new EntersBattlefieldTriggeredAbility( new CreateTokenEffect(new SoldierToken(), DevotionCount.W) + .setText("create a number of 1/1 white Soldier creature tokens equal to your devotion to white") ).addHint(DevotionCount.W.getHint())); } diff --git a/Mage.Sets/src/mage/cards/e/EverAfter.java b/Mage.Sets/src/mage/cards/e/EverAfter.java index f27e9391d4d..687eeae9a9e 100644 --- a/Mage.Sets/src/mage/cards/e/EverAfter.java +++ b/Mage.Sets/src/mage/cards/e/EverAfter.java @@ -1,4 +1,3 @@ - package mage.cards.e; import java.util.UUID; @@ -9,7 +8,7 @@ import mage.abilities.effects.common.continuous.BecomesBlackZombieAdditionEffect import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.common.FilterCreatureCard; +import mage.filter.StaticFilters; import mage.target.common.TargetCardInYourGraveyard; /** @@ -24,7 +23,7 @@ public final class EverAfter extends CardImpl { // Return up to two target creature cards from your graveyard to the battlefield. Each of those creatures is a black Zombie in addition // to its other colors and types. Put Ever After on the bottom of its owner's library. this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect()); - this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, 2, new FilterCreatureCard("creature cards from your graveyard"))); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, 2, StaticFilters.FILTER_CARD_CREATURES_YOUR_GRAVEYARD)); Effect effect = new BecomesBlackZombieAdditionEffect(); effect.setText("Each of those creatures is a black Zombie in addition to its other colors and types"); this.getSpellAbility().addEffect(effect); diff --git a/Mage.Sets/src/mage/cards/e/EvergloveCourier.java b/Mage.Sets/src/mage/cards/e/EvergloveCourier.java index f78fcc5c79c..2e2138b2fa3 100644 --- a/Mage.Sets/src/mage/cards/e/EvergloveCourier.java +++ b/Mage.Sets/src/mage/cards/e/EvergloveCourier.java @@ -44,10 +44,10 @@ public final class EvergloveCourier extends CardImpl { this.addAbility(new SkipUntapOptionalAbility()); // {2}{G}, {tap}: Target Elf creature gets +2/+2 and has trample for as long as Everglove Courier remains tapped. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect( - new BoostTargetEffect(2, 2, Duration.Custom), SourceTappedCondition.instance, + new BoostTargetEffect(2, 2, Duration.Custom), SourceTappedCondition.TAPPED, "target Elf creature gets +2/+2"), new ManaCostsImpl("{2}{G}")); ability.addEffect(new ConditionalContinuousEffect(new GainAbilityTargetEffect(TrampleAbility.getInstance(), - Duration.Custom), SourceTappedCondition.instance,"and has trample for as long as {this} remains tapped")); + Duration.Custom), SourceTappedCondition.TAPPED,"and has trample for as long as {this} remains tapped")); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent(filter)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/e/EvolutionaryEscalation.java b/Mage.Sets/src/mage/cards/e/EvolutionaryEscalation.java index 06a2d3bdf7e..107995c3763 100644 --- a/Mage.Sets/src/mage/cards/e/EvolutionaryEscalation.java +++ b/Mage.Sets/src/mage/cards/e/EvolutionaryEscalation.java @@ -12,7 +12,7 @@ import mage.constants.Outcome; import mage.constants.TargetController; import mage.counters.Counter; import mage.counters.CounterType; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.Target; @@ -24,11 +24,6 @@ import mage.target.common.TargetCreaturePermanent; * @author spjspj */ public final class EvolutionaryEscalation extends CardImpl { - private static final FilterCreaturePermanent filterOpponentCreature = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filterOpponentCreature.add(TargetController.OPPONENT.getControllerPredicate()); - } public EvolutionaryEscalation(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{G}"); @@ -37,7 +32,7 @@ public final class EvolutionaryEscalation extends CardImpl { EvolutionaryEscalationEffect effect = new EvolutionaryEscalationEffect(); Ability ability = new BeginningOfUpkeepTriggeredAbility(effect, TargetController.YOU, false); ability.addTarget(new TargetControlledCreaturePermanent()); - ability.addTarget(new TargetCreaturePermanent(filterOpponentCreature)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/e/ExavaRakdosBloodWitch.java b/Mage.Sets/src/mage/cards/e/ExavaRakdosBloodWitch.java index b32e9dab48a..17e9acf01c9 100644 --- a/Mage.Sets/src/mage/cards/e/ExavaRakdosBloodWitch.java +++ b/Mage.Sets/src/mage/cards/e/ExavaRakdosBloodWitch.java @@ -11,9 +11,7 @@ import mage.abilities.keyword.UnleashAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.counters.CounterType; -import mage.filter.FilterPermanent; -import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.filter.StaticFilters; /** * @@ -21,14 +19,6 @@ import mage.filter.predicate.mageobject.AnotherPredicate; */ public final class ExavaRakdosBloodWitch 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()); - filter.add(AnotherPredicate.instance); - } - static final String rule = "Each other creature you control with a +1/+1 counter on it has haste"; public ExavaRakdosBloodWitch(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{B}{R}"); @@ -46,7 +36,11 @@ public final class ExavaRakdosBloodWitch extends CardImpl { // Unleash this.addAbility(new UnleashAbility()); // Each other creature you control with a +1/+1 counter on it has haste. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(HasteAbility.getInstance(), Duration.WhileOnBattlefield, filter, rule))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect( + HasteAbility.getInstance(), + Duration.WhileOnBattlefield, + StaticFilters.FILTER_OTHER_CONTROLLED_CREATURE_P1P1, + rule))); } diff --git a/Mage.Sets/src/mage/cards/e/ExecutionersCapsule.java b/Mage.Sets/src/mage/cards/e/ExecutionersCapsule.java index e4c4881843a..a8ef97782e3 100644 --- a/Mage.Sets/src/mage/cards/e/ExecutionersCapsule.java +++ b/Mage.Sets/src/mage/cards/e/ExecutionersCapsule.java @@ -1,8 +1,6 @@ - package mage.cards.e; import java.util.UUID; -import mage.ObjectColor; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.common.TapSourceCost; @@ -12,9 +10,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Zone; -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; /** @@ -23,20 +19,13 @@ import mage.target.common.TargetCreaturePermanent; */ public final class ExecutionersCapsule extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public ExecutionersCapsule(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{B}"); - SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new ManaCostsImpl("{1}{B}")); ability.addCost(new TapSourceCost()); ability.addCost(new SacrificeSourceCost()); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/e/ExpeditionSupplier.java b/Mage.Sets/src/mage/cards/e/ExpeditionSupplier.java new file mode 100644 index 00000000000..b79994cae30 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/ExpeditionSupplier.java @@ -0,0 +1,44 @@ +package mage.cards.e; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility; +import mage.abilities.effects.common.ConjureCardEffect; +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 java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ExpeditionSupplier extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent(SubType.HUMAN, "Human"); + + public ExpeditionSupplier(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Whenever Expedition Supplier or another Human enters the battlefield under your control, conjure a card named Utility Knife onto the battlefield. This ability triggers only once each turn. + this.addAbility(new EntersBattlefieldThisOrAnotherTriggeredAbility(new ConjureCardEffect( + "Utility Knife", Zone.BATTLEFIELD, 1 + ), filter, false, true).setTriggersOnce(true)); + } + + private ExpeditionSupplier(final ExpeditionSupplier card) { + super(card); + } + + @Override + public ExpeditionSupplier copy() { + return new ExpeditionSupplier(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/ExperimentKraj.java b/Mage.Sets/src/mage/cards/e/ExperimentKraj.java index b6df994db9e..9967d4ed624 100644 --- a/Mage.Sets/src/mage/cards/e/ExperimentKraj.java +++ b/Mage.Sets/src/mage/cards/e/ExperimentKraj.java @@ -56,7 +56,8 @@ public final class ExperimentKraj extends CardImpl { class ExperimentKrajEffect extends ContinuousEffectImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("each other creature with a +1/+1 counter on it"); + static { filter.add(CounterType.P1P1.getPredicate()); filter.add(AnotherPredicate.instance); diff --git a/Mage.Sets/src/mage/cards/e/ExperimentalSynthesizer.java b/Mage.Sets/src/mage/cards/e/ExperimentalSynthesizer.java new file mode 100644 index 00000000000..b2a594ab4bd --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/ExperimentalSynthesizer.java @@ -0,0 +1,46 @@ +package mage.cards.e; + +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.common.EntersBattlefieldOrLeavesSourceTriggeredAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.ExileTopXMayPlayUntilEndOfTurnEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.game.permanent.token.SamuraiToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ExperimentalSynthesizer extends CardImpl { + + public ExperimentalSynthesizer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{R}"); + + // When Experimental Synthesizer enters or leaves the battlefield, exile the top card of your library. Until end of turn, you may play that card. + this.addAbility(new EntersBattlefieldOrLeavesSourceTriggeredAbility( + new ExileTopXMayPlayUntilEndOfTurnEffect(1), false + )); + + // {2}{R}, Sacrifice Experimental Synthesizer: Create a 2/2 white Samurai creature token with vigilance. Activate only as a sorcery. + Ability ability = new ActivateAsSorceryActivatedAbility( + new CreateTokenEffect(new SamuraiToken()), new ManaCostsImpl<>("{2}{R}") + ); + ability.addCost(new SacrificeSourceCost()); + this.addAbility(ability); + } + + private ExperimentalSynthesizer(final ExperimentalSynthesizer card) { + super(card); + } + + @Override + public ExperimentalSynthesizer copy() { + return new ExperimentalSynthesizer(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/ExplosiveEntry.java b/Mage.Sets/src/mage/cards/e/ExplosiveEntry.java new file mode 100644 index 00000000000..77186eda7e6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/ExplosiveEntry.java @@ -0,0 +1,42 @@ +package mage.cards.e; + +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.counters.CounterType; +import mage.target.common.TargetArtifactPermanent; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetpointer.FirstTargetPointer; +import mage.target.targetpointer.SecondTargetPointer; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ExplosiveEntry extends CardImpl { + + public ExplosiveEntry(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{R}"); + + // Destroy up to one target artifact. Put a +1/+1 counter on up to one target creature. + this.getSpellAbility().addEffect(new DestroyTargetEffect("destroy up to one target artifact") + .setTargetPointer(new FirstTargetPointer())); + this.getSpellAbility().addTarget(new TargetArtifactPermanent(0, 1)); + this.getSpellAbility().addEffect(new AddCountersTargetEffect(CounterType.P1P1.createInstance()) + .setText("Put a +1/+1 counter on up to one target creature") + .setTargetPointer(new SecondTargetPointer())); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(0, 1)); + } + + private ExplosiveEntry(final ExplosiveEntry card) { + super(card); + } + + @Override + public ExplosiveEntry copy() { + return new ExplosiveEntry(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/ExplosiveSingularity.java b/Mage.Sets/src/mage/cards/e/ExplosiveSingularity.java new file mode 100644 index 00000000000..b7b09459fab --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/ExplosiveSingularity.java @@ -0,0 +1,90 @@ +package mage.cards.e; + +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.TapTargetCost; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetAnyTarget; +import mage.target.common.TargetControlledPermanent; +import mage.util.CardUtil; + +import java.util.List; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ExplosiveSingularity extends CardImpl { + + public ExplosiveSingularity(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{8}{R}{R}"); + + // As an additional cost to cast this spell, you may tap any number of untapped creatures you control. This spell costs {1} less to cast for each creature tapped this way. + this.getSpellAbility().addCost(new TapTargetCost(new TargetControlledPermanent( + 0, Integer.MAX_VALUE, StaticFilters.FILTER_CONTROLLED_UNTAPPED_CREATURES, false + )).setText("you may tap any number of untapped creatures you control. " + + "This spell costs {1} less to cast for each creature tapped this way")); + Ability ability = new SimpleStaticAbility(Zone.ALL, new ExplosiveSingularityEffect()); + ability.setRuleVisible(false); + this.addAbility(ability); + + // Explosive Singularity deals 10 damage to any target. + this.getSpellAbility().addEffect(new DamageTargetEffect(10)); + this.getSpellAbility().addTarget(new TargetAnyTarget()); + } + + private ExplosiveSingularity(final ExplosiveSingularity card) { + super(card); + } + + @Override + public ExplosiveSingularity copy() { + return new ExplosiveSingularity(this); + } +} + +class ExplosiveSingularityEffect extends CostModificationEffectImpl { + + ExplosiveSingularityEffect() { + super(Duration.WhileOnStack, Outcome.Benefit, CostModificationType.REDUCE_COST); + } + + private ExplosiveSingularityEffect(final ExplosiveSingularityEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source, Ability abilityToModify) { + SpellAbility spellAbility = (SpellAbility) abilityToModify; + int reduction; + if (game.inCheckPlayableState()) { + reduction = game.getBattlefield().count( + StaticFilters.FILTER_CONTROLLED_UNTAPPED_CREATURES, + source.getSourceId(), source.getControllerId(), game + ); + } else { + reduction = ((List) spellAbility.getEffects().get(0).getValue("tappedPermanents")).size(); + } + CardUtil.adjustCost(spellAbility, reduction); + return true; + } + + @Override + public boolean applies(Ability abilityToModify, Ability source, Game game) { + return abilityToModify instanceof SpellAbility + && abilityToModify.getSourceId().equals(source.getSourceId()); + } + + @Override + public ExplosiveSingularityEffect copy() { + return new ExplosiveSingularityEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/ExposeToDaylight.java b/Mage.Sets/src/mage/cards/e/ExposeToDaylight.java index 7de10992de1..3d0198e9fb1 100644 --- a/Mage.Sets/src/mage/cards/e/ExposeToDaylight.java +++ b/Mage.Sets/src/mage/cards/e/ExposeToDaylight.java @@ -20,7 +20,7 @@ public final class ExposeToDaylight extends CardImpl { // Destroy target artifact or enchantment. Scry 1. this.getSpellAbility().addEffect(new DestroyTargetEffect()); - this.getSpellAbility().addEffect(new ScryEffect(1)); + this.getSpellAbility().addEffect(new ScryEffect(1, false)); this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_ENCHANTMENT)); } diff --git a/Mage.Sets/src/mage/cards/e/ExtricatorOfSin.java b/Mage.Sets/src/mage/cards/e/ExtricatorOfSin.java index 37699775eb2..191b698e828 100644 --- a/Mage.Sets/src/mage/cards/e/ExtricatorOfSin.java +++ b/Mage.Sets/src/mage/cards/e/ExtricatorOfSin.java @@ -42,7 +42,6 @@ public final class ExtricatorOfSin extends CardImpl { this.power = new MageInt(0); this.toughness = new MageInt(3); - this.transformable = true; this.secondSideCardClazz = ExtricatorOfFlesh.class; // When Extricator of Sin enters the battlefield, you may sacrifice another permanent. If you do, create a 3/2 colorless Eldrazi Horror creature token. @@ -52,7 +51,7 @@ public final class ExtricatorOfSin extends CardImpl { // Delirium — At the beginning of your upkeep, if there are four or more card types among cards in your graveyard, transform Extricator of Sin. this.addAbility(new TransformAbility()); this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new TransformSourceEffect(true), TargetController.YOU, false), + new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new TransformSourceEffect(), TargetController.YOU, false), DeliriumCondition.instance, "Delirium — At the beginning of your upkeep, if there are four or more card types among cards in your graveyard, " + " transform {this}.") diff --git a/Mage.Sets/src/mage/cards/e/ExuberantWolfbear.java b/Mage.Sets/src/mage/cards/e/ExuberantWolfbear.java index 7b2432fd808..c676b9cc540 100644 --- a/Mage.Sets/src/mage/cards/e/ExuberantWolfbear.java +++ b/Mage.Sets/src/mage/cards/e/ExuberantWolfbear.java @@ -55,7 +55,7 @@ class ExuberantWolfbearEffect extends OneShotEffect { ExuberantWolfbearEffect() { super(Outcome.Benefit); - staticText = "change the base power and toughness of target Human you control " + + staticText = "you may change the base power and toughness of target Human you control " + "to {this}'s power and toughness until end of turn"; } diff --git a/Mage.Sets/src/mage/cards/e/EyeblightAssassin.java b/Mage.Sets/src/mage/cards/e/EyeblightAssassin.java index a8a913b90db..a4e9edb5c25 100644 --- a/Mage.Sets/src/mage/cards/e/EyeblightAssassin.java +++ b/Mage.Sets/src/mage/cards/e/EyeblightAssassin.java @@ -11,8 +11,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; /** @@ -20,12 +19,6 @@ import mage.target.common.TargetCreaturePermanent; * @author fireshoes */ public final class EyeblightAssassin extends CardImpl { - - private static final FilterCreaturePermanent filterOpponentCreature = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filterOpponentCreature.add(TargetController.OPPONENT.getControllerPredicate()); - } public EyeblightAssassin(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{B}"); @@ -36,7 +29,7 @@ public final class EyeblightAssassin extends CardImpl { // When Eyeblight Assassin enters the battlefield, target creature an opponent controls gets -1/-1 until end of turn. Ability ability = new EntersBattlefieldTriggeredAbility(new BoostTargetEffect(-1,-1, Duration.EndOfTurn)); - ability.addTarget(new TargetCreaturePermanent(filterOpponentCreature)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/e/EyesEverywhere.java b/Mage.Sets/src/mage/cards/e/EyesEverywhere.java index 701e6ef5606..8407fa700ed 100644 --- a/Mage.Sets/src/mage/cards/e/EyesEverywhere.java +++ b/Mage.Sets/src/mage/cards/e/EyesEverywhere.java @@ -26,7 +26,7 @@ public final class EyesEverywhere extends CardImpl { // At the beginning of your upkeep, scry 1. this.addAbility(new BeginningOfUpkeepTriggeredAbility( - Zone.BATTLEFIELD, new ScryEffect(1), + Zone.BATTLEFIELD, new ScryEffect(1, false), TargetController.YOU, false )); diff --git a/Mage.Sets/src/mage/cards/e/EzuriClawOfProgress.java b/Mage.Sets/src/mage/cards/e/EzuriClawOfProgress.java index edd8234b6f3..445c4494935 100644 --- a/Mage.Sets/src/mage/cards/e/EzuriClawOfProgress.java +++ b/Mage.Sets/src/mage/cards/e/EzuriClawOfProgress.java @@ -34,7 +34,7 @@ public final class EzuriClawOfProgress extends CardImpl { filter2.add(AnotherPredicate.instance); } - String rule = "Whenever a creature with power 2 or less enters the battlefield under your control, you get an experience counter."; + private static final String rule = "Whenever a creature with power 2 or less enters the battlefield under your control, you get an experience counter."; public EzuriClawOfProgress(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{U}"); diff --git a/Mage.Sets/src/mage/cards/f/FableOfTheMirrorBreaker.java b/Mage.Sets/src/mage/cards/f/FableOfTheMirrorBreaker.java new file mode 100644 index 00000000000..d2777c8eba9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FableOfTheMirrorBreaker.java @@ -0,0 +1,53 @@ +package mage.cards.f; + +import mage.abilities.common.SagaAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.ExileSagaAndReturnTransformedEffect; +import mage.abilities.effects.common.discard.DiscardAndDrawThatManyEffect; +import mage.abilities.keyword.TransformAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SagaChapter; +import mage.constants.SubType; +import mage.game.permanent.token.FableOfTheMirrorBreakerToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FableOfTheMirrorBreaker extends CardImpl { + + public FableOfTheMirrorBreaker(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{R}"); + + this.subtype.add(SubType.SAGA); + this.secondSideCardClazz = mage.cards.r.ReflectionOfKikiJiki.class; + + // (As this Saga enters and after your draw step, add a lore counter.) + SagaAbility sagaAbility = new SagaAbility(this); + + // I — Create a 2/2 red Goblin Shaman creature token with "Whenever this creature attacks, create a Treasure token." + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, new CreateTokenEffect(new FableOfTheMirrorBreakerToken())); + + // II — You may discard up to two cards. If you do, draw that many cards. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_II, new DiscardAndDrawThatManyEffect(2) + .setText("you may discard up to two cards. If you do, draw that many cards")); + + // III — Exile this Saga, then return it to the battlefield transformed under your control. + this.addAbility(new TransformAbility()); + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, new ExileSagaAndReturnTransformedEffect()); + + this.addAbility(sagaAbility); + } + + private FableOfTheMirrorBreaker(final FableOfTheMirrorBreaker card) { + super(card); + } + + @Override + public FableOfTheMirrorBreaker copy() { + return new FableOfTheMirrorBreaker(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FacelessAgent.java b/Mage.Sets/src/mage/cards/f/FacelessAgent.java index 5366a54d271..1a603d9d0e9 100644 --- a/Mage.Sets/src/mage/cards/f/FacelessAgent.java +++ b/Mage.Sets/src/mage/cards/f/FacelessAgent.java @@ -35,7 +35,7 @@ public final class FacelessAgent extends CardImpl { this.subtype.add(SubType.SHAPESHIFTER); this.power = new MageInt(2); - this.toughness = new MageInt(1); + this.toughness = new MageInt(2); // Changeling this.addAbility(new ChangelingAbility()); diff --git a/Mage.Sets/src/mage/cards/f/FadingHope.java b/Mage.Sets/src/mage/cards/f/FadingHope.java index 766cf5b4080..40b0a6b4411 100644 --- a/Mage.Sets/src/mage/cards/f/FadingHope.java +++ b/Mage.Sets/src/mage/cards/f/FadingHope.java @@ -41,7 +41,8 @@ class FadingHopeEffect extends OneShotEffect { FadingHopeEffect() { super(Outcome.Benefit); - staticText = "return target creature to its owner's hand. If its mana value was 3 or less, scry 1"; + staticText = "return target creature to its owner's hand. If its mana value was 3 or less, scry 1. " + + "(Look at the top card of your library. You may put that card on the bottom of your library.)"; } private FadingHopeEffect(final FadingHopeEffect effect) { diff --git a/Mage.Sets/src/mage/cards/f/FaeOfWishes.java b/Mage.Sets/src/mage/cards/f/FaeOfWishes.java index ab1a4ce4652..2dac6ad521f 100644 --- a/Mage.Sets/src/mage/cards/f/FaeOfWishes.java +++ b/Mage.Sets/src/mage/cards/f/FaeOfWishes.java @@ -45,7 +45,7 @@ public final class FaeOfWishes extends AdventureCard { this.addAbility(ability); // Granted - // You may choose a noncreature card you own from outside the game, reveal it, and put it into your hand. + // You may reveal a noncreature card you own from outside the game and put it into your hand. this.getSpellCard().getSpellAbility().addEffect(new WishEffect(StaticFilters.FILTER_CARD_A_NON_CREATURE)); this.getSpellCard().getSpellAbility().addHint(OpenSideboardHint.instance); } diff --git a/Mage.Sets/src/mage/cards/f/FaerieArtisans.java b/Mage.Sets/src/mage/cards/f/FaerieArtisans.java index 7c7be3b9774..eff8adc812c 100644 --- a/Mage.Sets/src/mage/cards/f/FaerieArtisans.java +++ b/Mage.Sets/src/mage/cards/f/FaerieArtisans.java @@ -86,7 +86,7 @@ class FaerieArtisansEffect extends OneShotEffect { if (effect.apply(game, source)) { String oldTokens = (String) game.getState().getValue(source.getSourceId().toString() + source.getSourceObjectZoneChangeCounter()); StringBuilder sb = new StringBuilder(); - for (Permanent permanent : effect.getAddedPermanent()) { + for (Permanent permanent : effect.getAddedPermanents()) { if (sb.length() > 0) { sb.append(';'); } diff --git a/Mage.Sets/src/mage/cards/f/FairgroundsWarden.java b/Mage.Sets/src/mage/cards/f/FairgroundsWarden.java index ec1eda28649..e72a22e5322 100644 --- a/Mage.Sets/src/mage/cards/f/FairgroundsWarden.java +++ b/Mage.Sets/src/mage/cards/f/FairgroundsWarden.java @@ -14,8 +14,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Outcome; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; @@ -27,12 +26,6 @@ import mage.util.CardUtil; */ public final class FairgroundsWarden extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public FairgroundsWarden(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}"); this.subtype.add(SubType.DWARF); @@ -42,7 +35,7 @@ public final class FairgroundsWarden extends CardImpl { // When Fairgrounds Warden enters the battlefield, exile target creature an opponent controls until Fairgrounds Warden leaves the battlefield. Ability ability = new EntersBattlefieldTriggeredAbility(new FairsgroundsWardenExileEffect()); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); ability.addEffect(new CreateDelayedTriggeredAbilityEffect(new OnLeaveReturnExiledToBattlefieldAbility())); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/f/FaithUnbroken.java b/Mage.Sets/src/mage/cards/f/FaithUnbroken.java index 9c029573996..fb99686d4a9 100644 --- a/Mage.Sets/src/mage/cards/f/FaithUnbroken.java +++ b/Mage.Sets/src/mage/cards/f/FaithUnbroken.java @@ -15,7 +15,7 @@ import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; @@ -29,12 +29,6 @@ import mage.util.CardUtil; */ public final class FaithUnbroken extends CardImpl { - private static final FilterCreaturePermanent filterTarget = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filterTarget.add(TargetController.OPPONENT.getControllerPredicate()); - } - public FaithUnbroken(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{3}{W}"); this.subtype.add(SubType.AURA); @@ -48,7 +42,7 @@ public final class FaithUnbroken extends CardImpl { // When Faith Unbroken enters the battlefield, exile target creature an opponent controls until Faith Unbroken leaves the battlefield. ability = new EntersBattlefieldTriggeredAbility(new FaithUnbrokenEffect()); - ability.addTarget(new TargetCreaturePermanent(filterTarget)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); ability.addEffect(new CreateDelayedTriggeredAbilityEffect(new OnLeaveReturnExiledToBattlefieldAbility())); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/f/FaithboundJudge.java b/Mage.Sets/src/mage/cards/f/FaithboundJudge.java new file mode 100644 index 00000000000..96b377b8413 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FaithboundJudge.java @@ -0,0 +1,80 @@ +package mage.cards.f; + +import mage.MageInt; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.SourceHasCounterCondition; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.decorator.ConditionalAsThoughEffect; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.common.combat.CanAttackAsThoughItDidntHaveDefenderSourceEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.DefenderAbility; +import mage.abilities.keyword.DisturbAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.counters.CounterType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FaithboundJudge extends CardImpl { + + private static final Condition condition1 = new SourceHasCounterCondition(CounterType.JUDGMENT, 0, 2); + private static final Condition condition2 = new SourceHasCounterCondition(CounterType.JUDGMENT, 3); + + public FaithboundJudge(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{W}"); + + this.subtype.add(SubType.SPIRIT); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + this.secondSideCardClazz = mage.cards.s.SinnersJudgment.class; + + // Defender + this.addAbility(DefenderAbility.getInstance()); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // At the beginning of your upkeep, if Faithbound Judge has two or fewer judgment counters on it, put a judgment counter on it. + this.addAbility(new ConditionalInterveningIfTriggeredAbility( + new BeginningOfUpkeepTriggeredAbility( + new AddCountersSourceEffect(CounterType.JUDGMENT.createInstance()), + TargetController.YOU, false + ), condition1, "At the beginning of your upkeep, if {this} has " + + "two or fewer judgment counters on it, put a judgment counter on it." + )); + + // As long as Faithbound Judge has three or more judgment counters on it, it can attack as though it didn't have defender. + this.addAbility(new SimpleStaticAbility(new ConditionalAsThoughEffect( + new CanAttackAsThoughItDidntHaveDefenderSourceEffect(Duration.WhileOnBattlefield), condition2 + ).setText("as long as {this} has three or more judgment counters on it," + + " it can attack as though it didn't have defender"))); + + // Disturb {5}{W}{W} + this.addAbility(new DisturbAbility(this, "{5}{W}{W}")); + } + + private FaithboundJudge(final FaithboundJudge card) { + super(card); + } + + @Override + public FaithboundJudge copy() { + return new FaithboundJudge(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FaithfulDisciple.java b/Mage.Sets/src/mage/cards/f/FaithfulDisciple.java new file mode 100644 index 00000000000..fa1c59eeed2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FaithfulDisciple.java @@ -0,0 +1,63 @@ +package mage.cards.f; + +import mage.MageInt; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.effects.common.DraftFromSpellbookEffect; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FaithfulDisciple extends CardImpl { + + private static final List spellbook = Collections.unmodifiableList(Arrays.asList( + "All That Glitters", + "Angelic Exaltation", + "Angelic Gift", + "Anointed Procession", + "Authority of the Consuls", + "Banishing Light", + "Cathars' Crusade", + "Cleric Class", + "Divine Visitation", + "Duelist's Heritage", + "Gauntlets of Light", + "Glorious Anthem", + "Sigil of the Empty Throne", + "Spectral Steel", + "Teleportation Circle" + )); + + public FaithfulDisciple(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // When Faithful Disciple dies, draft a card from Faithful Disciple's spellbook. + this.addAbility(new DiesSourceTriggeredAbility(new DraftFromSpellbookEffect(spellbook))); + } + + private FaithfulDisciple(final FaithfulDisciple card) { + super(card); + } + + @Override + public FaithfulDisciple copy() { + return new FaithfulDisciple(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FaithfulSquire.java b/Mage.Sets/src/mage/cards/f/FaithfulSquire.java index fb72745176b..83d0a0bf04e 100644 --- a/Mage.Sets/src/mage/cards/f/FaithfulSquire.java +++ b/Mage.Sets/src/mage/cards/f/FaithfulSquire.java @@ -71,7 +71,7 @@ public final class FaithfulSquire extends CardImpl { this.flipCardName = "Kaiso, Memory of Loyalty"; // Whenever you cast a Spirit or Arcane spell, you may put a ki counter on Faithful Squire. - this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.KI.createInstance()), StaticFilters.SPIRIT_OR_ARCANE_CARD, true)); + this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.KI.createInstance()), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true)); // At the beginning of the end step, if there are two or more ki counters on Faithful Squire, you may flip it this.addAbility(new ConditionalInterveningIfTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/f/FalkenrathCelebrants.java b/Mage.Sets/src/mage/cards/f/FalkenrathCelebrants.java new file mode 100644 index 00000000000..4205f99f0ed --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FalkenrathCelebrants.java @@ -0,0 +1,42 @@ +package mage.cards.f; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.BloodToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FalkenrathCelebrants extends CardImpl { + + public FalkenrathCelebrants(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}"); + + this.subtype.add(SubType.VAMPIRE); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Menace + this.addAbility(new MenaceAbility()); + + // When Falkenrath Celebrants enters the battlefield, create two Blood tokens. + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new BloodToken(), 2))); + } + + private FalkenrathCelebrants(final FalkenrathCelebrants card) { + super(card); + } + + @Override + public FalkenrathCelebrants copy() { + return new FalkenrathCelebrants(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FalkenrathForebear.java b/Mage.Sets/src/mage/cards/f/FalkenrathForebear.java new file mode 100644 index 00000000000..c42044a4ecf --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FalkenrathForebear.java @@ -0,0 +1,67 @@ +package mage.cards.f; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.CantBlockAbility; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect; +import mage.constants.SubType; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.permanent.token.BloodToken; +import mage.target.common.TargetControlledPermanent; + +/** + * + * @author weirddan455 + */ +public final class FalkenrathForebear extends CardImpl { + + private static final FilterControlledPermanent filter = new FilterControlledPermanent("Blood tokens"); + + static { + filter.add(SubType.BLOOD.getPredicate()); + filter.add(TokenPredicate.TRUE); + } + + public FalkenrathForebear(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.subtype.add(SubType.VAMPIRE); + this.power = new MageInt(3); + this.toughness = new MageInt(1); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Falkenrath Forebear can't block. + this.addAbility(new CantBlockAbility()); + + // Whenever Falkenrath Forebear deals combat damage to a player, create a Blood token. + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new CreateTokenEffect(new BloodToken()), false)); + + // {B}, Sacrifice two Blood tokens: Return Falkenrath Forebear from your graveyard to the battlefield. + Ability ability = new SimpleActivatedAbility(Zone.GRAVEYARD, new ReturnSourceFromGraveyardToBattlefieldEffect(false, false), new ManaCostsImpl<>("{B}")); + ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(2, filter))); + this.addAbility(ability); + } + + private FalkenrathForebear(final FalkenrathForebear card) { + super(card); + } + + @Override + public FalkenrathForebear copy() { + return new FalkenrathForebear(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FalkenrathGorger.java b/Mage.Sets/src/mage/cards/f/FalkenrathGorger.java index d3a75de7a29..d18775a0eb9 100644 --- a/Mage.Sets/src/mage/cards/f/FalkenrathGorger.java +++ b/Mage.Sets/src/mage/cards/f/FalkenrathGorger.java @@ -69,7 +69,7 @@ class FalkenrathGorgerEffect extends ContinuousEffectImpl { public FalkenrathGorgerEffect() { super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); - this.staticText = "Each Vampire creature card you own that isn't on the battlefield has madness. Its madness cost is equal to its mana cost"; + this.staticText = "Each Vampire creature card you own that isn't on the battlefield has madness. The madness cost is equal to its mana cost"; } public FalkenrathGorgerEffect(final FalkenrathGorgerEffect effect) { diff --git a/Mage.Sets/src/mage/cards/f/FallOfTheImpostor.java b/Mage.Sets/src/mage/cards/f/FallOfTheImpostor.java index d71c41ec4ae..9e6fcba835f 100644 --- a/Mage.Sets/src/mage/cards/f/FallOfTheImpostor.java +++ b/Mage.Sets/src/mage/cards/f/FallOfTheImpostor.java @@ -33,7 +33,7 @@ public final class FallOfTheImpostor extends CardImpl { this.subtype.add(SubType.SAGA); // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) - SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); + SagaAbility sagaAbility = new SagaAbility(this); // I, II — Put a +1/+1 counter on up to one target creature. sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_II, diff --git a/Mage.Sets/src/mage/cards/f/FallOfTheThran.java b/Mage.Sets/src/mage/cards/f/FallOfTheThran.java index cb83f897f7c..133369a151b 100644 --- a/Mage.Sets/src/mage/cards/f/FallOfTheThran.java +++ b/Mage.Sets/src/mage/cards/f/FallOfTheThran.java @@ -36,7 +36,7 @@ public final class FallOfTheThran extends CardImpl { // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) - SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); + SagaAbility sagaAbility = new SagaAbility(this); // I — Destroy all lands. sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, new DestroyAllEffect(StaticFilters.FILTER_LANDS)); // II, III — Each player returns two land cards from their graveyard to the battlefield. diff --git a/Mage.Sets/src/mage/cards/f/FallOfTheTitans.java b/Mage.Sets/src/mage/cards/f/FallOfTheTitans.java index 7594e9ab828..f3c871cb5ab 100644 --- a/Mage.Sets/src/mage/cards/f/FallOfTheTitans.java +++ b/Mage.Sets/src/mage/cards/f/FallOfTheTitans.java @@ -21,7 +21,7 @@ public final class FallOfTheTitans extends CardImpl { // Fall of the Titans deals X damage to each of up to two target creatures and/or players. this.getSpellAbility().addTarget(new TargetAnyTarget(0, 2)); - this.getSpellAbility().addEffect(new DamageTargetEffect(ManacostVariableValue.REGULAR)); + this.getSpellAbility().addEffect(new DamageTargetEffect(ManacostVariableValue.REGULAR, true, "each of up to two targets")); // Surge {X}{R} addAbility(new SurgeAbility(this, "{X}{R}")); diff --git a/Mage.Sets/src/mage/cards/f/FallenShinobi.java b/Mage.Sets/src/mage/cards/f/FallenShinobi.java index 032694f8e98..bf9a7720880 100644 --- a/Mage.Sets/src/mage/cards/f/FallenShinobi.java +++ b/Mage.Sets/src/mage/cards/f/FallenShinobi.java @@ -29,7 +29,7 @@ public final class FallenShinobi extends CardImpl { this.toughness = new MageInt(4); // Ninjutsu {2}{U}{B} - this.addAbility(new NinjutsuAbility(new ManaCostsImpl("{2}{U}{B}"))); + this.addAbility(new NinjutsuAbility("{2}{U}{B}")); // Whenever Fallen Shinobi deals combat damage to a player, that player exiles the top two cards of their library. Until end of turn, you may play those cards without paying their mana cost. this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/f/FalthisShadowcatFamiliar.java b/Mage.Sets/src/mage/cards/f/FalthisShadowcatFamiliar.java index 5c6b4b83e52..f381e27dc35 100644 --- a/Mage.Sets/src/mage/cards/f/FalthisShadowcatFamiliar.java +++ b/Mage.Sets/src/mage/cards/f/FalthisShadowcatFamiliar.java @@ -41,7 +41,7 @@ public final class FalthisShadowcatFamiliar extends CardImpl { // Commanders you control have menace and deathtouch. Ability ability = new SimpleStaticAbility(new GainAbilityControlledEffect( - new MenaceAbility(), Duration.WhileOnBattlefield, filter + new MenaceAbility(false), Duration.WhileOnBattlefield, filter )); ability.addEffect(new GainAbilityControlledEffect( DeathtouchAbility.getInstance(), Duration.WhileOnBattlefield, filter diff --git a/Mage.Sets/src/mage/cards/f/FangOfShigeki.java b/Mage.Sets/src/mage/cards/f/FangOfShigeki.java new file mode 100644 index 00000000000..159da59a3e7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FangOfShigeki.java @@ -0,0 +1,37 @@ +package mage.cards.f; + +import mage.MageInt; +import mage.abilities.keyword.DeathtouchAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FangOfShigeki extends CardImpl { + + public FangOfShigeki(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{G}"); + + this.subtype.add(SubType.SNAKE); + this.subtype.add(SubType.NINJA); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Deathtouch + this.addAbility(DeathtouchAbility.getInstance()); + } + + private FangOfShigeki(final FangOfShigeki card) { + super(card); + } + + @Override + public FangOfShigeki copy() { + return new FangOfShigeki(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FangbladeBrigand.java b/Mage.Sets/src/mage/cards/f/FangbladeBrigand.java index c8bab4a3313..e5f33e35fa3 100644 --- a/Mage.Sets/src/mage/cards/f/FangbladeBrigand.java +++ b/Mage.Sets/src/mage/cards/f/FangbladeBrigand.java @@ -8,7 +8,6 @@ import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; import mage.abilities.keyword.DayboundAbility; import mage.abilities.keyword.FirstStrikeAbility; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -29,7 +28,6 @@ public final class FangbladeBrigand extends CardImpl { this.subtype.add(SubType.WEREWOLF); this.power = new MageInt(3); this.toughness = new MageInt(4); - this.transformable = true; this.secondSideCardClazz = mage.cards.f.FangbladeEviscerator.class; // {1}{R}: Fangblade Brigand gets +1/+0 and gains first strike until end of turn. @@ -42,7 +40,6 @@ public final class FangbladeBrigand extends CardImpl { this.addAbility(ability); // Daybound - this.addAbility(new TransformAbility()); this.addAbility(new DayboundAbility()); } diff --git a/Mage.Sets/src/mage/cards/f/FangbladeEviscerator.java b/Mage.Sets/src/mage/cards/f/FangbladeEviscerator.java index f3a9ba0926a..b9c7a4dabec 100644 --- a/Mage.Sets/src/mage/cards/f/FangbladeEviscerator.java +++ b/Mage.Sets/src/mage/cards/f/FangbladeEviscerator.java @@ -29,7 +29,6 @@ public final class FangbladeEviscerator extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(5); this.color.setRed(true); - this.transformable = true; this.nightCard = true; // {1}{R}: Fangblade Eviscerator gets +1/+0 and gains first strike until end of turn. diff --git a/Mage.Sets/src/mage/cards/f/FangrenPathcutter.java b/Mage.Sets/src/mage/cards/f/FangrenPathcutter.java index f217f54ba52..c6ae4c65f05 100644 --- a/Mage.Sets/src/mage/cards/f/FangrenPathcutter.java +++ b/Mage.Sets/src/mage/cards/f/FangrenPathcutter.java @@ -1,4 +1,3 @@ - package mage.cards.f; import java.util.UUID; @@ -11,7 +10,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; -import mage.filter.common.FilterAttackingCreature; +import mage.filter.StaticFilters; /** * @@ -27,7 +26,7 @@ public final class FangrenPathcutter extends CardImpl { this.toughness = new MageInt(6); // Whenever Fangren Pathcutter attacks, attacking creatures gain trample until end of turn. - this.addAbility(new AttacksTriggeredAbility(new GainAbilityAllEffect(TrampleAbility.getInstance(), Duration.EndOfTurn, new FilterAttackingCreature()), false)); + this.addAbility(new AttacksTriggeredAbility(new GainAbilityAllEffect(TrampleAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_ATTACKING_CREATURES), false)); } private FangrenPathcutter(final FangrenPathcutter card) { diff --git a/Mage.Sets/src/mage/cards/f/Farewell.java b/Mage.Sets/src/mage/cards/f/Farewell.java new file mode 100644 index 00000000000..ed468b3ebe1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/Farewell.java @@ -0,0 +1,46 @@ +package mage.cards.f; + +import mage.abilities.Mode; +import mage.abilities.effects.common.ExileAllEffect; +import mage.abilities.effects.common.ExileGraveyardAllPlayersEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Farewell extends CardImpl { + + public Farewell(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{W}{W}"); + + // Choose one or more — + this.getSpellAbility().getModes().setMinModes(1); + this.getSpellAbility().getModes().setMaxModes(4); + + // • Exile all artifacts. + this.getSpellAbility().addEffect(new ExileAllEffect(StaticFilters.FILTER_PERMANENT_ARTIFACTS)); + + // • Exile all creatures. + this.getSpellAbility().addMode(new Mode(new ExileAllEffect(StaticFilters.FILTER_PERMANENT_CREATURES))); + + // • Exile all enchantments. + this.getSpellAbility().addMode(new Mode(new ExileAllEffect(StaticFilters.FILTER_PERMANENT_ENCHANTMENTS))); + + // • Exile all graveyards. + this.getSpellAbility().addMode(new Mode(new ExileGraveyardAllPlayersEffect())); + } + + private Farewell(final Farewell card) { + super(card); + } + + @Override + public Farewell copy() { + return new Farewell(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FatalLore.java b/Mage.Sets/src/mage/cards/f/FatalLore.java index 8d6d9dd88b2..9ea81f4c759 100644 --- a/Mage.Sets/src/mage/cards/f/FatalLore.java +++ b/Mage.Sets/src/mage/cards/f/FatalLore.java @@ -76,7 +76,7 @@ class FatalLoreEffect extends OneShotEffect { && controller.choose(Outcome.DestroyPermanent, target, source.getSourceId(), game)) { for (UUID targetId : target.getTargets()) { Effect destroyCreature = new DestroyTargetEffect(true); - destroyCreature.setTargetPointer(new FixedTarget(targetId)); + destroyCreature.setTargetPointer(new FixedTarget(targetId, game)); destroyCreature.apply(game, source); } Effect opponentDrawsCards = new DrawCardTargetEffect(StaticValue.get(3), false, true); diff --git a/Mage.Sets/src/mage/cards/f/FatefulEnd.java b/Mage.Sets/src/mage/cards/f/FatefulEnd.java index 22813579de7..4890a8f8415 100644 --- a/Mage.Sets/src/mage/cards/f/FatefulEnd.java +++ b/Mage.Sets/src/mage/cards/f/FatefulEnd.java @@ -20,7 +20,7 @@ public final class FatefulEnd extends CardImpl { // Fateful End deals 3 damage to any target. Scry 1. this.getSpellAbility().addEffect(new DamageTargetEffect(3, true, "any target")); this.getSpellAbility().addTarget(new TargetAnyTarget()); - this.getSpellAbility().addEffect(new ScryEffect(1)); + this.getSpellAbility().addEffect(new ScryEffect(1, false)); } private FatefulEnd(final FatefulEnd card) { diff --git a/Mage.Sets/src/mage/cards/f/FathomFleetCaptain.java b/Mage.Sets/src/mage/cards/f/FathomFleetCaptain.java index a63f325fd2b..b90388e91cf 100644 --- a/Mage.Sets/src/mage/cards/f/FathomFleetCaptain.java +++ b/Mage.Sets/src/mage/cards/f/FathomFleetCaptain.java @@ -42,7 +42,7 @@ public final class FathomFleetCaptain extends CardImpl { this.toughness = new MageInt(1); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // Whenever Fathom Fleet Captain attacks, if you control another nontoken Pirate, you may pay {2}. If you do, creature a 2/2 black Pirate creature token with menace. this.addAbility(new ConditionalInterveningIfTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/f/FavorOfJukai.java b/Mage.Sets/src/mage/cards/f/FavorOfJukai.java new file mode 100644 index 00000000000..f038a205515 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FavorOfJukai.java @@ -0,0 +1,75 @@ +package mage.cards.f; + +import mage.abilities.Ability; +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.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.ChannelAbility; +import mage.abilities.keyword.EnchantAbility; +import mage.abilities.keyword.ReachAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FavorOfJukai extends CardImpl { + + private static final Condition condition = new AttachedToMatchesFilterCondition(StaticFilters.FILTER_PERMANENT_CREATURE); + + public FavorOfJukai(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{G}"); + + 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 +3/+3 and has reach. + Ability ability = new SimpleStaticAbility(new ConditionalContinuousEffect( + new BoostEnchantedEffect(3, 3), condition, + "as long as enchanted permanent is a creature, it gets +3/+3" + )); + ability.addEffect(new ConditionalContinuousEffect(new GainAbilityAttachedEffect( + ReachAbility.getInstance(), AttachmentType.AURA + ), condition, "and has reach")); + this.addAbility(ability); + + // Channel — {1}{G}, Discard Favor of Jukai: Target creature gets +3/+3 and gains reach until end of turn. + ability = new ChannelAbility( + "{1}{G}", new BoostTargetEffect(3, 3).setText("target creature gets +3/+3") + ); + ability.addEffect(new GainAbilityTargetEffect( + ReachAbility.getInstance() + ).setText("and gains reach until end of turn")); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + private FavorOfJukai(final FavorOfJukai card) { + super(card); + } + + @Override + public FavorOfJukai copy() { + return new FavorOfJukai(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FearOfDeath.java b/Mage.Sets/src/mage/cards/f/FearOfDeath.java new file mode 100644 index 00000000000..536dcd66c40 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FearOfDeath.java @@ -0,0 +1,86 @@ +package mage.cards.f; + +import java.util.UUID; + +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.MillCardsControllerEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.constants.SubType; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCreaturePermanent; +import mage.abilities.Ability; +import mage.abilities.effects.common.AttachEffect; +import mage.constants.Outcome; +import mage.target.TargetPermanent; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; + +/** + * + * @author weirddan455 + */ +public final class FearOfDeath extends CardImpl { + + public FearOfDeath(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.UnboostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // When Fear of Death enters the battlefield, mill two cards. + this.addAbility(new EntersBattlefieldTriggeredAbility(new MillCardsControllerEffect(2))); + + // Enchanted creature gets -X/-0, where X is the number of cards in your graveyard. + this.addAbility(new SimpleStaticAbility(new BoostEnchantedEffect(FearOfDeathValue.instance, StaticValue.get(0)))); + } + + private FearOfDeath(final FearOfDeath card) { + super(card); + } + + @Override + public FearOfDeath copy() { + return new FearOfDeath(this); + } +} + +enum FearOfDeathValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + Player controller = game.getPlayer(sourceAbility.getControllerId()); + if (controller == null) { + return 0; + } + return -controller.getGraveyard().size(); + } + + @Override + public FearOfDeathValue copy() { + return instance; + } + + @Override + public String toString() { + return "-X"; + } + + @Override + public String getMessage() { + return "the number of cards in your graveyard"; + } +} diff --git a/Mage.Sets/src/mage/cards/f/FearfulVillager.java b/Mage.Sets/src/mage/cards/f/FearfulVillager.java new file mode 100644 index 00000000000..175c8feb31f --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FearfulVillager.java @@ -0,0 +1,42 @@ +package mage.cards.f; + +import mage.MageInt; +import mage.abilities.keyword.DayboundAbility; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FearfulVillager extends CardImpl { + + public FearfulVillager(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WEREWOLF); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + this.secondSideCardClazz = mage.cards.f.FearsomeWerewolf.class; + + // Menace + this.addAbility(new MenaceAbility()); + + // Daybound + this.addAbility(new DayboundAbility()); + } + + private FearfulVillager(final FearfulVillager card) { + super(card); + } + + @Override + public FearfulVillager copy() { + return new FearfulVillager(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FearsomeWerewolf.java b/Mage.Sets/src/mage/cards/f/FearsomeWerewolf.java new file mode 100644 index 00000000000..53a629eabc4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FearsomeWerewolf.java @@ -0,0 +1,42 @@ +package mage.cards.f; + +import mage.MageInt; +import mage.abilities.keyword.MenaceAbility; +import mage.abilities.keyword.NightboundAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FearsomeWerewolf extends CardImpl { + + public FearsomeWerewolf(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); + + this.subtype.add(SubType.WEREWOLF); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + this.color.setRed(true); + this.nightCard = true; + + // Menace + this.addAbility(new MenaceAbility()); + + // Nightbound + this.addAbility(new NightboundAbility()); + } + + private FearsomeWerewolf(final FearsomeWerewolf card) { + super(card); + } + + @Override + public FearsomeWerewolf copy() { + return new FearsomeWerewolf(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/Feint.java b/Mage.Sets/src/mage/cards/f/Feint.java index 550d9942df3..4ffa7c667d8 100644 --- a/Mage.Sets/src/mage/cards/f/Feint.java +++ b/Mage.Sets/src/mage/cards/f/Feint.java @@ -1,4 +1,3 @@ - package mage.cards.f; import java.util.UUID; @@ -25,7 +24,7 @@ import mage.target.targetpointer.FixedTarget; public final class Feint extends CardImpl { public Feint(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{R}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{R}"); // Tap all creatures blocking target attacking creature. Prevent all combat damage that would be dealt this turn by that creature and each creature blocking it. this.getSpellAbility().addEffect(new PreventDamageByTargetEffect(Duration.EndOfTurn, true).setText("")); @@ -72,7 +71,7 @@ class FeintEffect extends OneShotEffect { if (blocker != null) { blocker.tap(source, game); PreventionEffect effect = new PreventDamageByTargetEffect(Duration.EndOfTurn, true); - effect.setTargetPointer(new FixedTarget(blocker.getId())); + effect.setTargetPointer(new FixedTarget(blocker.getId(), game)); game.addEffect(effect, source); } } diff --git a/Mage.Sets/src/mage/cards/f/FeldonOfTheThirdPath.java b/Mage.Sets/src/mage/cards/f/FeldonOfTheThirdPath.java index 7d5ee1e97da..71c460928f0 100644 --- a/Mage.Sets/src/mage/cards/f/FeldonOfTheThirdPath.java +++ b/Mage.Sets/src/mage/cards/f/FeldonOfTheThirdPath.java @@ -81,7 +81,7 @@ class FeldonOfTheThirdPathEffect extends OneShotEffect { CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(source.getControllerId(), CardType.ARTIFACT, true); effect.setTargetPointer(new FixedTarget(card.getId(), game.getState().getZoneChangeCounter(card.getId()))); effect.apply(game, source); - for (Permanent addedToken : effect.getAddedPermanent()) { + for (Permanent addedToken : effect.getAddedPermanents()) { SacrificeTargetEffect sacrificeEffect = new SacrificeTargetEffect("Sacrifice the token at the beginning of the next end step", source.getControllerId()); sacrificeEffect.setTargetPointer(new FixedTarget(addedToken, game)); DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(sacrificeEffect); diff --git a/Mage.Sets/src/mage/cards/f/FelhideSpiritbinder.java b/Mage.Sets/src/mage/cards/f/FelhideSpiritbinder.java index cfa673999e6..2d3263c107e 100644 --- a/Mage.Sets/src/mage/cards/f/FelhideSpiritbinder.java +++ b/Mage.Sets/src/mage/cards/f/FelhideSpiritbinder.java @@ -83,7 +83,7 @@ class FelhideSpiritbinderEffect extends OneShotEffect { CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(null, CardType.ENCHANTMENT, true); effect.setTargetPointer(getTargetPointer()); if (effect.apply(game, source)) { - for (Permanent tokenPermanent : effect.getAddedPermanent()) { + for (Permanent tokenPermanent : effect.getAddedPermanents()) { ExileTargetEffect exileEffect = new ExileTargetEffect(); exileEffect.setTargetPointer(new FixedTarget(tokenPermanent, game)); DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); diff --git a/Mage.Sets/src/mage/cards/f/FelidarGuardian.java b/Mage.Sets/src/mage/cards/f/FelidarGuardian.java index 155d6d53fa7..5bb596028b0 100644 --- a/Mage.Sets/src/mage/cards/f/FelidarGuardian.java +++ b/Mage.Sets/src/mage/cards/f/FelidarGuardian.java @@ -36,7 +36,7 @@ public final class FelidarGuardian extends CardImpl { // When Felidar Guardian enters the battlefield, you may exile another target permanent you control, then return that card to the battlefield under its owner's control. Ability ability = new EntersBattlefieldTriggeredAbility(new ExileTargetForSourceEffect(), true); - ability.addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false)); + ability.addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false).concatBy(",")); ability.addTarget(new TargetControlledPermanent(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/f/FellStinger.java b/Mage.Sets/src/mage/cards/f/FellStinger.java new file mode 100644 index 00000000000..8a9e4fdb051 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FellStinger.java @@ -0,0 +1,52 @@ +package mage.cards.f; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.ExploitCreatureTriggeredAbility; +import mage.abilities.effects.common.DrawCardTargetEffect; +import mage.abilities.effects.common.LoseLifeTargetEffect; +import mage.abilities.keyword.DeathtouchAbility; +import mage.abilities.keyword.ExploitAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.target.TargetPlayer; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FellStinger extends CardImpl { + + public FellStinger(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.subtype.add(SubType.ZOMBIE); + this.subtype.add(SubType.SCORPION); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // Deathtouch + this.addAbility(DeathtouchAbility.getInstance()); + + // Exploit + this.addAbility(new ExploitAbility()); + + // When Fell Stinger exploits a creature, target player draws two cards and loses 2 life. + Ability ability = new ExploitCreatureTriggeredAbility(new DrawCardTargetEffect(2)); + ability.addEffect(new LoseLifeTargetEffect(2).setText("and loses 2 life")); + ability.addTarget(new TargetPlayer()); + this.addAbility(ability); + } + + private FellStinger(final FellStinger card) { + super(card); + } + + @Override + public FellStinger copy() { + return new FellStinger(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FemerefEnchantress.java b/Mage.Sets/src/mage/cards/f/FemerefEnchantress.java index de66d317689..678c4df1430 100644 --- a/Mage.Sets/src/mage/cards/f/FemerefEnchantress.java +++ b/Mage.Sets/src/mage/cards/f/FemerefEnchantress.java @@ -29,7 +29,7 @@ public final class FemerefEnchantress extends CardImpl { // Whenever an enchantment is put into a graveyard from the battlefield, draw a card. this.addAbility(new ZoneChangeAllTriggeredAbility(Zone.BATTLEFIELD, Zone.BATTLEFIELD, Zone.GRAVEYARD, - new DrawCardSourceControllerEffect(1), StaticFilters.FILTER_ENCHANTMENT_PERMANENT, + new DrawCardSourceControllerEffect(1), StaticFilters.FILTER_PERMANENT_ENCHANTMENT, "Whenever an enchantment is put into a graveyard from the battlefield, ", false)); } diff --git a/Mage.Sets/src/mage/cards/f/FerociousTigorilla.java b/Mage.Sets/src/mage/cards/f/FerociousTigorilla.java index 32764e2b4f6..d5b09548e57 100644 --- a/Mage.Sets/src/mage/cards/f/FerociousTigorilla.java +++ b/Mage.Sets/src/mage/cards/f/FerociousTigorilla.java @@ -26,7 +26,9 @@ public final class FerociousTigorilla extends CardImpl { // Ferocious Tigorilla enters the battlefield with your choice of a trample counter or a menace counter on it. this.addAbility(new EntersBattlefieldAbility( - new AddCounterChoiceSourceEffect(CounterType.TRAMPLE, CounterType.MENACE) + new AddCounterChoiceSourceEffect(CounterType.TRAMPLE, CounterType.MENACE), + "with your choice of a trample counter or a menace counter on it. " + + "(A creature with menace can't be blocked except by two or more creatures.)" )); } diff --git a/Mage.Sets/src/mage/cards/f/FesteringNewt.java b/Mage.Sets/src/mage/cards/f/FesteringNewt.java index b721301ce63..323b1267474 100644 --- a/Mage.Sets/src/mage/cards/f/FesteringNewt.java +++ b/Mage.Sets/src/mage/cards/f/FesteringNewt.java @@ -15,7 +15,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.TargetController; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.NamePredicate; import mage.target.common.TargetCreaturePermanent; @@ -26,12 +26,12 @@ import mage.target.common.TargetCreaturePermanent; */ public final class FesteringNewt extends CardImpl { - private static final FilterCreaturePermanent filterCreature = new FilterCreaturePermanent("creature an opponent controls"); private static final FilterCreaturePermanent filterBogbrewWitch = new FilterCreaturePermanent(); + static { - filterCreature.add(TargetController.OPPONENT.getControllerPredicate()); filterBogbrewWitch.add(new NamePredicate("Bogbrew Witch")); } + public FesteringNewt(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{B}"); this.subtype.add(SubType.SALAMANDER); @@ -46,7 +46,7 @@ public final class FesteringNewt extends CardImpl { new LockedInCondition(new PermanentsOnTheBattlefieldCondition(filterBogbrewWitch)), "target creature an opponent controls gets -1/-1 until end of turn. That creature gets -4/-4 instead if you control a creature named Bogbrew Witch"); Ability ability = new DiesSourceTriggeredAbility(effect); - ability.addTarget(new TargetCreaturePermanent(filterCreature)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/f/FickleEfreet.java b/Mage.Sets/src/mage/cards/f/FickleEfreet.java index f345b9fb9fc..4b32a9a51a7 100644 --- a/Mage.Sets/src/mage/cards/f/FickleEfreet.java +++ b/Mage.Sets/src/mage/cards/f/FickleEfreet.java @@ -1,4 +1,3 @@ - package mage.cards.f; import java.util.UUID; @@ -28,7 +27,7 @@ import mage.target.targetpointer.FixedTarget; public final class FickleEfreet extends CardImpl { public FickleEfreet(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); this.subtype.add(SubType.EFREET); this.power = new MageInt(5); @@ -49,7 +48,6 @@ public final class FickleEfreet extends CardImpl { } } - class FickleEfreetChangeControlEffect extends OneShotEffect { public FickleEfreetChangeControlEffect() { @@ -82,7 +80,7 @@ class FickleEfreetChangeControlEffect extends OneShotEffect { Player chosenOpponent = game.getPlayer(target.getFirstTarget()); if (chosenOpponent != null) { ContinuousEffect effect = new FickleEfreetGainControlEffect(Duration.Custom, target.getFirstTarget()); - effect.setTargetPointer(new FixedTarget(sourcePermanent.getId())); + effect.setTargetPointer(new FixedTarget(sourcePermanent.getId(), game)); game.addEffect(effect, source); game.informPlayers(chosenOpponent.getLogName() + " has gained control of " + sourcePermanent.getLogName()); return true; diff --git a/Mage.Sets/src/mage/cards/f/FiddleheadKami.java b/Mage.Sets/src/mage/cards/f/FiddleheadKami.java index a907c1c3a06..6017d582d04 100644 --- a/Mage.Sets/src/mage/cards/f/FiddleheadKami.java +++ b/Mage.Sets/src/mage/cards/f/FiddleheadKami.java @@ -25,7 +25,7 @@ public final class FiddleheadKami extends CardImpl { this.toughness = new MageInt(3); // Whenever you cast a Spirit or Arcane spell, regenerate Fiddlehead Kami. - this.addAbility(new SpellCastControllerTriggeredAbility(new RegenerateSourceEffect(), StaticFilters.SPIRIT_OR_ARCANE_CARD, false)); + this.addAbility(new SpellCastControllerTriggeredAbility(new RegenerateSourceEffect(), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false)); } private FiddleheadKami(final FiddleheadKami card) { diff --git a/Mage.Sets/src/mage/cards/f/FiendOfTheShadows.java b/Mage.Sets/src/mage/cards/f/FiendOfTheShadows.java index 145bca509a9..04ac8592278 100644 --- a/Mage.Sets/src/mage/cards/f/FiendOfTheShadows.java +++ b/Mage.Sets/src/mage/cards/f/FiendOfTheShadows.java @@ -1,7 +1,6 @@ package mage.cards.f; import mage.MageInt; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; @@ -77,8 +76,7 @@ class FiendOfTheShadowsEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(targetPointer.getFirst(game, source)); - MageObject sourceObject = source.getSourceObject(game); - if (player == null || sourceObject == null || player.getHand().isEmpty()) { + if (player == null || player.getHand().isEmpty()) { return false; } TargetCard targetCard = new TargetDiscard(player.getId()); @@ -88,7 +86,7 @@ class FiendOfTheShadowsEffect extends OneShotEffect { return false; } player.moveCardToExileWithInfo( - card, CardUtil.getExileZoneId(game, source), sourceObject.getName(), + card, CardUtil.getExileZoneId(game, source), CardUtil.getSourceName(game, source), source, game, Zone.HAND, true ); CardUtil.makeCardPlayable(game, source, card, Duration.Custom, false); diff --git a/Mage.Sets/src/mage/cards/f/FierceRetribution.java b/Mage.Sets/src/mage/cards/f/FierceRetribution.java new file mode 100644 index 00000000000..f93338508f3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FierceRetribution.java @@ -0,0 +1,40 @@ +package mage.cards.f; + +import mage.abilities.Ability; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.keyword.CleaveAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.target.common.TargetAttackingCreature; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FierceRetribution extends CardImpl { + + public FierceRetribution(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}"); + + // Cleave {5}{W} + Ability ability = new CleaveAbility(this, new DestroyTargetEffect(), "{5}{W}"); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + + // Destroy target [attacking] creature. + this.getSpellAbility().addEffect(new DestroyTargetEffect().setText("destroy target [attacking] creature")); + this.getSpellAbility().addTarget(new TargetAttackingCreature()); + } + + private FierceRetribution(final FierceRetribution card) { + super(card); + } + + @Override + public FierceRetribution copy() { + return new FierceRetribution(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FieryJustice.java b/Mage.Sets/src/mage/cards/f/FieryJustice.java index 00767f0e590..252a927406f 100644 --- a/Mage.Sets/src/mage/cards/f/FieryJustice.java +++ b/Mage.Sets/src/mage/cards/f/FieryJustice.java @@ -21,8 +21,7 @@ public final class FieryJustice extends CardImpl { public FieryJustice(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{R}{G}{W}"); - - // Fiery Justice deals 5 damage divided as you choose among any number of target creatures and/or players. Target opponent gains 5 life. + // Fiery Justice deals 5 damage divided as you choose among any number of targets. Target opponent gains 5 life. this.getSpellAbility().addEffect(new DamageMultiEffect(5)); this.getSpellAbility().addTarget(new TargetAnyTargetAmount(5)); Effect effect = new GainLifeTargetEffect(5); diff --git a/Mage.Sets/src/mage/cards/f/FightingChance.java b/Mage.Sets/src/mage/cards/f/FightingChance.java index 8a8bc1676ed..6dbdd46f6b6 100644 --- a/Mage.Sets/src/mage/cards/f/FightingChance.java +++ b/Mage.Sets/src/mage/cards/f/FightingChance.java @@ -10,7 +10,6 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; -import mage.filter.common.FilterBlockingCreature; import mage.game.Game; import mage.players.Player; import mage.target.targetpointer.FixedTarget; @@ -21,8 +20,6 @@ import mage.target.targetpointer.FixedTarget; */ public final class FightingChance extends CardImpl { - private static final FilterBlockingCreature filter = new FilterBlockingCreature("Blocking creatures"); - public FightingChance(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{R}"); @@ -63,7 +60,7 @@ class FightingChanceEffect extends OneShotEffect { for (UUID blocker : game.getCombat().getBlockers()) { if (player.flipCoin(source, game, true)) { PreventDamageByTargetEffect effect = new PreventDamageByTargetEffect(Duration.EndOfTurn, true); - effect.setTargetPointer(new FixedTarget(blocker)); + effect.setTargetPointer(new FixedTarget(blocker, game)); game.addEffect(effect, source); } } diff --git a/Mage.Sets/src/mage/cards/f/FindFinality.java b/Mage.Sets/src/mage/cards/f/FindFinality.java index 5263dddebf5..47f69b73e71 100644 --- a/Mage.Sets/src/mage/cards/f/FindFinality.java +++ b/Mage.Sets/src/mage/cards/f/FindFinality.java @@ -14,8 +14,7 @@ import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.SpellAbilityType; import mage.counters.CounterType; -import mage.filter.FilterCard; -import mage.filter.common.FilterCreatureCard; +import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; import mage.target.Target; @@ -29,9 +28,6 @@ import mage.target.targetpointer.FixedTarget; */ public final class FindFinality extends SplitCard { - private static final FilterCard filter - = new FilterCreatureCard("creature cards from your graveyard"); - public FindFinality(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{B/G}{B/G}", "{4}{B}{G}", SpellAbilityType.SPLIT); @@ -41,7 +37,7 @@ public final class FindFinality extends SplitCard { new ReturnFromGraveyardToHandTargetEffect() ); this.getLeftHalfCard().getSpellAbility().addTarget( - new TargetCardInYourGraveyard(0, 2, filter) + new TargetCardInYourGraveyard(0, 2, StaticFilters.FILTER_CARD_CREATURES_YOUR_GRAVEYARD) ); // Finality @@ -86,6 +82,7 @@ class FinalityEffect extends OneShotEffect { return false; } Target target = new TargetControlledCreaturePermanent(0, 1); + target.setNotTarget(true); if (player.choose( Outcome.BoostCreature, target, source.getSourceId(), game )) { diff --git a/Mage.Sets/src/mage/cards/f/FinestHour.java b/Mage.Sets/src/mage/cards/f/FinestHour.java index a7103c56889..e53d5001372 100644 --- a/Mage.Sets/src/mage/cards/f/FinestHour.java +++ b/Mage.Sets/src/mage/cards/f/FinestHour.java @@ -1,5 +1,3 @@ - - package mage.cards.f; import java.util.UUID; @@ -16,7 +14,6 @@ import mage.constants.TurnPhase; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.game.turn.TurnMod; import mage.target.targetpointer.FixedTarget; @@ -28,10 +25,7 @@ import mage.target.targetpointer.FixedTarget; public final class FinestHour extends CardImpl { public FinestHour(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{G}{W}{U}"); - - - + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}{W}{U}"); // Exalted (Whenever a creature you control attacks alone, that creature gets +1/+1 until end of turn.) this.addAbility(new ExaltedAbility()); @@ -76,8 +70,8 @@ class FinestHourAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { if (game.isActivePlayer(this.controllerId)) { if (game.getCombat().attacksAlone()) { - for (Effect effect: this.getEffects()) { - effect.setTargetPointer(new FixedTarget(game.getCombat().getAttackers().get(0))); + for (Effect effect : this.getEffects()) { + effect.setTargetPointer(new FixedTarget(game.getCombat().getAttackers().get(0), game)); } return true; } diff --git a/Mage.Sets/src/mage/cards/f/FireAtWill.java b/Mage.Sets/src/mage/cards/f/FireAtWill.java index 2fe0cb00cc6..e910437823f 100644 --- a/Mage.Sets/src/mage/cards/f/FireAtWill.java +++ b/Mage.Sets/src/mage/cards/f/FireAtWill.java @@ -1,4 +1,3 @@ - package mage.cards.f; import java.util.UUID; @@ -6,10 +5,7 @@ import mage.abilities.effects.common.DamageMultiEffect; 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.permanent.AttackingPredicate; -import mage.filter.predicate.permanent.BlockingPredicate; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanentAmount; /** @@ -19,21 +15,12 @@ import mage.target.common.TargetCreaturePermanentAmount; */ public final class FireAtWill extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("attacking or blocking creatures"); - - static { - filter.add(Predicates.or( - AttackingPredicate.instance, - BlockingPredicate.instance)); - } - public FireAtWill(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{R/W}{R/W}{R/W}"); // Fire at Will deals 3 damage divided as you choose among one, two, or three target attacking or blocking creatures. - this.getSpellAbility().addEffect(new DamageMultiEffect(3).setText("{this} deals 3 damage divided as you choose among one, two, or three target attacking or blocking creatures.")); - this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount(3, filter)); - + this.getSpellAbility().addEffect(new DamageMultiEffect(3)); + this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount(3, StaticFilters.FILTER_ATTACKING_OR_BLOCKING_CREATURES)); } private FireAtWill(final FireAtWill card) { diff --git a/Mage.Sets/src/mage/cards/f/FireGiantsFury.java b/Mage.Sets/src/mage/cards/f/FireGiantsFury.java index c43508c821b..263dfa40fbb 100644 --- a/Mage.Sets/src/mage/cards/f/FireGiantsFury.java +++ b/Mage.Sets/src/mage/cards/f/FireGiantsFury.java @@ -100,7 +100,7 @@ class FireGiantsFuryDelayedTriggeredAbility extends DelayedTriggeredAbility { this.mor = mor; } - private FireGiantsFuryDelayedTriggeredAbility (FireGiantsFuryDelayedTriggeredAbility ability) { + private FireGiantsFuryDelayedTriggeredAbility(FireGiantsFuryDelayedTriggeredAbility ability) { super(ability); this.mor = ability.mor; } @@ -161,7 +161,7 @@ class FireGiantsFuryDelayedEffect extends OneShotEffect { for (Card card : cards) { ContinuousEffect effect = new FireGiantsFuryMayPlayEffect(); - effect.setTargetPointer(new FixedTarget(card.getId())); + effect.setTargetPointer(new FixedTarget(card.getId(), game)); game.addEffect(effect, source); } diff --git a/Mage.Sets/src/mage/cards/f/FireIce.java b/Mage.Sets/src/mage/cards/f/FireIce.java index dbb062c06bd..fc01b99963d 100644 --- a/Mage.Sets/src/mage/cards/f/FireIce.java +++ b/Mage.Sets/src/mage/cards/f/FireIce.java @@ -1,8 +1,6 @@ - package mage.cards.f; import java.util.UUID; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.DamageMultiEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.TapTargetEffect; @@ -19,10 +17,8 @@ public final class FireIce extends SplitCard { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{R}", "{1}{U}", SpellAbilityType.SPLIT); // Fire - // Fire deals 2 damage divided as you choose among one or two target creatures and/or players. - Effect effect = new DamageMultiEffect(2); - effect.setText("Fire deals 2 damage divided as you choose among one or two target creatures and/or players"); - getLeftHalfCard().getSpellAbility().addEffect(effect); + // Fire deals 2 damage divided as you choose among one or two targets. + getLeftHalfCard().getSpellAbility().addEffect(new DamageMultiEffect(2, "Fire")); getLeftHalfCard().getSpellAbility().addTarget(new TargetAnyTargetAmount(2)); // Ice diff --git a/Mage.Sets/src/mage/cards/f/FireShrineKeeper.java b/Mage.Sets/src/mage/cards/f/FireShrineKeeper.java index 689d6af1170..f94593d22e3 100644 --- a/Mage.Sets/src/mage/cards/f/FireShrineKeeper.java +++ b/Mage.Sets/src/mage/cards/f/FireShrineKeeper.java @@ -31,7 +31,7 @@ public final class FireShrineKeeper extends CardImpl { this.toughness = new MageInt(1); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // {7}{R}, {T}, Sacrifice Fire Shrine Keeper: It deals 3 damage to each of up to two target creatures. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, diff --git a/Mage.Sets/src/mage/cards/f/FiredrinkerSatyr.java b/Mage.Sets/src/mage/cards/f/FiredrinkerSatyr.java index b687fcac995..f5877ae4dfa 100644 --- a/Mage.Sets/src/mage/cards/f/FiredrinkerSatyr.java +++ b/Mage.Sets/src/mage/cards/f/FiredrinkerSatyr.java @@ -31,7 +31,7 @@ public final class FiredrinkerSatyr extends CardImpl { this.toughness = new MageInt(1); // Whenever Firedrinker Satyr is dealt damage, it deals that much damage to you. - this.addAbility(new DealtDamageToSourceTriggeredAbility(new FiredrinkerSatyrDealDamageEffect(), false, false, true)); + this.addAbility(new DealtDamageToSourceTriggeredAbility(new FiredrinkerSatyrDealDamageEffect(), false, false)); // {1}{R}: Firedrinker Satyr gets +1/+0 until end of turn and deals 1 damage to you. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1, 0, Duration.EndOfTurn), new ManaCostsImpl("{1}{R}")); Effect effect = new DamageControllerEffect(1); diff --git a/Mage.Sets/src/mage/cards/f/FiremindsResearch.java b/Mage.Sets/src/mage/cards/f/FiremindsResearch.java index 52a1acf4194..37280801d43 100644 --- a/Mage.Sets/src/mage/cards/f/FiremindsResearch.java +++ b/Mage.Sets/src/mage/cards/f/FiremindsResearch.java @@ -1,6 +1,5 @@ package mage.cards.f; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility; @@ -13,11 +12,12 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.counters.CounterType; -import mage.filter.common.FilterInstantOrSorcerySpell; +import mage.filter.StaticFilters; import mage.target.common.TargetAnyTarget; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class FiremindsResearch extends CardImpl { @@ -27,9 +27,8 @@ public final class FiremindsResearch extends CardImpl { // Whenever you cast an instant or sorcery spell, put a charge counter on Firemind's Research. this.addAbility(new SpellCastControllerTriggeredAbility( - new AddCountersSourceEffect( - CounterType.CHARGE.createInstance() - ), new FilterInstantOrSorcerySpell(), false + new AddCountersSourceEffect(CounterType.CHARGE.createInstance()), + StaticFilters.FILTER_SPELL_AN_INSTANT_OR_SORCERY, false )); // {1}{U}, Remove two charge counters from Firemind's Research: Draw a card. diff --git a/Mage.Sets/src/mage/cards/f/FiresOfYavimaya.java b/Mage.Sets/src/mage/cards/f/FiresOfYavimaya.java index 3d54d79e1ba..c383ff9d101 100644 --- a/Mage.Sets/src/mage/cards/f/FiresOfYavimaya.java +++ b/Mage.Sets/src/mage/cards/f/FiresOfYavimaya.java @@ -13,9 +13,8 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.TargetController; import mage.constants.Zone; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; /** @@ -24,17 +23,11 @@ import mage.target.common.TargetCreaturePermanent; */ public final class FiresOfYavimaya extends CardImpl { - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("Creatures you control"); - - static { - filter.add(TargetController.YOU.getControllerPredicate()); - } - public FiresOfYavimaya(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{R}{G}"); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(HasteAbility.getInstance(), Duration.WhileOnBattlefield, filter, false))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(HasteAbility.getInstance(), Duration.WhileOnBattlefield, StaticFilters.FILTER_CONTROLLED_CREATURES, false))); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostTargetEffect(2, 2, Duration.EndOfTurn), new SacrificeSourceCost()); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/f/FirjasRetribution.java b/Mage.Sets/src/mage/cards/f/FirjasRetribution.java index d4ea16d5d2f..7eb316a407e 100644 --- a/Mage.Sets/src/mage/cards/f/FirjasRetribution.java +++ b/Mage.Sets/src/mage/cards/f/FirjasRetribution.java @@ -46,7 +46,7 @@ public final class FirjasRetribution extends CardImpl { this.subtype.add(SubType.SAGA); // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) - SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); + SagaAbility sagaAbility = new SagaAbility(this); // I — Create a 4/4 white Angel Warrior creature token with flying and vigilance. sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, new CreateTokenEffect(new AngelWarriorVigilanceToken())); diff --git a/Mage.Sets/src/mage/cards/f/FirstSliversChosen.java b/Mage.Sets/src/mage/cards/f/FirstSliversChosen.java index 6d30afa21aa..7fd150c1d1a 100644 --- a/Mage.Sets/src/mage/cards/f/FirstSliversChosen.java +++ b/Mage.Sets/src/mage/cards/f/FirstSliversChosen.java @@ -28,7 +28,7 @@ public final class FirstSliversChosen extends CardImpl { // Sliver creatures you control have exalted. this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( new ExaltedAbility(), Duration.WhileOnBattlefield, - StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS + StaticFilters.FILTER_PERMANENT_SLIVERS ))); } diff --git a/Mage.Sets/src/mage/cards/f/FistfulOfForce.java b/Mage.Sets/src/mage/cards/f/FistfulOfForce.java index c3305d057e7..9cb872cedb9 100644 --- a/Mage.Sets/src/mage/cards/f/FistfulOfForce.java +++ b/Mage.Sets/src/mage/cards/f/FistfulOfForce.java @@ -67,12 +67,12 @@ class FistfulOfForceEffect extends OneShotEffect { Permanent creature = game.getPermanent(getTargetPointer().getFirst(game, source)); if (controller != null && creature != null) { ContinuousEffect effect = new BoostTargetEffect(2,2,Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(creature.getId())); + effect.setTargetPointer(new FixedTarget(creature.getId(), game)); game.addEffect(effect, source); if (ClashEffect.getInstance().apply(game, source)) { game.addEffect(effect.copy(), source); effect = new GainAbilityTargetEffect(TrampleAbility.getInstance(), Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(creature.getId())); + effect.setTargetPointer(new FixedTarget(creature.getId(), game)); game.addEffect(effect.copy(), source); } return true; diff --git a/Mage.Sets/src/mage/cards/f/FlailingDrake.java b/Mage.Sets/src/mage/cards/f/FlailingDrake.java index 2257fe4410a..e152eb5a9f3 100644 --- a/Mage.Sets/src/mage/cards/f/FlailingDrake.java +++ b/Mage.Sets/src/mage/cards/f/FlailingDrake.java @@ -13,7 +13,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; /** * @@ -32,7 +32,7 @@ public final class FlailingDrake extends CardImpl { // Whenever Flailing Drake blocks or becomes blocked by a creature, that creature gets +1/+1 until end of turn. Effect effect = new BoostTargetEffect(+1, +1, Duration.EndOfTurn); effect.setText("that creature gets +1/+1 until end of turn"); - Ability ability = new BlocksOrBecomesBlockedSourceTriggeredAbility(effect, new FilterCreaturePermanent("a creature"), false, null, true); + Ability ability = new BlocksOrBecomesBlockedSourceTriggeredAbility(effect, StaticFilters.FILTER_PERMANENT_A_CREATURE, false, null, true); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/f/FlameBlessedBolt.java b/Mage.Sets/src/mage/cards/f/FlameBlessedBolt.java new file mode 100644 index 00000000000..b9752274f53 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FlameBlessedBolt.java @@ -0,0 +1,35 @@ +package mage.cards.f; + +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.ExileTargetIfDiesEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.target.common.TargetCreatureOrPlaneswalker; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FlameBlessedBolt extends CardImpl { + + public FlameBlessedBolt(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{R}"); + + // Flame-Blessed Bolt deals 2 damage to target creature or planeswalker. If that creature or planeswalker would die this turn, exile it instead. + this.getSpellAbility().addEffect(new DamageTargetEffect(2)); + this.getSpellAbility().addEffect(new ExileTargetIfDiesEffect() + .setText("If that creature or planeswalker would die this turn, exile it instead.")); + this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalker()); + } + + private FlameBlessedBolt(final FlameBlessedBolt card) { + super(card); + } + + @Override + public FlameBlessedBolt copy() { + return new FlameBlessedBolt(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FlameBurst.java b/Mage.Sets/src/mage/cards/f/FlameBurst.java index cdf034fb28b..dc4842490a4 100644 --- a/Mage.Sets/src/mage/cards/f/FlameBurst.java +++ b/Mage.Sets/src/mage/cards/f/FlameBurst.java @@ -1,11 +1,12 @@ package mage.cards.f; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.AdditiveDynamicValue; +import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.CardsInAllGraveyardsCount; -import mage.abilities.effects.Effect; +import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.InfoEffect; import mage.cards.CardImpl; @@ -16,11 +17,11 @@ import mage.filter.FilterCard; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.AbilityPredicate; import mage.filter.predicate.mageobject.NamePredicate; -import mage.game.Game; import mage.target.common.TargetAnyTarget; +import java.util.UUID; + /** - * * @author LoneFox */ public final class FlameBurst extends CardImpl { @@ -28,17 +29,21 @@ public final class FlameBurst extends CardImpl { private static final FilterCard filter = new FilterCard(); static { - filter.add(Predicates.or(new NamePredicate("Flame Burst"), - new AbilityPredicate(CountAsFlameBurstAbility.class))); + filter.add(Predicates.or( + new NamePredicate("Flame Burst"), + new AbilityPredicate(CountAsFlameBurstAbility.class) + )); } + private static final DynamicValue xValue = new AdditiveDynamicValue( + new CardsInAllGraveyardsCount(filter), StaticValue.get(2) + ); + public FlameBurst(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{R}"); // Flame Burst deals X damage to any target, where X is 2 plus the number of cards named Flame Burst in all graveyards. - Effect effect = new DamageTargetEffect(new FlameBurstCount(filter)); - effect.setText("{this} deals X damage to any target, where X is 2 plus the number of cards named Flame Burst in all graveyards."); - this.getSpellAbility().addEffect(effect); + this.getSpellAbility().addEffect(new DamageTargetEffect(xValue).setText("{this} deals X damage to any target, where X is 2 plus the number of cards named Flame Burst in all graveyards.")); this.getSpellAbility().addTarget(new TargetAnyTarget()); } @@ -56,28 +61,6 @@ public final class FlameBurst extends CardImpl { } } -class FlameBurstCount extends CardsInAllGraveyardsCount { - - public FlameBurstCount(FilterCard filter) { - super(filter); - } - - public FlameBurstCount(FlameBurstCount value) { - super(value); - } - - @Override - public FlameBurstCount copy() { - return new FlameBurstCount(this); - } - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - return super.calculate(game, sourceAbility, effect) + 2; - } - -} - class CountAsFlameBurstAbility extends SimpleStaticAbility { public CountAsFlameBurstAbility() { diff --git a/Mage.Sets/src/mage/cards/f/FlameChanneler.java b/Mage.Sets/src/mage/cards/f/FlameChanneler.java index e3783c3d773..8bce9da31b1 100644 --- a/Mage.Sets/src/mage/cards/f/FlameChanneler.java +++ b/Mage.Sets/src/mage/cards/f/FlameChanneler.java @@ -27,7 +27,7 @@ public final class FlameChanneler extends CardImpl { this.subtype.add(SubType.WIZARD); this.power = new MageInt(2); this.toughness = new MageInt(2); - this.transformable=true;this.secondSideCardClazz=mage.cards.e.EmbodimentOfFlame.class; + this.secondSideCardClazz = mage.cards.e.EmbodimentOfFlame.class; // When a spell you control deals damage, transform Flame Channeler. this.addAbility(new TransformAbility()); @@ -47,7 +47,7 @@ public final class FlameChanneler extends CardImpl { class FlameChannelerTriggeredAbility extends TriggeredAbilityImpl { FlameChannelerTriggeredAbility() { - super(Zone.BATTLEFIELD, new TransformSourceEffect(true)); + super(Zone.BATTLEFIELD, new TransformSourceEffect()); } private FlameChannelerTriggeredAbility(final FlameChannelerTriggeredAbility ability) { @@ -68,7 +68,7 @@ class FlameChannelerTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { Spell spell = game.getSpellOrLKIStack(event.getSourceId()); - return spell != null && isControlledBy(spell.getControllerId()); + return spell != null && isControlledBy(spell.getControllerId()) && spell.isInstantOrSorcery(game); } @Override diff --git a/Mage.Sets/src/mage/cards/f/FlameDischarge.java b/Mage.Sets/src/mage/cards/f/FlameDischarge.java new file mode 100644 index 00000000000..c3d4c4e762b --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FlameDischarge.java @@ -0,0 +1,71 @@ +package mage.cards.f; + +import java.util.UUID; + +import mage.abilities.Ability; +import mage.abilities.condition.common.ControlledModifiedCreatureAsSpellCastCondition; +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.Permanent; +import mage.target.common.TargetCreatureOrPlaneswalker; +import mage.watchers.common.ControlledModifiedCreatureAsSpellCastWatcher; + +/** + * + * @author weirddan455 + */ +public final class FlameDischarge extends CardImpl { + + public FlameDischarge(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{X}{R}"); + + // Flame Discharge deals X damage to target creature or planeswalker. If you controlled a modified creature as you cast this spell, it deals X plus 2 damage instead. + this.getSpellAbility().addEffect(new FlameDischargeEffect()); + this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalker()); + this.getSpellAbility().addWatcher(new ControlledModifiedCreatureAsSpellCastWatcher()); + } + + private FlameDischarge(final FlameDischarge card) { + super(card); + } + + @Override + public FlameDischarge copy() { + return new FlameDischarge(this); + } +} + +class FlameDischargeEffect extends OneShotEffect { + + public FlameDischargeEffect() { + super(Outcome.Damage); + this.staticText = "{this} deals X damage to target creature or planeswalker. If you controlled a modified creature as you cast this spell, it deals X plus 2 damage instead"; + } + + private FlameDischargeEffect(final FlameDischargeEffect effect) { + super(effect); + } + + @Override + public FlameDischargeEffect copy() { + return new FlameDischargeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent == null) { + return false; + } + int damageAmount = source.getManaCostsToPay().getX(); + if (ControlledModifiedCreatureAsSpellCastCondition.instance.apply(game, source)) { + damageAmount += 2; + } + permanent.damage(damageAmount, source, game); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/f/FlamebladeAdept.java b/Mage.Sets/src/mage/cards/f/FlamebladeAdept.java index 0520943ff02..3b0772f8668 100644 --- a/Mage.Sets/src/mage/cards/f/FlamebladeAdept.java +++ b/Mage.Sets/src/mage/cards/f/FlamebladeAdept.java @@ -27,7 +27,7 @@ public final class FlamebladeAdept extends CardImpl { this.toughness = new MageInt(2); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // Whenever you cycle or discard a card, Flameblade Adept gets +1/+0 until end of turn. this.addAbility(new CycleOrDiscardControllerTriggeredAbility(new BoostSourceEffect(1, 0, Duration.EndOfTurn))); diff --git a/Mage.Sets/src/mage/cards/f/FlameheartWerewolf.java b/Mage.Sets/src/mage/cards/f/FlameheartWerewolf.java index 84bf2aba136..b73d8bc11e7 100644 --- a/Mage.Sets/src/mage/cards/f/FlameheartWerewolf.java +++ b/Mage.Sets/src/mage/cards/f/FlameheartWerewolf.java @@ -26,7 +26,6 @@ public final class FlameheartWerewolf extends CardImpl { // this card is the second face of double-faced card this.nightCard = true; - this.transformable = true; // Whenever Flameheart Werewolf blocks or becomes blocked by a creature, Flameheart Werewolf deals 2 damage to that creature. this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(new DamageTargetEffect(2, true, "that creature"), diff --git a/Mage.Sets/src/mage/cards/f/FlamerushRider.java b/Mage.Sets/src/mage/cards/f/FlamerushRider.java index 2e8e079c937..a7d0b8c60a1 100644 --- a/Mage.Sets/src/mage/cards/f/FlamerushRider.java +++ b/Mage.Sets/src/mage/cards/f/FlamerushRider.java @@ -89,7 +89,7 @@ class FlamerushRiderEffect extends OneShotEffect { CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(source.getControllerId(), null, true, 1, true, true); effect.setTargetPointer(new FixedTarget(permanent, game)); effect.apply(game, source); - for (Permanent addedToken : effect.getAddedPermanent()) { + for (Permanent addedToken : effect.getAddedPermanents()) { Effect exileEffect = new ExileTargetEffect(); exileEffect.setTargetPointer(new FixedTarget(addedToken, game)); new CreateDelayedTriggeredAbilityEffect(new AtTheEndOfCombatDelayedTriggeredAbility(exileEffect), false).apply(game, source); diff --git a/Mage.Sets/src/mage/cards/f/FlamesOfTheFirebrand.java b/Mage.Sets/src/mage/cards/f/FlamesOfTheFirebrand.java index b806b819aaa..7100668c180 100644 --- a/Mage.Sets/src/mage/cards/f/FlamesOfTheFirebrand.java +++ b/Mage.Sets/src/mage/cards/f/FlamesOfTheFirebrand.java @@ -1,4 +1,3 @@ - package mage.cards.f; import java.util.UUID; @@ -17,8 +16,7 @@ public final class FlamesOfTheFirebrand extends CardImpl { public FlamesOfTheFirebrand(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{R}"); - - // Flames of the Firebrand deals 3 damage divided as you choose among one, two, or three target creatures and/or players. + // Flames of the Firebrand deals 3 damage divided as you choose among one, two, or three targets. this.getSpellAbility().addEffect(new DamageMultiEffect(3)); this.getSpellAbility().addTarget(new TargetAnyTargetAmount(3)); } diff --git a/Mage.Sets/src/mage/cards/f/FlameshadowConjuring.java b/Mage.Sets/src/mage/cards/f/FlameshadowConjuring.java index 5f23c18d36d..fb7eca29d64 100644 --- a/Mage.Sets/src/mage/cards/f/FlameshadowConjuring.java +++ b/Mage.Sets/src/mage/cards/f/FlameshadowConjuring.java @@ -81,7 +81,7 @@ class FlameshadowConjuringEffect extends OneShotEffect { CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(null, null, true); effect.setTargetPointer(getTargetPointer()); if (effect.apply(game, source)) { - for (Permanent tokenPermanent : effect.getAddedPermanent()) { + for (Permanent tokenPermanent : effect.getAddedPermanents()) { ExileTargetEffect exileEffect = new ExileTargetEffect(); exileEffect.setTargetPointer(new FixedTarget(tokenPermanent, game)); DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); diff --git a/Mage.Sets/src/mage/cards/f/Flameshot.java b/Mage.Sets/src/mage/cards/f/Flameshot.java index d82386829b1..15fde82d4ad 100644 --- a/Mage.Sets/src/mage/cards/f/Flameshot.java +++ b/Mage.Sets/src/mage/cards/f/Flameshot.java @@ -32,7 +32,7 @@ public final class Flameshot extends CardImpl { this.addAbility(new AlternativeCostSourceAbility(new DiscardTargetCost(new TargetCardInHand(filter)))); // Flameshot deals 3 damage divided as you choose among one, two, or three target creatures. - this.getSpellAbility().addEffect(new DamageMultiEffect(3).setText("{this} deals 3 damage divided as you choose among one, two, or three target creatures")); + this.getSpellAbility().addEffect(new DamageMultiEffect(3)); this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount(3)); } diff --git a/Mage.Sets/src/mage/cards/f/FlamestickCourier.java b/Mage.Sets/src/mage/cards/f/FlamestickCourier.java index 60a288e7145..3e0876f3f25 100644 --- a/Mage.Sets/src/mage/cards/f/FlamestickCourier.java +++ b/Mage.Sets/src/mage/cards/f/FlamestickCourier.java @@ -44,10 +44,10 @@ public final class FlamestickCourier extends CardImpl { this.addAbility(new SkipUntapOptionalAbility()); // {2}{R}, {tap}: Target Goblin creature gets +2/+2 and has haste for as long as Flamestick Courier remains tapped. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect( - new BoostTargetEffect(2, 2, Duration.Custom), SourceTappedCondition.instance, + new BoostTargetEffect(2, 2, Duration.Custom), SourceTappedCondition.TAPPED, "target Goblin creature gets +2/+2"), new ManaCostsImpl("{2}{R}")); ability.addEffect(new ConditionalContinuousEffect(new GainAbilityTargetEffect(HasteAbility.getInstance(), - Duration.Custom), SourceTappedCondition.instance,"and has haste for as long as {this} remains tapped")); + Duration.Custom), SourceTappedCondition.TAPPED,"and has haste for as long as {this} remains tapped")); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent(filter)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/f/FlashOfInsight.java b/Mage.Sets/src/mage/cards/f/FlashOfInsight.java index c0eee07e074..7a8a577922b 100644 --- a/Mage.Sets/src/mage/cards/f/FlashOfInsight.java +++ b/Mage.Sets/src/mage/cards/f/FlashOfInsight.java @@ -1,7 +1,5 @@ - package mage.cards.f; -import java.util.UUID; import mage.MageObject; import mage.ObjectColor; import mage.abilities.Ability; @@ -14,33 +12,34 @@ import mage.abilities.keyword.FlashbackAbility; import mage.cards.*; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.TimingRule; import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardIdPredicate; import mage.filter.predicate.mageobject.ColorPredicate; import mage.game.Game; import mage.players.Player; import mage.target.TargetCard; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class FlashOfInsight extends CardImpl { + private static final FilterCard filter = new FilterCard("blue cards from your graveyard"); + + static { + filter.add(new ColorPredicate(ObjectColor.BLUE)); + } + public FlashOfInsight(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{X}{1}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{X}{1}{U}"); // Look at the top X cards of your library. Put one of them into your hand and the rest on the bottom of your library in any order. this.getSpellAbility().addEffect(new FlashOfInsightEffect()); // Flashback-{1}{U}, Exile X blue cards from your graveyard. - Ability ability = new FlashbackAbility(this, new ManaCostsImpl("{1}{U}")); - FilterCard filter = new FilterCard("blue cards from your graveyard"); - filter.add(new ColorPredicate(ObjectColor.BLUE)); - filter.add(Predicates.not(new CardIdPredicate(getId()))); + Ability ability = new FlashbackAbility(this, new ManaCostsImpl<>("{1}{U}")); ability.addCost(new ExileXFromYourGraveCost(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/f/FleetSwallower.java b/Mage.Sets/src/mage/cards/f/FleetSwallower.java index ec45e0924bf..8dd4df8346e 100644 --- a/Mage.Sets/src/mage/cards/f/FleetSwallower.java +++ b/Mage.Sets/src/mage/cards/f/FleetSwallower.java @@ -48,7 +48,7 @@ class FleetSwallowerEffect extends OneShotEffect { public FleetSwallowerEffect() { super(Outcome.Detriment); - staticText = "target player puts the top half of their library, rounded up, into their graveyard"; + staticText = "target player mills half their library, rounded up"; } public FleetSwallowerEffect(final FleetSwallowerEffect effect) { diff --git a/Mage.Sets/src/mage/cards/f/FleetingSpirit.java b/Mage.Sets/src/mage/cards/f/FleetingSpirit.java new file mode 100644 index 00000000000..9423634b7f6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FleetingSpirit.java @@ -0,0 +1,94 @@ +package mage.cards.f; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.costs.common.ExileFromGraveCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ReturnToBattlefieldUnderOwnerControlTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.constants.*; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCardInYourGraveyard; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author weirddan455 + */ +public final class FleetingSpirit extends CardImpl { + + public FleetingSpirit(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(3); + this.toughness = new MageInt(1); + + // {W}, Exile three cards from your graveyard: Fleeting Spirit gains first strike until end of turn. + Ability ability = new SimpleActivatedAbility( + new GainAbilitySourceEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn), + new ManaCostsImpl<>("{W}") + ); + ability.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard(3, StaticFilters.FILTER_CARDS_FROM_YOUR_GRAVEYARD))); + this.addAbility(ability); + + // Discard a card: Exile Fleeting Spirit. Return it to the battlefield under its owner's control at the beginning of the next end step. + this.addAbility(new SimpleActivatedAbility(new FleetingSpiritEffect(), new DiscardCardCost())); + } + + private FleetingSpirit(final FleetingSpirit card) { + super(card); + } + + @Override + public FleetingSpirit copy() { + return new FleetingSpirit(this); + } +} + +class FleetingSpiritEffect extends OneShotEffect { + + public FleetingSpiritEffect() { + super(Outcome.Protect); + staticText = "Exile {this}. Return it to the battlefield under its owner's control at the beginning of the next end step"; + } + + private FleetingSpiritEffect(final FleetingSpiritEffect effect) { + super(effect); + } + + @Override + public FleetingSpiritEffect copy() { + return new FleetingSpiritEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent permanent = game.getPermanent(source.getSourceId()); + if (controller == null || permanent == null) { + return false; + } + int zcc = permanent.getZoneChangeCounter(game); + if (!controller.moveCards(permanent, Zone.EXILED, source, game)) { + return false; + } + Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false); + effect.setTargetPointer(new FixedTarget(permanent.getId(), zcc + 1)); + AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect); + game.addDelayedTriggeredAbility(delayedAbility, source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/f/FleetwheelCruiser.java b/Mage.Sets/src/mage/cards/f/FleetwheelCruiser.java index eab6593b27f..504a212f336 100644 --- a/Mage.Sets/src/mage/cards/f/FleetwheelCruiser.java +++ b/Mage.Sets/src/mage/cards/f/FleetwheelCruiser.java @@ -1,7 +1,5 @@ - package mage.cards.f; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.common.continuous.AddCardTypeSourceEffect; @@ -14,8 +12,9 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; +import java.util.UUID; + /** - * * @author emerald000 */ public final class FleetwheelCruiser extends CardImpl { @@ -33,8 +32,9 @@ public final class FleetwheelCruiser extends CardImpl { this.addAbility(HasteAbility.getInstance()); // When Fleetwheel Cruiser enters the battlefield, it becomes an artifact creature until the end of turn. - this.addAbility(new EntersBattlefieldTriggeredAbility( - new AddCardTypeSourceEffect(Duration.EndOfTurn, CardType.ARTIFACT, CardType.CREATURE))); + this.addAbility(new EntersBattlefieldTriggeredAbility(new AddCardTypeSourceEffect( + Duration.EndOfTurn, CardType.ARTIFACT, CardType.CREATURE + ).setText("it becomes an artifact creature until end of turn"))); // Crew 2 this.addAbility(new CrewAbility(2)); diff --git a/Mage.Sets/src/mage/cards/f/Fleshtaker.java b/Mage.Sets/src/mage/cards/f/Fleshtaker.java index 2b83c1e8110..96560f84338 100644 --- a/Mage.Sets/src/mage/cards/f/Fleshtaker.java +++ b/Mage.Sets/src/mage/cards/f/Fleshtaker.java @@ -36,7 +36,7 @@ public final class Fleshtaker extends CardImpl { Ability ability = new SacrificePermanentTriggeredAbility( new GainLifeEffect(1), StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE ); - ability.addEffect(new ScryEffect(1, false).concatBy("and")); + ability.addEffect(new ScryEffect(1).concatBy("and")); this.addAbility(ability); // {1}, Sacrifice another creature: Fleshtaker gets +2/+2 until end of turn. diff --git a/Mage.Sets/src/mage/cards/f/FlintGolem.java b/Mage.Sets/src/mage/cards/f/FlintGolem.java index e299b0e19ad..ee04ec4c3f6 100644 --- a/Mage.Sets/src/mage/cards/f/FlintGolem.java +++ b/Mage.Sets/src/mage/cards/f/FlintGolem.java @@ -2,17 +2,12 @@ package mage.cards.f; import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.common.BecomesBlockedByCreatureTriggeredAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; +import mage.abilities.effects.common.MillCardsTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; /** * @@ -26,8 +21,10 @@ public final class FlintGolem extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(3); - // Whenever Flint Golem becomes blocked, defending player puts the top three cards of their library into their graveyard. - this.addAbility(new BecomesBlockedByCreatureTriggeredAbility(new FlintGolemEffect(), false)); + // Whenever Flint Golem becomes blocked, defending player mills three cards + this.addAbility(new BecomesBlockedSourceTriggeredAbility( + new MillCardsTargetEffect(3).setText("defending player mills three cards"), + false, true)); } private FlintGolem(final FlintGolem card) { @@ -39,33 +36,3 @@ public final class FlintGolem extends CardImpl { return new FlintGolem(this); } } - -class FlintGolemEffect extends OneShotEffect { - - public FlintGolemEffect() { - super(Outcome.Detriment); - this.staticText = "defending player mills three cards"; - } - - public FlintGolemEffect(final FlintGolemEffect effect) { - super(effect); - } - - @Override - public FlintGolemEffect copy() { - return new FlintGolemEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent blockingCreature = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (blockingCreature != null) { - Player opponent = game.getPlayer(blockingCreature.getControllerId()); - if (opponent != null) { - opponent.millCards(3, source, game); - return true; - } - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/f/FlourishingDefenses.java b/Mage.Sets/src/mage/cards/f/FlourishingDefenses.java index d9fc0642148..d262bf6a8e3 100644 --- a/Mage.Sets/src/mage/cards/f/FlourishingDefenses.java +++ b/Mage.Sets/src/mage/cards/f/FlourishingDefenses.java @@ -12,6 +12,7 @@ import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.game.permanent.token.ElfWarriorToken; +import java.util.Optional; import java.util.UUID; /** @@ -60,7 +61,10 @@ class FlourishingDefensesTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { if (event.getData().equals(CounterType.M1M1.getName())) { - Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId()); + Permanent permanent = Optional + .ofNullable(game.getPermanentOrLKIBattlefield(event.getTargetId())) + .orElse(game.getPermanentEntering(event.getTargetId())); + return permanent != null && permanent.isCreature(game); } return false; diff --git a/Mage.Sets/src/mage/cards/f/FlourishingHunter.java b/Mage.Sets/src/mage/cards/f/FlourishingHunter.java new file mode 100644 index 00000000000..95717e8dd4c --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FlourishingHunter.java @@ -0,0 +1,79 @@ +package mage.cards.f; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +/** + * + * @author weirddan455 + */ +public final class FlourishingHunter extends CardImpl { + + public FlourishingHunter(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}{G}"); + + this.subtype.add(SubType.WOLF); + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(6); + this.toughness = new MageInt(6); + + // When Flourishing Hunter enters the battlefield, you gain life equal to the greatest toughness among other creatures you control. + this.addAbility(new EntersBattlefieldTriggeredAbility(new FlourishingHunterEffect())); + } + + private FlourishingHunter(final FlourishingHunter card) { + super(card); + } + + @Override + public FlourishingHunter copy() { + return new FlourishingHunter(this); + } +} + +class FlourishingHunterEffect extends OneShotEffect { + + public FlourishingHunterEffect() { + super(Outcome.GainLife); + staticText = "you gain life equal to the greatest toughness among other creatures you control"; + } + + private FlourishingHunterEffect(final FlourishingHunterEffect effect) { + super(effect); + } + + @Override + public FlourishingHunterEffect copy() { + return new FlourishingHunterEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + int greatestToughness = 0; + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(controller.getId())) { + if (permanent.isCreature(game) && !permanent.getId().equals(source.getSourceId())) { + int toughness = permanent.getToughness().getValue(); + if (toughness > greatestToughness) { + greatestToughness = toughness; + } + } + } + controller.gainLife(greatestToughness, game, source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/f/FlowstoneArmor.java b/Mage.Sets/src/mage/cards/f/FlowstoneArmor.java index b8bb885a2f5..ab06e308a4b 100644 --- a/Mage.Sets/src/mage/cards/f/FlowstoneArmor.java +++ b/Mage.Sets/src/mage/cards/f/FlowstoneArmor.java @@ -30,7 +30,7 @@ public final class FlowstoneArmor extends CardImpl { this.addAbility(new SkipUntapOptionalAbility()); // {3}, {tap}: Target creature gets +1/-1 for as long as Flowstone Armor remains tapped. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect( - new BoostTargetEffect(1, -1, Duration.Custom), SourceTappedCondition.instance, + new BoostTargetEffect(1, -1, Duration.Custom), SourceTappedCondition.TAPPED, "target creature gets +1/-1 for as long as {this} remains tapped"), new ManaCostsImpl("{3}")); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent()); diff --git a/Mage.Sets/src/mage/cards/f/FlurryOfWings.java b/Mage.Sets/src/mage/cards/f/FlurryOfWings.java index dd325c2afd2..789e215f3bc 100644 --- a/Mage.Sets/src/mage/cards/f/FlurryOfWings.java +++ b/Mage.Sets/src/mage/cards/f/FlurryOfWings.java @@ -1,13 +1,13 @@ - package mage.cards.f; import java.util.UUID; +import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterAttackingCreature; +import mage.filter.StaticFilters; import mage.game.permanent.token.BirdSoldierToken; /** @@ -16,12 +16,12 @@ import mage.game.permanent.token.BirdSoldierToken; */ public final class FlurryOfWings extends CardImpl { - private static final FilterAttackingCreature filter = new FilterAttackingCreature("the number of attacking creatures"); + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(StaticFilters.FILTER_ATTACKING_CREATURES, null); public FlurryOfWings(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{G}{W}{U}"); - this.getSpellAbility().addEffect(new CreateTokenEffect(new BirdSoldierToken(), new PermanentsOnBattlefieldCount(filter))); + this.getSpellAbility().addEffect(new CreateTokenEffect(new BirdSoldierToken(), xValue)); } private FlurryOfWings(final FlurryOfWings card) { diff --git a/Mage.Sets/src/mage/cards/f/FoeRazerRegent.java b/Mage.Sets/src/mage/cards/f/FoeRazerRegent.java index 188274190ab..1f2b30ccf9d 100644 --- a/Mage.Sets/src/mage/cards/f/FoeRazerRegent.java +++ b/Mage.Sets/src/mage/cards/f/FoeRazerRegent.java @@ -19,7 +19,6 @@ import mage.counters.CounterType; 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.common.TargetCreaturePermanent; import mage.target.targetpointer.FixedTarget; @@ -41,7 +40,7 @@ public final class FoeRazerRegent extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Foe-Razer Regent enters the battlefield, you may have it fight target creature you don't control. - Ability ability = new EntersBattlefieldTriggeredAbility(new FightTargetSourceEffect(), true); + Ability ability = new EntersBattlefieldTriggeredAbility(new FightTargetSourceEffect().setText("you may have it fight target creature you don't control"), true); ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/f/Fogwalker.java b/Mage.Sets/src/mage/cards/f/Fogwalker.java index e28452db148..da868d38759 100644 --- a/Mage.Sets/src/mage/cards/f/Fogwalker.java +++ b/Mage.Sets/src/mage/cards/f/Fogwalker.java @@ -10,8 +10,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; /** @@ -20,12 +19,6 @@ import mage.target.common.TargetCreaturePermanent; */ public final class Fogwalker extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public Fogwalker(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}"); this.subtype.add(SubType.SPIRIT); @@ -36,7 +29,7 @@ public final class Fogwalker extends CardImpl { this.addAbility(new SkulkAbility()); // When Fogwalker enters the battlefield, target creature an opponent controls doesn't untap during it controler's next untap step. EntersBattlefieldTriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new DontUntapInControllersNextUntapStepTargetEffect()); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/f/FollowedFootsteps.java b/Mage.Sets/src/mage/cards/f/FollowedFootsteps.java index 65c333ac9bd..352384a18f2 100644 --- a/Mage.Sets/src/mage/cards/f/FollowedFootsteps.java +++ b/Mage.Sets/src/mage/cards/f/FollowedFootsteps.java @@ -81,7 +81,7 @@ class FollowedFootstepsEffect extends OneShotEffect { Permanent target = game.getPermanentOrLKIBattlefield(enchantment.getAttachedTo()); if (target != null) { Effect effect = new CreateTokenCopyTargetEffect(); - effect.setTargetPointer(new FixedTarget(enchantment.getAttachedTo())); + effect.setTargetPointer(new FixedTarget(enchantment.getAttachedTo(), game)); return effect.apply(game, source); } } diff --git a/Mage.Sets/src/mage/cards/f/FontOfReturn.java b/Mage.Sets/src/mage/cards/f/FontOfReturn.java index 8c5cbcd0d54..1491cd26268 100644 --- a/Mage.Sets/src/mage/cards/f/FontOfReturn.java +++ b/Mage.Sets/src/mage/cards/f/FontOfReturn.java @@ -1,4 +1,3 @@ - package mage.cards.f; import java.util.UUID; @@ -11,7 +10,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Zone; -import mage.filter.common.FilterCreatureCard; +import mage.filter.StaticFilters; import mage.target.common.TargetCardInYourGraveyard; /** @@ -23,11 +22,10 @@ public final class FontOfReturn extends CardImpl { public FontOfReturn(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{B}"); - // {3}{B}, Sacrifice Font of Return: Return up to three target creature cards from your graveyard to your hand. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ReturnFromGraveyardToHandTargetEffect(), new ManaCostsImpl("{3}{B}")); ability.addCost(new SacrificeSourceCost()); - ability.addTarget(new TargetCardInYourGraveyard(0, 3, new FilterCreatureCard("creature cards from your graveyard"))); + ability.addTarget(new TargetCardInYourGraveyard(0, 3, StaticFilters.FILTER_CARD_CREATURES_YOUR_GRAVEYARD)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/f/FootbottomFeast.java b/Mage.Sets/src/mage/cards/f/FootbottomFeast.java index 25af502c72f..e8c1078ee90 100644 --- a/Mage.Sets/src/mage/cards/f/FootbottomFeast.java +++ b/Mage.Sets/src/mage/cards/f/FootbottomFeast.java @@ -1,4 +1,3 @@ - package mage.cards.f; import java.util.UUID; @@ -7,7 +6,7 @@ import mage.abilities.effects.common.PutOnLibraryTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterCreatureCard; +import mage.filter.StaticFilters; import mage.target.common.TargetCardInYourGraveyard; /** @@ -21,8 +20,8 @@ public final class FootbottomFeast extends CardImpl { // Put any number of target creature cards from your graveyard on top of your library. this.getSpellAbility().addEffect(new PutOnLibraryTargetEffect(true)); - this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, Integer.MAX_VALUE, new FilterCreatureCard("creature cards from your graveyard"))); - + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, Integer.MAX_VALUE, StaticFilters.FILTER_CARD_CREATURES_YOUR_GRAVEYARD)); + // Draw a card. this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); } diff --git a/Mage.Sets/src/mage/cards/f/ForceOfWill.java b/Mage.Sets/src/mage/cards/f/ForceOfWill.java index 43cb3c1aeee..a313bbe9d6a 100644 --- a/Mage.Sets/src/mage/cards/f/ForceOfWill.java +++ b/Mage.Sets/src/mage/cards/f/ForceOfWill.java @@ -9,8 +9,6 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.filter.common.FilterOwnedCard; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardIdPredicate; import mage.filter.predicate.mageobject.ColorPredicate; import mage.target.TargetSpell; import mage.target.common.TargetCardInHand; @@ -22,14 +20,17 @@ import java.util.UUID; */ public final class ForceOfWill extends CardImpl { + private static final FilterOwnedCard filter + = new FilterOwnedCard("a blue card from your hand"); + + static { + filter.add(new ColorPredicate(ObjectColor.BLUE)); + } + public ForceOfWill(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{U}{U}"); // You may pay 1 life and exile a blue card from your hand rather than pay Force of Will's mana cost. - FilterOwnedCard filter = new FilterOwnedCard("a blue card from your hand"); - filter.add(new ColorPredicate(ObjectColor.BLUE)); - filter.add(Predicates.not(new CardIdPredicate(this.getId()))); // the exile cost can never be paid with the card itself - AlternativeCostSourceAbility ability = new AlternativeCostSourceAbility(new PayLifeCost(1)); ability.addCost(new ExileFromHandCost(new TargetCardInHand(filter))); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/f/ForceProjection.java b/Mage.Sets/src/mage/cards/f/ForceProjection.java index e185fe46d15..45bef91531c 100644 --- a/Mage.Sets/src/mage/cards/f/ForceProjection.java +++ b/Mage.Sets/src/mage/cards/f/ForceProjection.java @@ -80,7 +80,7 @@ class ForceProjectionEffect extends OneShotEffect { // and gains "When this creature becomes the target of a spell, sacrifice it." Effect sacrificeEffect = new SacrificeSourceEffect(); - sacrificeEffect.setTargetPointer(new FixedTarget(effect.getAddedPermanent().get(0), game)); + sacrificeEffect.setTargetPointer(new FixedTarget(effect.getAddedPermanents().get(0), game)); TriggeredAbility ability = new BecomesTargetTriggeredAbility(sacrificeEffect, new FilterSpell()); game.addTriggeredAbility(ability, null); diff --git a/Mage.Sets/src/mage/cards/f/Forcefield.java b/Mage.Sets/src/mage/cards/f/Forcefield.java index 2fbbb99282f..95248b163ad 100644 --- a/Mage.Sets/src/mage/cards/f/Forcefield.java +++ b/Mage.Sets/src/mage/cards/f/Forcefield.java @@ -1,4 +1,3 @@ - package mage.cards.f; import java.util.UUID; @@ -19,7 +18,6 @@ import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.permanent.UnblockedPredicate; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.Target; @@ -33,7 +31,7 @@ import mage.target.targetpointer.FixedTarget; public final class Forcefield extends CardImpl { public Forcefield(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); // {1}: The next time an unblocked creature of your choice would deal combat damage to you this turn, prevent all but 1 of that damage. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new ForcefieldEffect(), new GenericManaCost(1))); @@ -50,26 +48,27 @@ public final class Forcefield extends CardImpl { } class ForcefieldEffect extends OneShotEffect { - + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("an unblocked creature"); + static { filter.add(UnblockedPredicate.instance); } - + ForcefieldEffect() { super(Outcome.PreventDamage); this.staticText = "The next time an unblocked creature of your choice would deal combat damage to you this turn, prevent all but 1 of that damage"; } - + ForcefieldEffect(final ForcefieldEffect effect) { super(effect); } - + @Override public ForcefieldEffect copy() { return new ForcefieldEffect(this); } - + @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); @@ -82,7 +81,7 @@ class ForcefieldEffect extends OneShotEffect { game.informPlayers(sourceObject.getLogName() + ": " + controller.getLogName() + " has chosen " + creature.getLogName()); } ContinuousEffect effect = new ForcefieldPreventionEffect(); - effect.setTargetPointer(new FixedTarget(target.getFirstTarget())); + effect.setTargetPointer(new FixedTarget(target.getFirstTarget(), game)); game.addEffect(effect, source); } return true; diff --git a/Mage.Sets/src/mage/cards/f/ForebodingStatue.java b/Mage.Sets/src/mage/cards/f/ForebodingStatue.java new file mode 100644 index 00000000000..1bd52d1d33c --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/ForebodingStatue.java @@ -0,0 +1,58 @@ +package mage.cards.f; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.condition.common.SourceHasCounterCondition; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.effects.common.UntapSourceEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.TransformAbility; +import mage.abilities.mana.AnyColorManaAbility; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.counters.CounterType; + +/** + * + * @author weirddan455 + */ +public final class ForebodingStatue extends CardImpl { + + public ForebodingStatue(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}"); + + this.subtype.add(SubType.CONSTRUCT); + this.power = new MageInt(1); + this.toughness = new MageInt(2); + this.secondSideCardClazz = mage.cards.f.ForsakenThresher.class; + + // {T}: Add one mana of any color. Put an omen counter on Foreboding Statue. + Ability ability = new AnyColorManaAbility(); + ability.addEffect(new AddCountersSourceEffect(CounterType.OMEN.createInstance())); + this.addAbility(ability); + + // At the beginning of your end step, if there are three or more omen counters on Foreboding Statue, uptap it, then transform it. + this.addAbility(new TransformAbility()); + ability = new BeginningOfEndStepTriggeredAbility( + Zone.BATTLEFIELD, new UntapSourceEffect().setText("untap it,"), TargetController.YOU, + new SourceHasCounterCondition(CounterType.OMEN, 3), false + ).setTriggerPhrase("At the beginning of your end step, if there are three or more omen counters on {this}, "); + ability.addEffect(new TransformSourceEffect().setText("then transform it")); + this.addAbility(ability); + } + + private ForebodingStatue(final ForebodingStatue card) { + super(card); + } + + @Override + public ForebodingStatue copy() { + return new ForebodingStatue(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/ForeverYoung.java b/Mage.Sets/src/mage/cards/f/ForeverYoung.java index 144e81ebf09..c90b5c59023 100644 --- a/Mage.Sets/src/mage/cards/f/ForeverYoung.java +++ b/Mage.Sets/src/mage/cards/f/ForeverYoung.java @@ -1,24 +1,14 @@ package mage.cards.f; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; +import java.util.UUID; import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.PutOnLibraryTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.cards.CardsImpl; import mage.constants.CardType; -import mage.constants.Outcome; import mage.filter.StaticFilters; -import mage.game.Game; -import mage.players.Player; -import mage.target.Target; import mage.target.common.TargetCardInYourGraveyard; -import java.util.Collection; -import java.util.Objects; -import java.util.UUID; -import java.util.stream.Collectors; - /** * @author TheElk801 */ @@ -28,10 +18,8 @@ public final class ForeverYoung extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}"); // Put any number of target creature cards from your graveyard on top of your library. - this.getSpellAbility().addEffect(new ForeverYoungEffect()); - this.getSpellAbility().addTarget(new TargetCardInYourGraveyard( - 0, Integer.MAX_VALUE, StaticFilters.FILTER_CARD_CREATURES_YOUR_GRAVEYARD - )); + this.getSpellAbility().addEffect(new PutOnLibraryTargetEffect(true)); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, Integer.MAX_VALUE, StaticFilters.FILTER_CARD_CREATURES_YOUR_GRAVEYARD)); // Draw a card. this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); @@ -46,37 +34,3 @@ public final class ForeverYoung extends CardImpl { return new ForeverYoung(this); } } - -class ForeverYoungEffect extends OneShotEffect { - - ForeverYoungEffect() { - super(Outcome.Benefit); - staticText = "Put any number of target creature cards from your graveyard on top of your library"; - } - - private ForeverYoungEffect(final ForeverYoungEffect effect) { - super(effect); - } - - @Override - public ForeverYoungEffect copy() { - return new ForeverYoungEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player == null) { - return false; - } - return player.putCardsOnTopOfLibrary(new CardsImpl( - source.getTargets() - .stream() - .map(Target::getTargets) - .flatMap(Collection::stream) - .map(game::getCard) - .filter(Objects::nonNull) - .collect(Collectors.toSet()) - ), game, source, true); - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/f/ForgingTheTyriteSword.java b/Mage.Sets/src/mage/cards/f/ForgingTheTyriteSword.java index d2888199f86..b66acdd79e7 100644 --- a/Mage.Sets/src/mage/cards/f/ForgingTheTyriteSword.java +++ b/Mage.Sets/src/mage/cards/f/ForgingTheTyriteSword.java @@ -37,7 +37,7 @@ public final class ForgingTheTyriteSword extends CardImpl { this.subtype.add(SubType.SAGA); // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) - SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); + SagaAbility sagaAbility = new SagaAbility(this); // I, II — Create a Treasure token. sagaAbility.addChapterEffect( diff --git a/Mage.Sets/src/mage/cards/f/ForkedBolt.java b/Mage.Sets/src/mage/cards/f/ForkedBolt.java index adcdeff3430..167d4999165 100644 --- a/Mage.Sets/src/mage/cards/f/ForkedBolt.java +++ b/Mage.Sets/src/mage/cards/f/ForkedBolt.java @@ -1,6 +1,5 @@ package mage.cards.f; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.DamageMultiEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -17,10 +16,8 @@ public final class ForkedBolt extends CardImpl { public ForkedBolt(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{R}"); - // Forked Bolt deals 2 damage divided as you choose among one or two target creatures and/or players. - Effect effect = new DamageMultiEffect(2); - effect.setText("{this} deals 2 damage divided as you choose among one or two target creatures and/or players"); - this.getSpellAbility().addEffect(effect); + // Forked Bolt deals 2 damage divided as you choose among one or two targets. + this.getSpellAbility().addEffect(new DamageMultiEffect(2)); this.getSpellAbility().addTarget(new TargetAnyTargetAmount(2)); } diff --git a/Mage.Sets/src/mage/cards/f/ForsakenThresher.java b/Mage.Sets/src/mage/cards/f/ForsakenThresher.java new file mode 100644 index 00000000000..702ccc45023 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/ForsakenThresher.java @@ -0,0 +1,38 @@ +package mage.cards.f; + +import mage.MageInt; +import mage.abilities.common.BeginningOfPreCombatMainTriggeredAbility; +import mage.abilities.effects.mana.AddManaOfAnyColorEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; + +import java.util.UUID; + +public class ForsakenThresher extends CardImpl { + + public ForsakenThresher(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, ""); + + this.subtype.add(SubType.CONSTRUCT); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + + // Back half of Foreboding Statue + this.nightCard = true; + + // At the beginning of your precombat main phase, add one mana of any color. + this.addAbility(new BeginningOfPreCombatMainTriggeredAbility(new AddManaOfAnyColorEffect(), TargetController.YOU, false)); + } + + private ForsakenThresher(final ForsakenThresher card) { + super(card); + } + + @Override + public ForsakenThresher copy() { + return new ForsakenThresher(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/ForswornPaladin.java b/Mage.Sets/src/mage/cards/f/ForswornPaladin.java index 16ed68fd800..1ba35dffdf3 100644 --- a/Mage.Sets/src/mage/cards/f/ForswornPaladin.java +++ b/Mage.Sets/src/mage/cards/f/ForswornPaladin.java @@ -39,7 +39,7 @@ public final class ForswornPaladin extends CardImpl { this.toughness = new MageInt(1); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // {1}{B}, {T}, Pay 1 life: Create a Treasure token. Ability ability = new SimpleActivatedAbility(new CreateTokenEffect(new TreasureToken()), new ManaCostsImpl<>("{1}{B}")); diff --git a/Mage.Sets/src/mage/cards/f/FortuitousFind.java b/Mage.Sets/src/mage/cards/f/FortuitousFind.java index 6dd23eab946..eac793b0a75 100644 --- a/Mage.Sets/src/mage/cards/f/FortuitousFind.java +++ b/Mage.Sets/src/mage/cards/f/FortuitousFind.java @@ -1,10 +1,11 @@ package mage.cards.f; import mage.abilities.Mode; -import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.filter.FilterCard; import mage.filter.StaticFilters; import mage.filter.common.FilterArtifactCard; import mage.target.common.TargetCardInYourGraveyard; @@ -16,6 +17,8 @@ import java.util.UUID; */ public final class FortuitousFind extends CardImpl { + private static final FilterCard filter = new FilterArtifactCard("artifact card from your graveyard"); + public FortuitousFind(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}"); @@ -24,12 +27,12 @@ public final class FortuitousFind extends CardImpl { this.getSpellAbility().getModes().setMaxModes(2); // Return target artifact card from your graveyard to your hand.; - this.getSpellAbility().addEffect(new ReturnToHandTargetEffect()); - this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(new FilterArtifactCard("artifact card from your graveyard")).withChooseHint("return to hand")); + this.getSpellAbility().addEffect(new ReturnFromGraveyardToHandTargetEffect()); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(filter).withChooseHint("return to hand")); // or Return target creature card from your graveyard to your hand. Mode mode = new Mode(); - mode.addEffect(new ReturnToHandTargetEffect()); + mode.addEffect(new ReturnFromGraveyardToHandTargetEffect()); mode.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD).withChooseHint("return to hand")); this.getSpellAbility().addMode(mode); } diff --git a/Mage.Sets/src/mage/cards/f/FoundryHornet.java b/Mage.Sets/src/mage/cards/f/FoundryHornet.java index 64b2f21d8fb..4ac213ded3d 100644 --- a/Mage.Sets/src/mage/cards/f/FoundryHornet.java +++ b/Mage.Sets/src/mage/cards/f/FoundryHornet.java @@ -14,10 +14,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.TargetController; -import mage.counters.CounterType; -import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; /** * @@ -25,14 +22,6 @@ import mage.filter.common.FilterCreaturePermanent; */ public final class FoundryHornet extends CardImpl { - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("a creature with a +1/+1 counter on it"); - private static final FilterCreaturePermanent filterOpponent = new FilterCreaturePermanent(); - - static { - filter.add(CounterType.P1P1.getPredicate()); - filterOpponent.add(TargetController.OPPONENT.getControllerPredicate()); - } - private static final String rule = "When {this} enters the battlefield, if you control a creature with a +1/+1 counter on it, creatures your opponents control get -1/-1 until end of turn."; public FoundryHornet(UUID ownerId, CardSetInfo setInfo) { @@ -46,8 +35,8 @@ public final class FoundryHornet extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Foundry Hornet enters the battlefield, if you control a creature with a +1/+1 counter on it, creatures your opponents control get -1/-1 until end of turn. - TriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new BoostAllEffect(-1, -1, Duration.EndOfTurn, filterOpponent, false), false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new PermanentsOnTheBattlefieldCondition(filter), rule)); + TriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new BoostAllEffect(-1, -1, Duration.EndOfTurn, StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE, false), false); + this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new PermanentsOnTheBattlefieldCondition(StaticFilters.FILTER_A_CREATURE_P1P1), rule)); } private FoundryHornet(final FoundryHornet card) { diff --git a/Mage.Sets/src/mage/cards/f/FoundryStreetDenizen.java b/Mage.Sets/src/mage/cards/f/FoundryStreetDenizen.java index 8542f5d87a7..b8e08a8265c 100644 --- a/Mage.Sets/src/mage/cards/f/FoundryStreetDenizen.java +++ b/Mage.Sets/src/mage/cards/f/FoundryStreetDenizen.java @@ -1,4 +1,3 @@ - package mage.cards.f; import java.util.UUID; @@ -11,10 +10,8 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.TargetController; import mage.constants.Zone; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; import mage.filter.predicate.mageobject.AnotherPredicate; @@ -24,13 +21,12 @@ import mage.filter.predicate.mageobject.AnotherPredicate; */ public final class FoundryStreetDenizen extends CardImpl { - private static final FilterPermanent filter = new FilterCreaturePermanent("another red creature"); + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("another red creature"); static { filter.add(AnotherPredicate.instance); - filter.add(TargetController.YOU.getControllerPredicate()); filter.add(new ColorPredicate(ObjectColor.RED)); } - + public FoundryStreetDenizen(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{R}"); this.subtype.add(SubType.GOBLIN); diff --git a/Mage.Sets/src/mage/cards/f/FourthBridgeProwler.java b/Mage.Sets/src/mage/cards/f/FourthBridgeProwler.java index 12f1e809726..8c7ea0c9779 100644 --- a/Mage.Sets/src/mage/cards/f/FourthBridgeProwler.java +++ b/Mage.Sets/src/mage/cards/f/FourthBridgeProwler.java @@ -1,21 +1,18 @@ - package mage.cards.f; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Duration; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class FourthBridgeProwler extends CardImpl { @@ -29,9 +26,8 @@ public final class FourthBridgeProwler extends CardImpl { this.toughness = new MageInt(1); // When Fourth Bridge Prowler enters the battlefield, you may have target creature get -1/-1 until end of turn. - Effect effect = new BoostTargetEffect(-1, -1, Duration.EndOfTurn); - effect.setText("have target creature get -1/-1 until end of turn"); - Ability ability = new EntersBattlefieldTriggeredAbility(effect, true); + Ability ability = new EntersBattlefieldTriggeredAbility(new BoostTargetEffect(-1, -1) + .setText("you may have target creature get -1/-1 until end of turn"), true); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/f/FractalHarness.java b/Mage.Sets/src/mage/cards/f/FractalHarness.java index 8978cdfdce8..5d409f34796 100644 --- a/Mage.Sets/src/mage/cards/f/FractalHarness.java +++ b/Mage.Sets/src/mage/cards/f/FractalHarness.java @@ -35,7 +35,7 @@ public final class FractalHarness extends CardImpl { // Whenever equipped creature attacks, double the number of +1/+1 counters on it. this.addAbility(new AttacksAttachedTriggeredAbility( - new FractalHarnessDoubleEffect(), AttachmentType.EQUIPMENT, false + new FractalHarnessDoubleEffect(), AttachmentType.EQUIPMENT, false, true )); // Equip {2} @@ -108,14 +108,11 @@ class FractalHarnessDoubleEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = (Permanent) getValue("attachedPermanent"); + Permanent permanent = game.getPermanent(targetPointer.getFirst(game, source)); if (permanent == null) { return false; } - // BUG : changed this to a integer due to the trigger firing twice - final int addedCounters = permanent.getCounters(game).getCount(CounterType.P1P1); - // BUG : this oneShotEffect is being run twice for some reason, so the number of counters is four times as many - return permanent.addCounters(CounterType.P1P1.createInstance(addedCounters), + return permanent.addCounters(CounterType.P1P1.createInstance(permanent.getCounters(game).getCount(CounterType.P1P1)), source.getControllerId(), source, game); } } diff --git a/Mage.Sets/src/mage/cards/f/FracturedLoyalty.java b/Mage.Sets/src/mage/cards/f/FracturedLoyalty.java index ca75b74ecbf..268da9d0199 100644 --- a/Mage.Sets/src/mage/cards/f/FracturedLoyalty.java +++ b/Mage.Sets/src/mage/cards/f/FracturedLoyalty.java @@ -14,7 +14,6 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetPermanent; @@ -77,7 +76,7 @@ public final class FracturedLoyalty extends CardImpl { if (enchantment.getAttachedTo() != null) { if (controller != null && !enchantedCreature.isControlledBy(this.getTargetPointer().getFirst(game, source))) { ContinuousEffect effect = new GainControlTargetEffect(Duration.EndOfGame, this.getTargetPointer().getFirst(game, source)); - effect.setTargetPointer(new FixedTarget(enchantment.getAttachedTo())); + effect.setTargetPointer(new FixedTarget(enchantment.getAttachedTo(), game)); game.addEffect(effect, source); return true; } diff --git a/Mage.Sets/src/mage/cards/f/FragmentOfKonda.java b/Mage.Sets/src/mage/cards/f/FragmentOfKonda.java new file mode 100644 index 00000000000..77b85acde32 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FragmentOfKonda.java @@ -0,0 +1,44 @@ +package mage.cards.f; + +import mage.MageInt; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.keyword.DefenderAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FragmentOfKonda extends CardImpl { + + public FragmentOfKonda(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, ""); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.NOBLE); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + this.color.setWhite(true); + this.nightCard = true; + + // Defender + this.addAbility(DefenderAbility.getInstance()); + + // When Fragment of Konda dies, draw a card. + this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1))); + } + + private FragmentOfKonda(final FragmentOfKonda card) { + super(card); + } + + @Override + public FragmentOfKonda copy() { + return new FragmentOfKonda(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FreeForAll.java b/Mage.Sets/src/mage/cards/f/FreeForAll.java index 77cef34a0e7..c82f5d67618 100644 --- a/Mage.Sets/src/mage/cards/f/FreeForAll.java +++ b/Mage.Sets/src/mage/cards/f/FreeForAll.java @@ -16,7 +16,6 @@ import mage.constants.Zone; import mage.filter.StaticFilters; import mage.game.ExileZone; import mage.game.Game; -import mage.game.permanent.Permanent; import mage.players.Player; import mage.util.CardUtil; @@ -69,15 +68,14 @@ class FreeForAllExileAllEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - Permanent sourcePermanent = source.getSourcePermanentOrLKI(game); - if (player == null || sourcePermanent == null) { + if (player == null) { return false; } Cards cards = new CardsImpl(); game.getBattlefield().getActivePermanents( StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), source.getSourceId(), game ).stream().forEach(cards::add); - player.moveCardsToExile(cards.getCards(game), source, game, false, CardUtil.getExileZoneId(game, source), sourcePermanent.getIdName()); + player.moveCardsToExile(cards.getCards(game), source, game, false, CardUtil.getExileZoneId(game, source), CardUtil.getSourceName(game, source)); cards.getCards(game) .stream() .filter(card -> game.getState().getZone(card.getId()) == Zone.EXILED) diff --git a/Mage.Sets/src/mage/cards/f/FrenziedDevils.java b/Mage.Sets/src/mage/cards/f/FrenziedDevils.java new file mode 100644 index 00000000000..013015f2977 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FrenziedDevils.java @@ -0,0 +1,46 @@ +package mage.cards.f; + +import mage.MageInt; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FrenziedDevils extends CardImpl { + + public FrenziedDevils(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}"); + + this.subtype.add(SubType.DEVIL); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // Whenever you cast a noncreature spell, Frenzied Devils gets +2/+2 until end of turn. + this.addAbility(new SpellCastControllerTriggeredAbility( + new BoostSourceEffect(2, 2, Duration.EndOfTurn), + StaticFilters.FILTER_SPELL_A_NON_CREATURE, false + )); + } + + private FrenziedDevils(final FrenziedDevils card) { + super(card); + } + + @Override + public FrenziedDevils copy() { + return new FrenziedDevils(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FrenziedRage.java b/Mage.Sets/src/mage/cards/f/FrenziedRage.java index 52dd2bfec8a..de51f6e52d6 100644 --- a/Mage.Sets/src/mage/cards/f/FrenziedRage.java +++ b/Mage.Sets/src/mage/cards/f/FrenziedRage.java @@ -41,7 +41,7 @@ public final class FrenziedRage extends CardImpl { // Enchanted creature gets +2/+1 and has menace. ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(2, 1)); Effect effect = new GainAbilityAttachedEffect(new MenaceAbility(), AttachmentType.AURA); - effect.setText("and has menace"); + effect.setText("and has menace. (It can't be blocked except by two or more creatures.)"); ability.addEffect(effect); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/f/FrenziedTrapbreaker.java b/Mage.Sets/src/mage/cards/f/FrenziedTrapbreaker.java index 40af95b9dff..771d12a6289 100644 --- a/Mage.Sets/src/mage/cards/f/FrenziedTrapbreaker.java +++ b/Mage.Sets/src/mage/cards/f/FrenziedTrapbreaker.java @@ -39,7 +39,6 @@ public final class FrenziedTrapbreaker extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(3); this.color.setGreen(true); - this.transformable = true; this.nightCard = true; // {1}, Sacrifice Frenzied Trapbreaker: Destroy target artifact or enchantment. diff --git a/Mage.Sets/src/mage/cards/f/FreyaliseLlanowarsFury.java b/Mage.Sets/src/mage/cards/f/FreyaliseLlanowarsFury.java index afab8b04b86..b4ea2016cbe 100644 --- a/Mage.Sets/src/mage/cards/f/FreyaliseLlanowarsFury.java +++ b/Mage.Sets/src/mage/cards/f/FreyaliseLlanowarsFury.java @@ -3,7 +3,6 @@ package mage.cards.f; import mage.ObjectColor; import mage.abilities.LoyaltyAbility; import mage.abilities.common.CanBeYourCommanderAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.DestroyTargetEffect; @@ -38,7 +37,7 @@ public final class FreyaliseLlanowarsFury extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.FREYALISE); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +2: Create a 1/1 green Elf Druid creature token with "{T}: Add {G}." this.addAbility(new LoyaltyAbility(new CreateTokenEffect(new ElfDruidToken()), 2)); diff --git a/Mage.Sets/src/mage/cards/f/FrightshroudCourier.java b/Mage.Sets/src/mage/cards/f/FrightshroudCourier.java index 3deee8e6559..ad7f2697762 100644 --- a/Mage.Sets/src/mage/cards/f/FrightshroudCourier.java +++ b/Mage.Sets/src/mage/cards/f/FrightshroudCourier.java @@ -44,10 +44,10 @@ public final class FrightshroudCourier extends CardImpl { this.addAbility(new SkipUntapOptionalAbility()); // {2}{B}, {tap}: Target Zombie creature gets +2/+2 and has fear for as long as Frightshroud Courier remains tapped. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect( - new BoostTargetEffect(2, 2, Duration.Custom), SourceTappedCondition.instance, + new BoostTargetEffect(2, 2, Duration.Custom), SourceTappedCondition.TAPPED, "target Zombie creature gets +2/+2"), new ManaCostsImpl("{2}{B}")); ability.addEffect(new ConditionalContinuousEffect(new GainAbilityTargetEffect(FearAbility.getInstance(), - Duration.Custom), SourceTappedCondition.instance,"and has fear for as long as {this} remains tapped")); + Duration.Custom), SourceTappedCondition.TAPPED,"and has fear for as long as {this} remains tapped")); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent(filter)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/f/FrillscareMentor.java b/Mage.Sets/src/mage/cards/f/FrillscareMentor.java index 87e3d85b73b..919a18f74df 100644 --- a/Mage.Sets/src/mage/cards/f/FrillscareMentor.java +++ b/Mage.Sets/src/mage/cards/f/FrillscareMentor.java @@ -47,7 +47,9 @@ public final class FrillscareMentor extends CardImpl { // When Frillscare Mentor enters the battlefield, put a menace counter on target non-Human creature you control. Ability ability = new EntersBattlefieldTriggeredAbility( - new AddCountersTargetEffect(CounterType.MENACE.createInstance()) + new AddCountersTargetEffect(CounterType.MENACE.createInstance() + ).setText("put a menace counter on target non-Human creature you control. " + + "(It can't be blocked except by two or more creatures.)") ); ability.addTarget(new TargetPermanent(filter)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/f/FrozenAether.java b/Mage.Sets/src/mage/cards/f/FrozenAether.java index e62b00f5b79..00eb7f7e361 100644 --- a/Mage.Sets/src/mage/cards/f/FrozenAether.java +++ b/Mage.Sets/src/mage/cards/f/FrozenAether.java @@ -1,32 +1,39 @@ package mage.cards.f; -import java.util.UUID; -import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.PermanentsEnterBattlefieldTappedEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.EntersTheBattlefieldEvent; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicates; + +import java.util.UUID; /** - * * @author fireshoes */ public final class FrozenAether extends CardImpl { + private static final FilterPermanent filter + = new FilterPermanent("artifacts, creatures, and lands your opponents control"); + + static { + filter.add(TargetController.OPPONENT.getControllerPredicate()); + filter.add(Predicates.or( + CardType.ARTIFACT.getPredicate(), + CardType.CREATURE.getPredicate(), + CardType.LAND.getPredicate() + )); + } + public FrozenAether(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{3}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{U}"); // Artifacts, creatures, and lands your opponents control enter the battlefield tapped. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new FrozenAetherTapEffect())); + this.addAbility(new SimpleStaticAbility(new PermanentsEnterBattlefieldTappedEffect(filter))); } private FrozenAether(final FrozenAether card) { @@ -38,48 +45,3 @@ public final class FrozenAether extends CardImpl { return new FrozenAether(this); } } - -class FrozenAetherTapEffect extends ReplacementEffectImpl { - - FrozenAetherTapEffect() { - super(Duration.WhileOnBattlefield, Outcome.Tap); - staticText = "Artifacts, creatures, and lands your opponents control enter the battlefield tapped"; - } - - FrozenAetherTapEffect(final FrozenAetherTapEffect effect) { - super(effect); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Permanent target = ((EntersTheBattlefieldEvent) event).getTarget(); - if (target != null) { - target.setTapped(true); - } - return false; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (game.getOpponents(source.getControllerId()).contains(event.getPlayerId())) { - Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget(); - if (permanent != null - && (permanent.isCreature(game) - || permanent.isLand(game) - || permanent.isArtifact(game))) { - return true; - } - } - return false; - } - - @Override - public FrozenAetherTapEffect copy() { - return new FrozenAetherTapEffect(this); - } -} diff --git a/Mage.Sets/src/mage/cards/f/FuneralRites.java b/Mage.Sets/src/mage/cards/f/FuneralRites.java index 133370fa9b7..7c12689b3a0 100644 --- a/Mage.Sets/src/mage/cards/f/FuneralRites.java +++ b/Mage.Sets/src/mage/cards/f/FuneralRites.java @@ -23,7 +23,7 @@ public final class FuneralRites extends CardImpl { this.getSpellAbility().addEffect(new LoseLifeSourceControllerEffect(2) .setText(", lose 2 life")); this.getSpellAbility().addEffect(new MillCardsControllerEffect(2) - .concatBy(", and")); + .concatBy(", then")); } private FuneralRites(final FuneralRites card) { diff --git a/Mage.Sets/src/mage/cards/f/FuriousResistance.java b/Mage.Sets/src/mage/cards/f/FuriousResistance.java index 7bc1dd0401e..612e5327fe8 100644 --- a/Mage.Sets/src/mage/cards/f/FuriousResistance.java +++ b/Mage.Sets/src/mage/cards/f/FuriousResistance.java @@ -1,4 +1,3 @@ - package mage.cards.f; import java.util.UUID; @@ -25,12 +24,11 @@ import mage.target.targetpointer.FixedTarget; * @author jeffwadsworth */ public final class FuriousResistance extends CardImpl { - + private static final FilterCreaturePermanent filter = new FilterBlockingCreature("blocking creature"); public FuriousResistance(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{R}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{R}"); // Target blocking creature gets +3/+0 and gains first strike until end of turn. this.getSpellAbility().addEffect(new FuriousResistanceEffect()); @@ -64,11 +62,11 @@ class FuriousResistanceEffect extends OneShotEffect { if (target == null) { return false; } - + ContinuousEffect effect = new BoostTargetEffect(3, 0, Duration.EndOfTurn); ContinuousEffect effect2 = new GainAbilityTargetEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(target.getId())); - effect2.setTargetPointer(new FixedTarget(target.getId())); + effect.setTargetPointer(new FixedTarget(target.getId(), game)); + effect2.setTargetPointer(new FixedTarget(target.getId(), game)); game.addEffect(effect, source); game.addEffect(effect2, source); return true; diff --git a/Mage.Sets/src/mage/cards/f/FurySliver.java b/Mage.Sets/src/mage/cards/f/FurySliver.java index 0a1c2188ca6..e29441344e9 100644 --- a/Mage.Sets/src/mage/cards/f/FurySliver.java +++ b/Mage.Sets/src/mage/cards/f/FurySliver.java @@ -30,7 +30,7 @@ public final class FurySliver extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(DoubleStrikeAbility.getInstance(), Duration.WhileOnBattlefield, - StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS + StaticFilters.FILTER_PERMANENT_ALL_SLIVERS ) )); } diff --git a/Mage.Sets/src/mage/cards/f/FuturistOperative.java b/Mage.Sets/src/mage/cards/f/FuturistOperative.java new file mode 100644 index 00000000000..b8c26f9a162 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FuturistOperative.java @@ -0,0 +1,106 @@ +package mage.cards.f; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.SourceTappedCondition; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.decorator.ConditionalRestrictionEffect; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.common.UntapSourceEffect; +import mage.abilities.effects.common.combat.CantBeBlockedSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.permanent.Permanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FuturistOperative extends CardImpl { + + public FuturistOperative(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.NINJA); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // As long as Futurist Operative is tapped, it's a Human Citizen with base power and toughness 1/1 and can't be blocked. + Ability ability = new SimpleStaticAbility(new FuturistOperativeEffect()); + ability.addEffect(new ConditionalRestrictionEffect( + new CantBeBlockedSourceEffect(), SourceTappedCondition.TAPPED, "and can't be blocked" + )); + this.addAbility(ability); + + // {2}{U}: Untap Futurist Operative. + this.addAbility(new SimpleActivatedAbility(new UntapSourceEffect(), new ManaCostsImpl<>("{2}{U}"))); + } + + private FuturistOperative(final FuturistOperative card) { + super(card); + } + + @Override + public FuturistOperative copy() { + return new FuturistOperative(this); + } +} + +class FuturistOperativeEffect extends ContinuousEffectImpl { + + FuturistOperativeEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "as long as {this} is tapped, it's a Human Citizen with base power and toughness 1/1"; + } + + private FuturistOperativeEffect(final FuturistOperativeEffect effect) { + super(effect); + } + + @Override + public FuturistOperativeEffect copy() { + return new FuturistOperativeEffect(this); + } + + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (permanent == null || !permanent.isTapped()) { + return false; + } + switch (layer) { + case TypeChangingEffects_4: + permanent.removeAllSubTypes(game); + permanent.addSubType(game, SubType.HUMAN, SubType.CITIZEN); + return true; + case PTChangingEffects_7: + if (sublayer == SubLayer.SetPT_7b) { + permanent.getPower().setValue(1); + permanent.getToughness().setValue(1); + return true; + } + } + return false; + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean hasLayer(Layer layer) { + switch (layer) { + case TypeChangingEffects_4: + case PTChangingEffects_7: + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/f/FuturistSentinel.java b/Mage.Sets/src/mage/cards/f/FuturistSentinel.java new file mode 100644 index 00000000000..d39ea1888d9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FuturistSentinel.java @@ -0,0 +1,36 @@ +package mage.cards.f; + +import mage.MageInt; +import mage.abilities.keyword.CrewAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FuturistSentinel extends CardImpl { + + public FuturistSentinel(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}{U}"); + + this.subtype.add(SubType.VEHICLE); + this.power = new MageInt(6); + this.toughness = new MageInt(6); + + // Crew 3 + this.addAbility(new CrewAbility(3)); + } + + private FuturistSentinel(final FuturistSentinel card) { + super(card); + } + + @Override + public FuturistSentinel copy() { + return new FuturistSentinel(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FyndhornPollen.java b/Mage.Sets/src/mage/cards/f/FyndhornPollen.java index d0dadf5ab42..a6eda41ac77 100644 --- a/Mage.Sets/src/mage/cards/f/FyndhornPollen.java +++ b/Mage.Sets/src/mage/cards/f/FyndhornPollen.java @@ -11,8 +11,6 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; /** * @@ -20,8 +18,6 @@ import mage.filter.common.FilterCreaturePermanent; */ public final class FyndhornPollen extends CardImpl { - private static FilterCreaturePermanent filter = new FilterCreaturePermanent("All creatures"); - public FyndhornPollen(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}"); @@ -29,10 +25,10 @@ public final class FyndhornPollen extends CardImpl { this.addAbility(new CumulativeUpkeepAbility(new ManaCostsImpl("{1}"))); // All creatures get -1/-0. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostAllEffect(-1, 0, Duration.WhileOnBattlefield, filter, false))); + this.addAbility(new SimpleStaticAbility(new BoostAllEffect(-1, 0, Duration.WhileOnBattlefield))); // {1}{G}: All creatures get -1/-0 until end of turn. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostAllEffect(-1, 0, Duration.EndOfTurn, filter, false), new ManaCostsImpl("{1}{G}"))); + this.addAbility(new SimpleActivatedAbility(new BoostAllEffect(-1, 0, Duration.EndOfTurn), new ManaCostsImpl("{1}{G}"))); } private FyndhornPollen(final FyndhornPollen card) { diff --git a/Mage.Sets/src/mage/cards/g/GOTOJAIL.java b/Mage.Sets/src/mage/cards/g/GOTOJAIL.java index 8254ac8f733..d80ba1e2414 100644 --- a/Mage.Sets/src/mage/cards/g/GOTOJAIL.java +++ b/Mage.Sets/src/mage/cards/g/GOTOJAIL.java @@ -14,9 +14,8 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.TargetController; import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -32,18 +31,12 @@ import java.util.UUID; */ public final class GOTOJAIL extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public GOTOJAIL(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}"); // When GO TO JAIL enters the battlefield, exile target creature an opponent controls until GO TO JAIL leaves the battlefield. Ability ability = new EntersBattlefieldTriggeredAbility(new GoToJailExileEffect()); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); ability.addEffect(new CreateDelayedTriggeredAbilityEffect(new OnLeaveReturnExiledToBattlefieldAbility())); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/g/GahijiHonoredOne.java b/Mage.Sets/src/mage/cards/g/GahijiHonoredOne.java index 7679206a28d..169ef57066c 100644 --- a/Mage.Sets/src/mage/cards/g/GahijiHonoredOne.java +++ b/Mage.Sets/src/mage/cards/g/GahijiHonoredOne.java @@ -1,4 +1,3 @@ - package mage.cards.g; import java.util.Set; @@ -16,7 +15,6 @@ import mage.constants.SuperType; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.targetpointer.FixedTarget; @@ -28,7 +26,7 @@ import mage.target.targetpointer.FixedTarget; public final class GahijiHonoredOne extends CardImpl { public GahijiHonoredOne(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{R}{G}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{G}{W}"); addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.BEAST); @@ -53,7 +51,7 @@ public final class GahijiHonoredOne extends CardImpl { class GahijiHonoredOneTriggeredAbility extends TriggeredAbilityImpl { public GahijiHonoredOneTriggeredAbility() { - super(Zone.BATTLEFIELD, new BoostTargetEffect(2,0, Duration.EndOfTurn), false); + super(Zone.BATTLEFIELD, new BoostTargetEffect(2, 0, Duration.EndOfTurn), false); } public GahijiHonoredOneTriggeredAbility(Effect effect, boolean optional, String text) { @@ -81,8 +79,8 @@ class GahijiHonoredOneTriggeredAbility extends TriggeredAbilityImpl { if (defender != null) { Set opponents = game.getOpponents(this.getControllerId()); if (opponents != null && opponents.contains(defender.getId())) { - for (Effect effect: this.getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getSourceId())); + for (Effect effect : this.getEffects()) { + effect.setTargetPointer(new FixedTarget(event.getSourceId(), game)); } return true; } diff --git a/Mage.Sets/src/mage/cards/g/Galedrifter.java b/Mage.Sets/src/mage/cards/g/Galedrifter.java index 1ef6a7f13da..c76b1198447 100644 --- a/Mage.Sets/src/mage/cards/g/Galedrifter.java +++ b/Mage.Sets/src/mage/cards/g/Galedrifter.java @@ -4,7 +4,6 @@ import mage.MageInt; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.keyword.DisturbAbility; import mage.abilities.keyword.FlyingAbility; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -23,15 +22,13 @@ public final class Galedrifter extends CardImpl { this.subtype.add(SubType.HIPPOGRIFF); this.power = new MageInt(3); this.toughness = new MageInt(2); - this.transformable = true; this.secondSideCardClazz = mage.cards.w.Waildrifter.class; // Flying this.addAbility(FlyingAbility.getInstance()); // Disturb {4}{U} - this.addAbility(new TransformAbility()); - this.addAbility(new DisturbAbility(new ManaCostsImpl<>("{4}{U}"))); + this.addAbility(new DisturbAbility(this, "{4}{U}")); } private Galedrifter(final Galedrifter card) { diff --git a/Mage.Sets/src/mage/cards/g/GaleriderSliver.java b/Mage.Sets/src/mage/cards/g/GaleriderSliver.java index 9d6f32bbbfb..4992ac5c565 100644 --- a/Mage.Sets/src/mage/cards/g/GaleriderSliver.java +++ b/Mage.Sets/src/mage/cards/g/GaleriderSliver.java @@ -1,7 +1,5 @@ - package mage.cards.g; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; @@ -9,13 +7,13 @@ import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.SubType; import mage.filter.StaticFilters; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class GaleriderSliver extends CardImpl { @@ -28,9 +26,10 @@ public final class GaleriderSliver extends CardImpl { this.toughness = new MageInt(1); // Sliver creatures you control have flying. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, - new GainAbilityControlledEffect(FlyingAbility.getInstance(), - Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS))); + this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( + FlyingAbility.getInstance(), Duration.WhileOnBattlefield, + StaticFilters.FILTER_PERMANENT_SLIVERS + ))); } private GaleriderSliver(final GaleriderSliver card) { diff --git a/Mage.Sets/src/mage/cards/g/GamePreserve.java b/Mage.Sets/src/mage/cards/g/GamePreserve.java index cd4a840eaff..1ebc3cc2147 100644 --- a/Mage.Sets/src/mage/cards/g/GamePreserve.java +++ b/Mage.Sets/src/mage/cards/g/GamePreserve.java @@ -1,11 +1,8 @@ - package mage.cards.g; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.cards.Cards; @@ -14,11 +11,13 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.TargetController; import mage.constants.Zone; +import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class GamePreserve extends CardImpl { @@ -27,7 +26,9 @@ public final class GamePreserve extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}"); // At the beginning of your upkeep, each player reveals the top card of their library. If all cards revealed this way are creature cards, put those cards onto the battlefield under their owners' control. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(new DuskmarEffect(), TargetController.YOU, false)); + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new GamePreserveEffect(), TargetController.YOU, false + )); } private GamePreserve(final GamePreserve card) { @@ -40,47 +41,43 @@ public final class GamePreserve extends CardImpl { } } -class DuskmarEffect extends OneShotEffect { +class GamePreserveEffect extends OneShotEffect { - public DuskmarEffect() { + public GamePreserveEffect() { super(Outcome.Detriment); this.staticText = "each player reveals the top card of their library. If all cards revealed this way are creature cards, put those cards onto the battlefield under their owners' control"; } - public DuskmarEffect(final DuskmarEffect effect) { + public GamePreserveEffect(final GamePreserveEffect effect) { super(effect); } @Override - public DuskmarEffect copy() { - return new DuskmarEffect(this); + public GamePreserveEffect copy() { + return new GamePreserveEffect(this); } @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - boolean putToPlay = true; - Cards cards = new CardsImpl(); - for (Player player : game.getPlayers().values()) { - if (player.getLibrary().hasCards()) { - Card card = player.getLibrary().removeFromTop(game); - if (card != null) { - cards.add(card); - if (!card.isCreature(game)) { - putToPlay = false; - } - player.revealCards(source, "- Revealed by " + player.getName(), cards, game); - } - } else { - putToPlay = false; - } - } - if (putToPlay) { - controller.moveCards(cards.getCards(game), Zone.BATTLEFIELD, source, game, false, false, true, null); - } - return true; + if (controller == null) { + return false; } - return false; + Cards cards = new CardsImpl(); + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + if (player != null) { + cards.add(player.getLibrary().getFromTop(game)); + } + } + controller.revealCards(source, cards, game); + if (cards.isEmpty() || cards.count(StaticFilters.FILTER_CARD_NON_CREATURE, game) > 0) { + return false; + } + controller.moveCards( + cards.getCards(game), Zone.BATTLEFIELD, source, game, + false, false, true, null + ); + return true; } } diff --git a/Mage.Sets/src/mage/cards/g/GangOfDevils.java b/Mage.Sets/src/mage/cards/g/GangOfDevils.java index 6875b1acf6b..a782b65ce5c 100644 --- a/Mage.Sets/src/mage/cards/g/GangOfDevils.java +++ b/Mage.Sets/src/mage/cards/g/GangOfDevils.java @@ -1,4 +1,3 @@ - package mage.cards.g; import java.util.UUID; @@ -26,7 +25,7 @@ public final class GangOfDevils extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(3); - // When Gang of Devils dies, it deals 3 damage divided as you choose among one, two, or three target creatures and/or players. + // When Gang of Devils dies, it deals 3 damage divided as you choose among one, two, or three targets. Ability ability = new DiesSourceTriggeredAbility(new DamageMultiEffect(3, "it")); ability.addTarget(new TargetAnyTargetAmount(3)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/g/GargoyleSentinel.java b/Mage.Sets/src/mage/cards/g/GargoyleSentinel.java index 91acc1a4865..5698e3cf249 100644 --- a/Mage.Sets/src/mage/cards/g/GargoyleSentinel.java +++ b/Mage.Sets/src/mage/cards/g/GargoyleSentinel.java @@ -1,20 +1,20 @@ - - package mage.cards.g; 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.ContinuousEffectImpl; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.effects.common.continuous.LoseAbilitySourceEffect; import mage.abilities.keyword.DefenderAbility; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; /** * @@ -29,7 +29,15 @@ public final class GargoyleSentinel extends CardImpl { this.toughness = new MageInt(3); this.addAbility(DefenderAbility.getInstance()); - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new GargoyleSentinelEffect(), new ManaCostsImpl("{3}"))); + + // {3}: Until end of turn, Gargoyle Sentinel loses defender and gains flying. + Effect effect = new LoseAbilitySourceEffect(DefenderAbility.getInstance(), Duration.EndOfTurn); + effect.setText("until end of turn, {this} loses defender"); + Ability ability = new SimpleActivatedAbility(effect, new GenericManaCost(3)); + effect = new GainAbilitySourceEffect(FlyingAbility.getInstance(), Duration.EndOfTurn); + effect.setText("and gains flying"); + ability.addEffect(effect); + this.addAbility(ability); } private GargoyleSentinel(final GargoyleSentinel card) { @@ -40,50 +48,4 @@ public final class GargoyleSentinel extends CardImpl { public GargoyleSentinel copy() { return new GargoyleSentinel(this); } - -} - -class GargoyleSentinelEffect extends ContinuousEffectImpl { - - public GargoyleSentinelEffect() { - super(Duration.EndOfTurn, Outcome.AddAbility); - staticText = "Until end of turn, {this} loses defender and gains flying"; - } - - public GargoyleSentinelEffect(final GargoyleSentinelEffect effect) { - super(effect); - } - - @Override - public GargoyleSentinelEffect copy() { - return new GargoyleSentinelEffect(this); - } - - @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { - Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent != null) { - switch (layer) { - case AbilityAddingRemovingEffects_6: - if (sublayer == SubLayer.NA) { - permanent.removeAbility(DefenderAbility.getInstance(), source.getSourceId(), game); - permanent.getAbilities().add(FlyingAbility.getInstance()); - } - break; - } - return true; - } - return false; - } - - @Override - public boolean apply(Game game, Ability source) { - return false; - } - - @Override - public boolean hasLayer(Layer layer) { - return layer == Layer.AbilityAddingRemovingEffects_6; - } - } diff --git a/Mage.Sets/src/mage/cards/g/GarrukApexPredator.java b/Mage.Sets/src/mage/cards/g/GarrukApexPredator.java index dbdfb814733..d5972b7ea95 100644 --- a/Mage.Sets/src/mage/cards/g/GarrukApexPredator.java +++ b/Mage.Sets/src/mage/cards/g/GarrukApexPredator.java @@ -2,7 +2,6 @@ package mage.cards.g; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; @@ -44,7 +43,7 @@ public final class GarrukApexPredator extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.GARRUK); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +1: Destroy another target planeswalker. LoyaltyAbility ability = new LoyaltyAbility(new DestroyTargetEffect(), 1); diff --git a/Mage.Sets/src/mage/cards/g/GarrukCallerOfBeasts.java b/Mage.Sets/src/mage/cards/g/GarrukCallerOfBeasts.java index 85060e3b69c..1293041c4f5 100644 --- a/Mage.Sets/src/mage/cards/g/GarrukCallerOfBeasts.java +++ b/Mage.Sets/src/mage/cards/g/GarrukCallerOfBeasts.java @@ -4,7 +4,6 @@ package mage.cards.g; import java.util.UUID; import mage.ObjectColor; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.common.GetEmblemEffect; import mage.abilities.effects.common.PutCardFromHandOntoBattlefieldEffect; import mage.abilities.effects.common.RevealLibraryPutIntoHandEffect; @@ -35,7 +34,7 @@ public final class GarrukCallerOfBeasts extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.GARRUK); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Reveal the top 5 cards of your library. Put all creature 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(5, new FilterCreatureCard("creature cards"), Zone.LIBRARY), 1)); diff --git a/Mage.Sets/src/mage/cards/g/GarrukCursedHuntsman.java b/Mage.Sets/src/mage/cards/g/GarrukCursedHuntsman.java index fbd0483c5a0..e16b41871b3 100644 --- a/Mage.Sets/src/mage/cards/g/GarrukCursedHuntsman.java +++ b/Mage.Sets/src/mage/cards/g/GarrukCursedHuntsman.java @@ -2,7 +2,6 @@ package mage.cards.g; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; @@ -28,7 +27,7 @@ public final class GarrukCursedHuntsman extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.GARRUK); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // 0: Create two 2/2 black and green Wolf creature tokens with "When this creature dies, put a loyalty counter on each Garruk you control." this.addAbility(new LoyaltyAbility(new CreateTokenEffect(new GarrukCursedHuntsmanToken(), 2), 0)); diff --git a/Mage.Sets/src/mage/cards/g/GarrukPrimalHunter.java b/Mage.Sets/src/mage/cards/g/GarrukPrimalHunter.java index 5ce38d35bfb..c31694f980e 100644 --- a/Mage.Sets/src/mage/cards/g/GarrukPrimalHunter.java +++ b/Mage.Sets/src/mage/cards/g/GarrukPrimalHunter.java @@ -4,7 +4,6 @@ package mage.cards.g; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; @@ -36,7 +35,7 @@ public final class GarrukPrimalHunter extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.GARRUK); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +1: Create a 3/3 green Beast creature token. this.addAbility(new LoyaltyAbility(new CreateTokenEffect(new BeastToken()), 1)); diff --git a/Mage.Sets/src/mage/cards/g/GarrukRelentless.java b/Mage.Sets/src/mage/cards/g/GarrukRelentless.java index a6a35b52442..f81ee1874a0 100644 --- a/Mage.Sets/src/mage/cards/g/GarrukRelentless.java +++ b/Mage.Sets/src/mage/cards/g/GarrukRelentless.java @@ -4,7 +4,6 @@ package mage.cards.g; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.StateTriggeredAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.TransformSourceEffect; @@ -31,10 +30,9 @@ public final class GarrukRelentless extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.GARRUK); - this.transformable = true; this.secondSideCardClazz = GarrukTheVeilCursed.class; - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // When Garruk Relentless has two or fewer loyalty counters on him, transform him. this.addAbility(new TransformAbility()); @@ -63,7 +61,7 @@ public final class GarrukRelentless extends CardImpl { class GarrukRelentlessStateTrigger extends StateTriggeredAbility { public GarrukRelentlessStateTrigger() { - super(Zone.BATTLEFIELD, new TransformSourceEffect(true)); + super(Zone.BATTLEFIELD, new TransformSourceEffect()); } public GarrukRelentlessStateTrigger(final GarrukRelentlessStateTrigger ability) { diff --git a/Mage.Sets/src/mage/cards/g/GarrukSavageHerald.java b/Mage.Sets/src/mage/cards/g/GarrukSavageHerald.java index d668ae3abea..efa4203c131 100644 --- a/Mage.Sets/src/mage/cards/g/GarrukSavageHerald.java +++ b/Mage.Sets/src/mage/cards/g/GarrukSavageHerald.java @@ -3,7 +3,6 @@ package mage.cards.g; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.common.DamageAsThoughNotBlockedAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; @@ -39,7 +38,7 @@ public final class GarrukSavageHerald extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.GARRUK); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +1: Reveal the top card of your library. If it's a creature card, put it into your hand. Otherwise, put it on the bottom of your library. this.addAbility(new LoyaltyAbility(new GarrukSavageHeraldEffect(), 1)); diff --git a/Mage.Sets/src/mage/cards/g/GarrukTheVeilCursed.java b/Mage.Sets/src/mage/cards/g/GarrukTheVeilCursed.java index 65231c23e64..065638a1a87 100644 --- a/Mage.Sets/src/mage/cards/g/GarrukTheVeilCursed.java +++ b/Mage.Sets/src/mage/cards/g/GarrukTheVeilCursed.java @@ -38,7 +38,6 @@ public final class GarrukTheVeilCursed extends CardImpl { // this card is the second face of double-faced card this.nightCard = true; - this.transformable = true; this.color.setGreen(true); this.color.setBlack(true); diff --git a/Mage.Sets/src/mage/cards/g/GarrukUnleashed.java b/Mage.Sets/src/mage/cards/g/GarrukUnleashed.java index 44e56a1345d..6ec137afbc6 100644 --- a/Mage.Sets/src/mage/cards/g/GarrukUnleashed.java +++ b/Mage.Sets/src/mage/cards/g/GarrukUnleashed.java @@ -1,7 +1,6 @@ package mage.cards.g; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.condition.common.OpponentControlsMoreCondition; import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.Effect; @@ -36,7 +35,7 @@ public final class GarrukUnleashed extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.GARRUK); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Up to one target creature gets +3/+3 and gains trample until end of turn. Effect effect = new BoostTargetEffect(3, 3, Duration.EndOfTurn) diff --git a/Mage.Sets/src/mage/cards/g/GarrukWildspeaker.java b/Mage.Sets/src/mage/cards/g/GarrukWildspeaker.java index c6a719311f9..fae4c36f535 100644 --- a/Mage.Sets/src/mage/cards/g/GarrukWildspeaker.java +++ b/Mage.Sets/src/mage/cards/g/GarrukWildspeaker.java @@ -3,7 +3,6 @@ package mage.cards.g; import java.util.UUID; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.Effects; import mage.abilities.effects.common.CreateTokenEffect; @@ -34,7 +33,7 @@ public final class GarrukWildspeaker extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.GARRUK); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +1: Untap two target lands. LoyaltyAbility ability1 = new LoyaltyAbility(new UntapTargetEffect(), 1); diff --git a/Mage.Sets/src/mage/cards/g/GarzasAssassin.java b/Mage.Sets/src/mage/cards/g/GarzasAssassin.java index b837f02437c..7ca2c49e77b 100644 --- a/Mage.Sets/src/mage/cards/g/GarzasAssassin.java +++ b/Mage.Sets/src/mage/cards/g/GarzasAssassin.java @@ -3,7 +3,6 @@ package mage.cards.g; import java.util.UUID; import mage.MageInt; -import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.Cost; @@ -16,9 +15,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ColorPredicate; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent; import mage.players.Player; @@ -31,12 +28,6 @@ import mage.util.CardUtil; */ public final class GarzasAssassin extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public GarzasAssassin(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{B}{B}{B}"); this.subtype.add(SubType.HUMAN); @@ -46,7 +37,7 @@ public final class GarzasAssassin extends CardImpl { // Sacrifice Garza's Assassin: Destroy target nonblack creature. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new SacrificeSourceCost()); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); this.addAbility(ability); // Recover—Pay half your life, rounded up. diff --git a/Mage.Sets/src/mage/cards/g/GatstafArsonists.java b/Mage.Sets/src/mage/cards/g/GatstafArsonists.java index d71ff6bac96..198d47e1d7b 100644 --- a/Mage.Sets/src/mage/cards/g/GatstafArsonists.java +++ b/Mage.Sets/src/mage/cards/g/GatstafArsonists.java @@ -2,18 +2,12 @@ package mage.cards.g; import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.WerewolfFrontTriggeredAbility; -import mage.abilities.condition.common.NoSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.TransformSourceEffect; import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; /** * @@ -28,7 +22,6 @@ public final class GatstafArsonists extends CardImpl { this.power = new MageInt(5); this.toughness = new MageInt(4); - this.transformable = true; this.secondSideCardClazz = GatstafRavagers.class; // At the beginning of each upkeep, if no spells were cast last turn, transform Gatstaf Arsonists. diff --git a/Mage.Sets/src/mage/cards/g/GatstafHowler.java b/Mage.Sets/src/mage/cards/g/GatstafHowler.java index df0faacf87c..3d98791e211 100644 --- a/Mage.Sets/src/mage/cards/g/GatstafHowler.java +++ b/Mage.Sets/src/mage/cards/g/GatstafHowler.java @@ -22,7 +22,6 @@ public final class GatstafHowler extends CardImpl { // this card is the second face of double-faced card this.nightCard = true; - this.transformable = true; this.power = new MageInt(3); this.toughness = new MageInt(3); diff --git a/Mage.Sets/src/mage/cards/g/GatstafRavagers.java b/Mage.Sets/src/mage/cards/g/GatstafRavagers.java index ce353bf6473..fd2463f180d 100644 --- a/Mage.Sets/src/mage/cards/g/GatstafRavagers.java +++ b/Mage.Sets/src/mage/cards/g/GatstafRavagers.java @@ -23,7 +23,6 @@ public final class GatstafRavagers extends CardImpl { this.color.setRed(true); - this.transformable = true; this.nightCard = true; // Menace diff --git a/Mage.Sets/src/mage/cards/g/GatstafShepherd.java b/Mage.Sets/src/mage/cards/g/GatstafShepherd.java index 1e49e181c0f..47d1bb9d281 100644 --- a/Mage.Sets/src/mage/cards/g/GatstafShepherd.java +++ b/Mage.Sets/src/mage/cards/g/GatstafShepherd.java @@ -20,7 +20,6 @@ public final class GatstafShepherd extends CardImpl { this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.WEREWOLF); - this.transformable = true; this.secondSideCardClazz = GatstafHowler.class; this.power = new MageInt(2); diff --git a/Mage.Sets/src/mage/cards/g/GeierReachBandit.java b/Mage.Sets/src/mage/cards/g/GeierReachBandit.java index 9fde0db14a4..004e24bd542 100644 --- a/Mage.Sets/src/mage/cards/g/GeierReachBandit.java +++ b/Mage.Sets/src/mage/cards/g/GeierReachBandit.java @@ -24,7 +24,6 @@ public final class GeierReachBandit extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(2); - this.transformable = true; this.secondSideCardClazz = mage.cards.v.VildinPackAlpha.class; // Haste diff --git a/Mage.Sets/src/mage/cards/g/GeistOfSaintTraft.java b/Mage.Sets/src/mage/cards/g/GeistOfSaintTraft.java index f05a55c3d1e..9deea9bf8b0 100644 --- a/Mage.Sets/src/mage/cards/g/GeistOfSaintTraft.java +++ b/Mage.Sets/src/mage/cards/g/GeistOfSaintTraft.java @@ -1,4 +1,3 @@ - package mage.cards.g; import java.util.UUID; @@ -34,6 +33,7 @@ public final class GeistOfSaintTraft extends CardImpl { // Hexproof this.addAbility(HexproofAbility.getInstance()); + // Whenever Geist of Saint Traft attacks, create a 4/4 white Angel creature token with flying tapped and attacking. Exile that token at end of combat. this.addAbility(new AttacksTriggeredAbility(new GeistOfSaintTraftEffect(), false)); } @@ -52,7 +52,7 @@ class GeistOfSaintTraftEffect extends OneShotEffect { GeistOfSaintTraftEffect() { super(Outcome.PutCreatureInPlay); - staticText = "create a 4/4 white Angel creature token with flying tapped and attacking. Exile that token at end of combat"; + staticText = "create a 4/4 white Angel creature token with flying that's tapped and attacking. Exile that token at end of combat"; } GeistOfSaintTraftEffect(final GeistOfSaintTraftEffect effect) { diff --git a/Mage.Sets/src/mage/cards/g/GeistlightSnare.java b/Mage.Sets/src/mage/cards/g/GeistlightSnare.java new file mode 100644 index 00000000000..db17414ca14 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GeistlightSnare.java @@ -0,0 +1,61 @@ +package mage.cards.g; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.CounterUnlessPaysEffect; +import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +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.filter.common.FilterControlledPermanent; +import mage.target.TargetSpell; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GeistlightSnare extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.SPIRIT); + private static final Condition condition1 = new PermanentsOnTheBattlefieldCondition(filter); + private static final Condition condition2 = new PermanentsOnTheBattlefieldCondition(StaticFilters.FILTER_PERMANENT_ENCHANTMENT); + private static final Hint hint1 = new ConditionHint(condition1, "You control a Spirit"); + private static final Hint hint2 = new ConditionHint(condition2, "You control an enchantment"); + + public GeistlightSnare(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{U}"); + + // This spell costs {1} less to cast if you control a Spirit. It also costs {1} less to cast if you control an enchantment. + Ability ability = new SimpleStaticAbility( + Zone.ALL, new SpellCostReductionSourceEffect(1, condition1).setCanWorksOnStackOnly(true) + .setText("this spell costs {1} less to cast if you control a Spirit") + ).setRuleAtTheTop(true); + ability.addEffect(new SpellCostReductionSourceEffect(1, condition2).setCanWorksOnStackOnly(true) + .setText("It also costs {1} less to cast if you control an enchantment")); + + // Counter target spell unless its controller pays {3}. + this.getSpellAbility().addEffect(new CounterUnlessPaysEffect(new GenericManaCost(3))); + this.getSpellAbility().addTarget(new TargetSpell()); + this.getSpellAbility().addHint(hint1); + this.getSpellAbility().addHint(hint2); + } + + private GeistlightSnare(final GeistlightSnare card) { + super(card); + } + + @Override + public GeistlightSnare copy() { + return new GeistlightSnare(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GenerousSoul.java b/Mage.Sets/src/mage/cards/g/GenerousSoul.java new file mode 100644 index 00000000000..d8516e9c39a --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GenerousSoul.java @@ -0,0 +1,43 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.common.PutIntoGraveFromAnywhereSourceAbility; +import mage.abilities.effects.common.ExileSourceEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +public final class GenerousSoul extends CardImpl { + + public GenerousSoul(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); + + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + this.color.setWhite(true); + + // This is the back half of Beloved Beggar + this.nightCard = true; + + this.addAbility(FlyingAbility.getInstance()); + this.addAbility(VigilanceAbility.getInstance()); + + // If Generous Soul would be put into a graveyard from anywhere, exile it instead. + this.addAbility(new PutIntoGraveFromAnywhereSourceAbility(new ExileSourceEffect().setText("exile it instead"))); + } + + private GenerousSoul(final GenerousSoul card) { + super(card); + } + + @Override + public GenerousSoul copy() { + return new GenerousSoul(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GenerousVisitor.java b/Mage.Sets/src/mage/cards/g/GenerousVisitor.java new file mode 100644 index 00000000000..678b5e59b5f --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GenerousVisitor.java @@ -0,0 +1,51 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.FilterSpell; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GenerousVisitor extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("an enchantment spell"); + + static { + filter.add(CardType.ENCHANTMENT.getPredicate()); + } + + public GenerousVisitor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}"); + + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Whenever you cast an enchantment spell, put a +1/+1 counter on target creature. + Ability ability = new SpellCastControllerTriggeredAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance()), filter, false + ); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + private GenerousVisitor(final GenerousVisitor card) { + super(card); + } + + @Override + public GenerousVisitor copy() { + return new GenerousVisitor(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GeodeRager.java b/Mage.Sets/src/mage/cards/g/GeodeRager.java index 049b0f0fdcb..f88c93825e0 100644 --- a/Mage.Sets/src/mage/cards/g/GeodeRager.java +++ b/Mage.Sets/src/mage/cards/g/GeodeRager.java @@ -74,7 +74,7 @@ class GeodeRagerEffect extends OneShotEffect { if (permanent == null) { continue; } - new GoadTargetEffect().setTargetPointer(new FixedTarget(permanent, game)).apply(game, source); + game.addEffect(new GoadTargetEffect().setTargetPointer(new FixedTarget(permanent, game)), source); } return true; } diff --git a/Mage.Sets/src/mage/cards/g/GeothermalKami.java b/Mage.Sets/src/mage/cards/g/GeothermalKami.java new file mode 100644 index 00000000000..dad402694f8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GeothermalKami.java @@ -0,0 +1,47 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.costs.common.ReturnToHandChosenControlledPermanentCost; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.GainLifeEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.common.FilterControlledEnchantmentPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GeothermalKami extends CardImpl { + + private static final FilterControlledPermanent filter + = new FilterControlledEnchantmentPermanent("an enchantment you control"); + + public GeothermalKami(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}"); + + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + + // When Geothermal Kami enters the battlfield, you may return an enchantment you control to its owner's hand. If you do, you gain 3 life. + this.addAbility(new EntersBattlefieldTriggeredAbility(new DoIfCostPaid( + new GainLifeEffect(3), new ReturnToHandChosenControlledPermanentCost(new TargetControlledPermanent(filter)) + ))); + } + + private GeothermalKami(final GeothermalKami card) { + super(card); + } + + @Override + public GeothermalKami copy() { + return new GeothermalKami(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GeralfVisionaryStitcher.java b/Mage.Sets/src/mage/cards/g/GeralfVisionaryStitcher.java new file mode 100644 index 00000000000..4a43896b3f7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GeralfVisionaryStitcher.java @@ -0,0 +1,98 @@ +package mage.cards.g; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.constants.*; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.StitcherGeralfZombieToken; + +/** + * + * @author weirddan455 + */ +public final class GeralfVisionaryStitcher extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent(SubType.ZOMBIE, "Zombies"); + private static final FilterControlledCreaturePermanent filter2 + = new FilterControlledCreaturePermanent("another nontoken creature"); + + static { + filter2.add(AnotherPredicate.instance); + filter2.add(TokenPredicate.FALSE); + } + + public GeralfVisionaryStitcher(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(1); + this.toughness = new MageInt(4); + + // Zombies you control have flying. + this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect(FlyingAbility.getInstance(), Duration.WhileOnBattlefield, filter))); + + // {U}, {T}, Sacrifice another nontoken creature: Create an X/X blue Zombie token, where X is the sacrificed creature's toughness. + Ability ability = new SimpleActivatedAbility(new GeralfVisionaryStitcherEffect(), new ManaCostsImpl<>("{U}")); + ability.addCost(new TapSourceCost()); + ability.addCost(new SacrificeTargetCost(filter2)); + this.addAbility(ability); + } + + private GeralfVisionaryStitcher(final GeralfVisionaryStitcher card) { + super(card); + } + + @Override + public GeralfVisionaryStitcher copy() { + return new GeralfVisionaryStitcher(this); + } +} + +class GeralfVisionaryStitcherEffect extends OneShotEffect { + + public GeralfVisionaryStitcherEffect() { + super(Outcome.PutCreatureInPlay); + staticText = "Create an X/X blue Zombie creature token, where X is the sacrificed creature's toughness"; + } + + private GeralfVisionaryStitcherEffect(final GeralfVisionaryStitcherEffect effect) { + super(effect); + } + + @Override + public GeralfVisionaryStitcherEffect copy() { + return new GeralfVisionaryStitcherEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + int xValue = 0; + for (Cost cost : source.getCosts()) { + if (cost instanceof SacrificeTargetCost) { + for (Permanent permanent : ((SacrificeTargetCost) cost).getPermanents()) { + xValue += permanent.getToughness().getValue(); + } + } + } + return new StitcherGeralfZombieToken(xValue).putOntoBattlefield(1, game, source, source.getControllerId()); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GetThePoint.java b/Mage.Sets/src/mage/cards/g/GetThePoint.java index 1e618822375..eaaf9e751d3 100644 --- a/Mage.Sets/src/mage/cards/g/GetThePoint.java +++ b/Mage.Sets/src/mage/cards/g/GetThePoint.java @@ -19,7 +19,7 @@ public final class GetThePoint extends CardImpl { // Destroy target creature. Scry 1. this.getSpellAbility().addEffect(new DestroyTargetEffect()); - this.getSpellAbility().addEffect(new ScryEffect(1)); + this.getSpellAbility().addEffect(new ScryEffect(1, false)); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } diff --git a/Mage.Sets/src/mage/cards/g/GeyadroneDihada.java b/Mage.Sets/src/mage/cards/g/GeyadroneDihada.java index 67bc26f82ce..41a627a630c 100644 --- a/Mage.Sets/src/mage/cards/g/GeyadroneDihada.java +++ b/Mage.Sets/src/mage/cards/g/GeyadroneDihada.java @@ -4,7 +4,6 @@ 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.LoseLifeOpponentsEffect; import mage.abilities.effects.common.UntapTargetEffect; @@ -47,7 +46,7 @@ public final class GeyadroneDihada extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.DIHADA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // Protection from permanents with corruption counters on them this.addAbility(new ProtectionAbility(filter)); diff --git a/Mage.Sets/src/mage/cards/g/GhastlyDemise.java b/Mage.Sets/src/mage/cards/g/GhastlyDemise.java index 77ee7cd3ab9..325d906c4cc 100644 --- a/Mage.Sets/src/mage/cards/g/GhastlyDemise.java +++ b/Mage.Sets/src/mage/cards/g/GhastlyDemise.java @@ -1,41 +1,30 @@ - package mage.cards.g; -import java.util.UUID; -import mage.ObjectColor; 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.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ColorPredicate; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.target.Target; +import mage.players.Player; import mage.target.common.TargetCreaturePermanent; -import mage.target.targetpointer.FirstTargetPointer; + +import java.util.UUID; /** - * * @author cbt33 */ public final class GhastlyDemise extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature if its tougness is less than the number of cards in your graveyard"); - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public GhastlyDemise(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{B}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{B}"); // Destroy target nonblack creature if its toughness is less than or equal to the number of cards in your graveyard. - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); - this.getSpellAbility().addEffect(new GhastlyDemiseEffect(false)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); + this.getSpellAbility().addEffect(new GhastlyDemiseEffect()); } private GhastlyDemise(final GhastlyDemise card) { @@ -50,17 +39,14 @@ public final class GhastlyDemise extends CardImpl { class GhastlyDemiseEffect extends OneShotEffect { - protected boolean noRegen; - - public GhastlyDemiseEffect(boolean noRegen) { + GhastlyDemiseEffect() { super(Outcome.DestroyPermanent); - staticText = "Destroy target nonblack creature if its toughness is less than or equal to the number of cards in your graveyard"; - this.noRegen = noRegen; + staticText = "destroy target nonblack creature if its toughness " + + "is less than or equal to the number of cards in your graveyard"; } - public GhastlyDemiseEffect(final GhastlyDemiseEffect effect) { + private GhastlyDemiseEffect(final GhastlyDemiseEffect effect) { super(effect); - this.noRegen = effect.noRegen; } @Override @@ -70,26 +56,10 @@ class GhastlyDemiseEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - int affectedTargets = 0; - if (source.getTargets().size() > 1 && this.targetPointer instanceof FirstTargetPointer) { // for Rain of Thorns - for (Target target : source.getTargets()) { - for (UUID permanentId : target.getTargets()) { - Permanent permanent = game.getPermanent(permanentId); - if (permanent != null && permanent.getToughness().getValue() <= game.getPlayer(source.getControllerId()).getGraveyard().size()) { - permanent.destroy(source, game, noRegen); - affectedTargets++; - } - } - } - } else if (this.targetPointer != null && !this.targetPointer.getTargets(game, source).isEmpty()) { - for (UUID permanentId : this.targetPointer.getTargets(game, source)) { - Permanent permanent = game.getPermanent(permanentId); - if (permanent != null && permanent.getToughness().getValue() <= game.getPlayer(source.getControllerId()).getGraveyard().size()) { - permanent.destroy(source, game, noRegen); - affectedTargets++; - } - } - } - return affectedTargets > 0; + Permanent permanent = game.getPermanent(source.getFirstTarget()); + Player player = game.getPlayer(source.getControllerId()); + return permanent != null && player != null + && permanent.getToughness().getValue() <= player.getGraveyard().size() + && permanent.destroy(source, game); } } diff --git a/Mage.Sets/src/mage/cards/g/GhastlyDiscovery.java b/Mage.Sets/src/mage/cards/g/GhastlyDiscovery.java index 6243e09fe49..aed7f17c206 100644 --- a/Mage.Sets/src/mage/cards/g/GhastlyDiscovery.java +++ b/Mage.Sets/src/mage/cards/g/GhastlyDiscovery.java @@ -25,7 +25,7 @@ public final class GhastlyDiscovery extends CardImpl { this.getSpellAbility().addEffect(new GhastlyDiscoveryEffect()); // Conspire - this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.NONE)); + this.addAbility(new ConspireAbility(ConspireAbility.ConspireTargets.NONE)); } private GhastlyDiscovery(final GhastlyDiscovery card) { diff --git a/Mage.Sets/src/mage/cards/g/GhastlyHaunting.java b/Mage.Sets/src/mage/cards/g/GhastlyHaunting.java index 1dd17b39100..d8165558962 100644 --- a/Mage.Sets/src/mage/cards/g/GhastlyHaunting.java +++ b/Mage.Sets/src/mage/cards/g/GhastlyHaunting.java @@ -4,6 +4,7 @@ package mage.cards.g; import java.util.UUID; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.continuous.ControlEnchantedEffect; +import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -25,11 +26,11 @@ public final class GhastlyHaunting extends CardImpl { // this card is the second face of double-faced card this.nightCard = true; - this.transformable = true; // Enchant creature TargetPermanent auraTarget = new TargetCreaturePermanent(); this.getSpellAbility().addTarget(auraTarget); + this.addAbility(new EnchantAbility(auraTarget.getTargetName())); // You control enchanted creature. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ControlEnchantedEffect())); diff --git a/Mage.Sets/src/mage/cards/g/GhastlyMimicry.java b/Mage.Sets/src/mage/cards/g/GhastlyMimicry.java new file mode 100644 index 00000000000..e250267c673 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GhastlyMimicry.java @@ -0,0 +1,94 @@ +package mage.cards.g; + +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.PutIntoGraveFromAnywhereSourceAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.CreateTokenCopyTargetEffect; +import mage.abilities.effects.common.ExileSourceEffect; +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.constants.TargetController; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GhastlyMimicry extends CardImpl { + + public GhastlyMimicry(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, ""); + + this.subtype.add(SubType.AURA); + this.color.setBlue(true); + this.nightCard = true; + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // At the beginning of your upkeep, create a token that's a copy of enchanted creature, except it's a Spirit in addition to its other types. + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new GhastlyMimicryEffect(), TargetController.YOU, false + )); + + // If Ghastly Mimicry would be put into a graveyard from anywhere, exile it instead. + this.addAbility(new PutIntoGraveFromAnywhereSourceAbility(new ExileSourceEffect().setText("exile it instead"))); + } + + private GhastlyMimicry(final GhastlyMimicry card) { + super(card); + } + + @Override + public GhastlyMimicry copy() { + return new GhastlyMimicry(this); + } +} + +class GhastlyMimicryEffect extends OneShotEffect { + + GhastlyMimicryEffect() { + super(Outcome.Benefit); + staticText = "create a token that's a copy of enchanted creature, " + + "except it's a Spirit in addition to its other types"; + } + + private GhastlyMimicryEffect(final GhastlyMimicryEffect effect) { + super(effect); + } + + @Override + public GhastlyMimicryEffect copy() { + return new GhastlyMimicryEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = source.getSourcePermanentOrLKI(game); + if (permanent == null) { + return false; + } + Permanent attached = game.getPermanent(permanent.getAttachedTo()); + if (attached == null) { + return false; + } + CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(); + effect.setAdditionalSubType(SubType.SPIRIT); + return effect.setTargetPointer(new FixedTarget(attached, game)).apply(game, source); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GhirapurGuide.java b/Mage.Sets/src/mage/cards/g/GhirapurGuide.java index c6c7a27c841..b3f84186494 100644 --- a/Mage.Sets/src/mage/cards/g/GhirapurGuide.java +++ b/Mage.Sets/src/mage/cards/g/GhirapurGuide.java @@ -1,7 +1,5 @@ - package mage.cards.g; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -10,16 +8,16 @@ import mage.abilities.effects.common.combat.CantBeBlockedByAllTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.ComparisonType; import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.SubType; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.PowerPredicate; -import mage.target.common.TargetCreaturePermanent; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; /** - * * @author fireshoes */ public final class GhirapurGuide extends CardImpl { @@ -31,15 +29,17 @@ public final class GhirapurGuide extends CardImpl { } public GhirapurGuide(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); this.subtype.add(SubType.ELF); this.subtype.add(SubType.SCOUT); this.power = new MageInt(3); this.toughness = new MageInt(2); // {2}{G}: Target creature you control can't be blocked by creatures with power 2 or less this turn. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CantBeBlockedByAllTargetEffect(filter, Duration.EndOfTurn), new ManaCostsImpl("{2}{G}")); - ability.addTarget(new TargetCreaturePermanent()); + Ability ability = new SimpleActivatedAbility( + new CantBeBlockedByAllTargetEffect(filter, Duration.EndOfTurn), new ManaCostsImpl<>("{2}{G}") + ); + ability.addTarget(new TargetControlledCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/g/GhosthelmCourier.java b/Mage.Sets/src/mage/cards/g/GhosthelmCourier.java index 3766b67cbbb..21afeb69b5b 100644 --- a/Mage.Sets/src/mage/cards/g/GhosthelmCourier.java +++ b/Mage.Sets/src/mage/cards/g/GhosthelmCourier.java @@ -45,10 +45,10 @@ public final class GhosthelmCourier extends CardImpl { this.addAbility(new SkipUntapOptionalAbility()); // {2}{U}, {tap}: Target Wizard creature gets +2/+2 and has shroud for as long as Ghosthelm Courier remains tapped. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect( - new BoostTargetEffect(2, 2, Duration.Custom), SourceTappedCondition.instance, + new BoostTargetEffect(2, 2, Duration.Custom), SourceTappedCondition.TAPPED, "target Wizard creature gets +2/+2"), new ManaCostsImpl("{2}{U}")); ability.addEffect(new ConditionalContinuousEffect(new GainAbilityTargetEffect(ShroudAbility.getInstance(), - Duration.Custom), SourceTappedCondition.instance,"and has shroud for as long as {this} remains tapped")); + Duration.Custom), SourceTappedCondition.TAPPED,"and has shroud for as long as {this} remains tapped")); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent(filter)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/g/GhostlyCastigator.java b/Mage.Sets/src/mage/cards/g/GhostlyCastigator.java index 35833d52144..f18d5299621 100644 --- a/Mage.Sets/src/mage/cards/g/GhostlyCastigator.java +++ b/Mage.Sets/src/mage/cards/g/GhostlyCastigator.java @@ -27,7 +27,6 @@ public final class GhostlyCastigator extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(4); this.color.setBlue(true); - this.transformable = true; this.nightCard = true; // Flying diff --git a/Mage.Sets/src/mage/cards/g/GhostlyVisit.java b/Mage.Sets/src/mage/cards/g/GhostlyVisit.java index 205152948ed..cfb939434aa 100644 --- a/Mage.Sets/src/mage/cards/g/GhostlyVisit.java +++ b/Mage.Sets/src/mage/cards/g/GhostlyVisit.java @@ -1,15 +1,11 @@ - package mage.cards.g; import java.util.UUID; -import mage.ObjectColor; import mage.abilities.effects.common.DestroyTargetEffect; 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; /** @@ -18,18 +14,12 @@ import mage.target.common.TargetCreaturePermanent; */ public final class GhostlyVisit extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public GhostlyVisit(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{B}"); // Destroy target nonblack creature. this.getSpellAbility().addEffect(new DestroyTargetEffect()); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); } private GhostlyVisit(final GhostlyVisit card) { diff --git a/Mage.Sets/src/mage/cards/g/Ghoulraiser.java b/Mage.Sets/src/mage/cards/g/Ghoulraiser.java index b70c6b27b9f..84ea5154629 100644 --- a/Mage.Sets/src/mage/cards/g/Ghoulraiser.java +++ b/Mage.Sets/src/mage/cards/g/Ghoulraiser.java @@ -75,7 +75,7 @@ class GhoulraiserEffect extends OneShotEffect { TargetCard target = new TargetCardInYourGraveyard(filter); target.setNotTarget(true); target.setRandom(true); - player.chooseTarget(outcome, target, source, game); + target.chooseTarget(outcome, player.getId(), source, game); return player.moveCards(game.getCard(target.getFirstTarget()), Zone.HAND, source, game); } } diff --git a/Mage.Sets/src/mage/cards/g/GiantTortoise.java b/Mage.Sets/src/mage/cards/g/GiantTortoise.java index 2b5fd197afa..4fc00c04bd2 100644 --- a/Mage.Sets/src/mage/cards/g/GiantTortoise.java +++ b/Mage.Sets/src/mage/cards/g/GiantTortoise.java @@ -31,7 +31,7 @@ public final class GiantTortoise extends CardImpl { // Giant Tortoise gets +0/+3 as long as it's untapped. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect( new BoostSourceEffect(0,3, Duration.WhileOnBattlefield), - new InvertCondition(SourceTappedCondition.instance), + SourceTappedCondition.UNTAPPED, "{this} gets +0/+3 as long as it's untapped"))); } diff --git a/Mage.Sets/src/mage/cards/g/Giantbaiting.java b/Mage.Sets/src/mage/cards/g/Giantbaiting.java index 4f688b34145..112ce1272ee 100644 --- a/Mage.Sets/src/mage/cards/g/Giantbaiting.java +++ b/Mage.Sets/src/mage/cards/g/Giantbaiting.java @@ -26,7 +26,7 @@ public final class Giantbaiting extends CardImpl { this.getSpellAbility().addEffect(new GiantbaitingEffect()); // Conspire - this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.NONE)); + this.addAbility(new ConspireAbility(ConspireAbility.ConspireTargets.NONE)); } diff --git a/Mage.Sets/src/mage/cards/g/GiantsAmulet.java b/Mage.Sets/src/mage/cards/g/GiantsAmulet.java index 88ce7c51bfe..a8d37a61b55 100644 --- a/Mage.Sets/src/mage/cards/g/GiantsAmulet.java +++ b/Mage.Sets/src/mage/cards/g/GiantsAmulet.java @@ -44,7 +44,7 @@ public final class GiantsAmulet extends CardImpl { new GainAbilitySourceEffect( HexproofAbility.getInstance(), Duration.WhileOnBattlefield - ), new InvertCondition(SourceTappedCondition.instance), + ), SourceTappedCondition.UNTAPPED, "{this} has hexproof as long as it's untapped" )), AttachmentType.EQUIPMENT ).setText("and has \"This creature has hexproof as long as it's untapped.\"")); diff --git a/Mage.Sets/src/mage/cards/g/GideonAllyOfZendikar.java b/Mage.Sets/src/mage/cards/g/GideonAllyOfZendikar.java index df069125b58..d58a6d72caf 100644 --- a/Mage.Sets/src/mage/cards/g/GideonAllyOfZendikar.java +++ b/Mage.Sets/src/mage/cards/g/GideonAllyOfZendikar.java @@ -4,7 +4,6 @@ package mage.cards.g; import java.util.UUID; import mage.MageInt; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.GetEmblemEffect; @@ -32,7 +31,7 @@ public final class GideonAllyOfZendikar extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.GIDEON); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Until end of turn, Gideon, Ally of Zendikar becomes a 5/5 Human Soldier Ally creature with indestructible that's still a planeswalker. Prevent all damage that would be dealt to him this turn. LoyaltyAbility ability = new LoyaltyAbility(new BecomesCreatureSourceEffect(new GideonAllyOfZendikarToken(), "planeswalker", Duration.EndOfTurn), 1); diff --git a/Mage.Sets/src/mage/cards/g/GideonBattleForged.java b/Mage.Sets/src/mage/cards/g/GideonBattleForged.java index 740e82a5b4f..67ddfbe2f58 100644 --- a/Mage.Sets/src/mage/cards/g/GideonBattleForged.java +++ b/Mage.Sets/src/mage/cards/g/GideonBattleForged.java @@ -4,7 +4,6 @@ import mage.MageInt; import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.RequirementEffect; import mage.abilities.effects.common.PreventAllDamageToSourceEffect; @@ -42,9 +41,8 @@ public final class GideonBattleForged extends CardImpl { this.color.setWhite(true); this.nightCard = true; - this.transformable = true; - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +2: Up to one target creature an opponent controls attacks Gideon, Battle-Forged during its controller's next turn if able. LoyaltyAbility loyaltyAbility = new LoyaltyAbility(new GideonBattleForgedAttacksIfAbleTargetEffect(Duration.Custom), 2); diff --git a/Mage.Sets/src/mage/cards/g/GideonBlackblade.java b/Mage.Sets/src/mage/cards/g/GideonBlackblade.java index 00e7ec80c04..9e8e88aa423 100644 --- a/Mage.Sets/src/mage/cards/g/GideonBlackblade.java +++ b/Mage.Sets/src/mage/cards/g/GideonBlackblade.java @@ -3,7 +3,6 @@ package mage.cards.g; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.MyTurnCondition; import mage.abilities.decorator.ConditionalContinuousEffect; @@ -52,7 +51,7 @@ public final class GideonBlackblade extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.GIDEON); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // As long as it's your turn, Gideon Blackblade is a 4/4 Human Soldier creature with indestructible that's still a planeswalker. this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( diff --git a/Mage.Sets/src/mage/cards/g/GideonChampionOfJustice.java b/Mage.Sets/src/mage/cards/g/GideonChampionOfJustice.java index 93e3ba6cf0a..99270065921 100644 --- a/Mage.Sets/src/mage/cards/g/GideonChampionOfJustice.java +++ b/Mage.Sets/src/mage/cards/g/GideonChampionOfJustice.java @@ -4,7 +4,6 @@ import mage.MageInt; import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.dynamicvalue.LockedInDynamicValue; import mage.abilities.dynamicvalue.common.CountersSourceCount; import mage.abilities.dynamicvalue.common.PermanentsTargetOpponentControlsCount; @@ -39,7 +38,7 @@ public final class GideonChampionOfJustice extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.GIDEON); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Put a loyalty counter on Gideon, Champion of Justice for each creature target opponent controls. LoyaltyAbility ability1 = new LoyaltyAbility( diff --git a/Mage.Sets/src/mage/cards/g/GideonJura.java b/Mage.Sets/src/mage/cards/g/GideonJura.java index 335e547b331..6da6c4e7efb 100644 --- a/Mage.Sets/src/mage/cards/g/GideonJura.java +++ b/Mage.Sets/src/mage/cards/g/GideonJura.java @@ -4,7 +4,6 @@ import mage.MageInt; import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.RequirementEffect; import mage.abilities.effects.common.DestroyTargetEffect; @@ -39,7 +38,7 @@ public final class GideonJura extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.GIDEON); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(6)); + this.setStartingLoyalty(6); // +2: During target opponent's next turn, creatures that player controls attack Gideon Jura if able. LoyaltyAbility ability1 = new LoyaltyAbility(new GideonJuraEffect(), 2); diff --git a/Mage.Sets/src/mage/cards/g/GideonMartialParagon.java b/Mage.Sets/src/mage/cards/g/GideonMartialParagon.java index 4f500d35a03..92d5e3c294f 100644 --- a/Mage.Sets/src/mage/cards/g/GideonMartialParagon.java +++ b/Mage.Sets/src/mage/cards/g/GideonMartialParagon.java @@ -4,7 +4,6 @@ package mage.cards.g; import java.util.UUID; import mage.MageInt; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.PreventAllDamageToSourceEffect; import mage.abilities.effects.common.TapAllEffect; @@ -34,7 +33,7 @@ public final class GideonMartialParagon extends CardImpl { this.subtype.add(SubType.GIDEON); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +2: Untap all creatures you control. Those creatures get +1/+1 until end of turn. LoyaltyAbility ability = new LoyaltyAbility(new UntapAllEffect(new FilterControlledCreaturePermanent()), 2); diff --git a/Mage.Sets/src/mage/cards/g/GideonOfTheTrials.java b/Mage.Sets/src/mage/cards/g/GideonOfTheTrials.java index eb3a1608152..594df5eab77 100644 --- a/Mage.Sets/src/mage/cards/g/GideonOfTheTrials.java +++ b/Mage.Sets/src/mage/cards/g/GideonOfTheTrials.java @@ -4,7 +4,6 @@ package mage.cards.g; import java.util.UUID; import mage.MageInt; 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.PreventAllDamageToSourceEffect; @@ -33,7 +32,7 @@ public final class GideonOfTheTrials extends CardImpl { this.subtype.add(SubType.GIDEON); //Starting Loyalty: 3 - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +1: Until your next turn, prevent all damage target permanent would deal. Effect effect = new PreventDamageByTargetEffect(Duration.UntilYourNextTurn); diff --git a/Mage.Sets/src/mage/cards/g/GideonTheOathsworn.java b/Mage.Sets/src/mage/cards/g/GideonTheOathsworn.java index e110f6619a8..463ae961d0c 100644 --- a/Mage.Sets/src/mage/cards/g/GideonTheOathsworn.java +++ b/Mage.Sets/src/mage/cards/g/GideonTheOathsworn.java @@ -5,7 +5,6 @@ import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ExileAllEffect; import mage.abilities.effects.common.ExileSourceEffect; @@ -35,7 +34,7 @@ public final class GideonTheOathsworn extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.GIDEON); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // Whenever you attack with two or more non-Gideon creatures, put a +1/+1 counter on each of those creatures. this.addAbility(new GideonTheOathswornTriggeredAbility()); diff --git a/Mage.Sets/src/mage/cards/g/GideonsAvenger.java b/Mage.Sets/src/mage/cards/g/GideonsAvenger.java index ea3580e9f7c..f49a6223e2b 100644 --- a/Mage.Sets/src/mage/cards/g/GideonsAvenger.java +++ b/Mage.Sets/src/mage/cards/g/GideonsAvenger.java @@ -9,9 +9,8 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; import mage.counters.CounterType; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; /** * @@ -19,12 +18,6 @@ import mage.filter.common.FilterCreaturePermanent; */ public final class GideonsAvenger extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public GideonsAvenger(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{W}{W}"); this.subtype.add(SubType.HUMAN); @@ -34,7 +27,7 @@ public final class GideonsAvenger extends CardImpl { this.toughness = new MageInt(2); // Whenever a creature an opponent controls becomes tapped, put a +1/+1 counter on Gideon's Avenger. - this.addAbility(new BecomesTappedTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false, filter)); + this.addAbility(new BecomesTappedTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false, StaticFilters.FILTER_OPPONENTS_PERMANENT_A_CREATURE)); } private GideonsAvenger(final GideonsAvenger card) { diff --git a/Mage.Sets/src/mage/cards/g/GiftOfFangs.java b/Mage.Sets/src/mage/cards/g/GiftOfFangs.java new file mode 100644 index 00000000000..c3b9bdad602 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GiftOfFangs.java @@ -0,0 +1,106 @@ +package mage.cards.g; + +import java.util.UUID; + +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.constants.*; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCreaturePermanent; +import mage.abilities.Ability; +import mage.abilities.effects.common.AttachEffect; +import mage.target.TargetPermanent; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author weirddan455 + */ +public final class GiftOfFangs extends CardImpl { + + public GiftOfFangs(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{B}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.Neutral)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // Enchanted creature gets +2/+2 as long as it's a Vampire. Otherwise, it gets -2/-2. + this.addAbility(new SimpleStaticAbility(new GiftOfFangsEffect())); + } + + private GiftOfFangs(final GiftOfFangs card) { + super(card); + } + + @Override + public GiftOfFangs copy() { + return new GiftOfFangs(this); + } +} + +class GiftOfFangsEffect extends ContinuousEffectImpl { + + public GiftOfFangsEffect() { + super(Duration.WhileOnBattlefield, Layer.PTChangingEffects_7, SubLayer.ModifyPT_7c, Outcome.Neutral); + staticText = "Enchanted creature gets +2/+2 as long as it's a Vampire. Otherwise, it gets -2/-2"; + } + + private GiftOfFangsEffect(final GiftOfFangsEffect effect) { + super(effect); + } + + @Override + public GiftOfFangsEffect copy() { + return new GiftOfFangsEffect(this); + } + + @Override + public void init(Ability source, Game game) { + super.init(source, game); + if (affectedObjectsSet) { + // Added boosts of activated or triggered abilities exist independent from the source they are created by + // so a continuous effect for the permanent itself with the attachment is created + Permanent equipment = game.getPermanentOrLKIBattlefield(source.getSourceId()); + if (equipment != null && equipment.getAttachedTo() != null) { + this.setTargetPointer(new FixedTarget(equipment.getAttachedTo(), game.getState().getZoneChangeCounter(equipment.getAttachedTo()))); + } + } + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = null; + if (affectedObjectsSet) { + permanent = game.getPermanent(targetPointer.getFirst(game, source)); + if (permanent == null) { + discard(); + return true; + } + } else { + Permanent equipment = game.getPermanent(source.getSourceId()); + if (equipment != null && equipment.getAttachedTo() != null) { + permanent = game.getPermanent(equipment.getAttachedTo()); + } + } + if (permanent != null) { + if (permanent.hasSubtype(SubType.VAMPIRE, game)) { + permanent.addPower(2); + permanent.addToughness(2); + } else { + permanent.addPower(-2); + permanent.addToughness(-2); + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/g/GiftOfTheWoods.java b/Mage.Sets/src/mage/cards/g/GiftOfTheWoods.java index c6518ace39d..2dd91bd49d2 100644 --- a/Mage.Sets/src/mage/cards/g/GiftOfTheWoods.java +++ b/Mage.Sets/src/mage/cards/g/GiftOfTheWoods.java @@ -1,30 +1,28 @@ - package mage.cards.g; -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; +import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.GainLifeEffect; 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.Duration; -import mage.constants.SubType; -import mage.constants.Outcome; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * - * @author Ketsuban + * @author TheElk801 */ public final class GiftOfTheWoods extends CardImpl { public GiftOfTheWoods(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[] { CardType.ENCHANTMENT }, "{G}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{G}"); this.subtype.add(SubType.AURA); @@ -32,15 +30,10 @@ public final class GiftOfTheWoods extends CardImpl { TargetPermanent auraTarget = new TargetCreaturePermanent(); this.getSpellAbility().addEffect(new AttachEffect(Outcome.Benefit)); this.getSpellAbility().addTarget(auraTarget); - Ability ability = new EnchantAbility(auraTarget.getTargetName()); - this.addAbility(ability); + this.addAbility(new EnchantAbility(auraTarget.getTargetName())); - // Whenever enchanted creature blocks or becomes blocked, it gets +0/+3 until - // end of turn and you gain 1 life. - Ability ability2 = new BlocksOrBecomesBlockedSourceTriggeredAbility( - new BoostEnchantedEffect(0, 3, Duration.EndOfTurn), false); - ability2.addEffect(new GainLifeEffect(1).concatBy("and")); - this.addAbility(ability2); + // Whenever enchanted creature blocks or becomes blocked, it gets +0/+3 until end of turn and you gain 1 life. + this.addAbility(new GiftOfTheWoodsTriggeredAbility()); } private GiftOfTheWoods(final GiftOfTheWoods card) { @@ -52,3 +45,39 @@ public final class GiftOfTheWoods extends CardImpl { return new GiftOfTheWoods(this); } } + +class GiftOfTheWoodsTriggeredAbility extends TriggeredAbilityImpl { + + GiftOfTheWoodsTriggeredAbility() { + super(Zone.BATTLEFIELD, new BoostEnchantedEffect(0, 3, Duration.EndOfTurn)); + this.addEffect(new GainLifeEffect(1)); + } + + private GiftOfTheWoodsTriggeredAbility(final GiftOfTheWoodsTriggeredAbility ability) { + super(ability); + } + + @Override + public GiftOfTheWoodsTriggeredAbility copy() { + return new GiftOfTheWoodsTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.BLOCKER_DECLARED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent permanent = getSourcePermanentIfItStillExists(game); + return permanent != null && permanent.getAttachedTo() != null + && (event.getSourceId().equals(permanent.getAttachedTo()) + || event.getTargetId().equals(permanent.getAttachedTo())); + } + + @Override + public String getRule() { + return "Whenever enchanted creature blocks or becomes blocked, " + + "it gets +0/+3 until end of turn and you gain 1 life."; + } +} diff --git a/Mage.Sets/src/mage/cards/g/GiftOfWrath.java b/Mage.Sets/src/mage/cards/g/GiftOfWrath.java new file mode 100644 index 00000000000..2576d3f871f --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GiftOfWrath.java @@ -0,0 +1,67 @@ +package mage.cards.g; + +import mage.abilities.Ability; +import mage.abilities.common.LeavesBattlefieldTriggeredAbility; +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.CreateTokenEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.game.permanent.token.SpiritRedToken; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GiftOfWrath extends CardImpl { + + private static final Condition condition = new AttachedToMatchesFilterCondition(StaticFilters.FILTER_PERMANENT_CREATURE); + + public GiftOfWrath(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{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/+2 and has menace. + Ability ability = new SimpleStaticAbility(new ConditionalContinuousEffect( + new BoostEnchantedEffect(2, 2), condition, + "as long as enchanted permanent is a creature, it gets +2/+2" + )); + ability.addEffect(new ConditionalContinuousEffect(new GainAbilityAttachedEffect( + new MenaceAbility(), AttachmentType.AURA + ), condition, "and has menace. (It can't be blocked except by two or more creatures.)")); + this.addAbility(ability); + + // When Gift of Wrath leaves the battlefield, create a 2/2 red Spirit creature token with menace. + this.addAbility(new LeavesBattlefieldTriggeredAbility(new CreateTokenEffect(new SpiritRedToken()), false)); + } + + private GiftOfWrath(final GiftOfWrath card) { + super(card); + } + + @Override + public GiftOfWrath copy() { + return new GiftOfWrath(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GildedDrake.java b/Mage.Sets/src/mage/cards/g/GildedDrake.java index 167f9a1524a..10938bacf2e 100644 --- a/Mage.Sets/src/mage/cards/g/GildedDrake.java +++ b/Mage.Sets/src/mage/cards/g/GildedDrake.java @@ -15,8 +15,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Outcome; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -28,12 +27,6 @@ import mage.target.common.TargetCreaturePermanent; */ public final class GildedDrake extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public GildedDrake(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); this.subtype.add(SubType.DRAKE); @@ -47,7 +40,7 @@ public final class GildedDrake extends CardImpl { // This ability can't be countered except by spells and abilities. Ability ability = new EntersBattlefieldTriggeredAbility(new GildedDrakeEffect()); ability.setCanFizzle(false); - ability.addTarget(new TargetCreaturePermanent(0, 1, filter, false)); + ability.addTarget(new TargetCreaturePermanent(0, 1, StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE, false)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/g/GisaGloriousResurrector.java b/Mage.Sets/src/mage/cards/g/GisaGloriousResurrector.java index d21bd6a7c0e..bd1e929324b 100644 --- a/Mage.Sets/src/mage/cards/g/GisaGloriousResurrector.java +++ b/Mage.Sets/src/mage/cards/g/GisaGloriousResurrector.java @@ -89,7 +89,7 @@ class GisaGloriousResurrectorExileEffect extends ReplacementEffectImpl { + game.getState().getZoneChangeCounter(source.getSourceId()), source); return player.moveCardsToExile( zEvent.getTarget(), source, game, false, - CardUtil.getExileZoneId(game, source), "Gisa, Glorious Resurrector" + CardUtil.getExileZoneId(game, source), CardUtil.getSourceName(game, source) ); } diff --git a/Mage.Sets/src/mage/cards/g/GlacialGrasp.java b/Mage.Sets/src/mage/cards/g/GlacialGrasp.java index 200dba1e92b..a528d93fa8d 100644 --- a/Mage.Sets/src/mage/cards/g/GlacialGrasp.java +++ b/Mage.Sets/src/mage/cards/g/GlacialGrasp.java @@ -28,7 +28,7 @@ public final class GlacialGrasp extends CardImpl { this.getSpellAbility().addTarget(new TargetCreaturePermanent()); // Draw a card. - this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("
")); } private GlacialGrasp(final GlacialGrasp card) { diff --git a/Mage.Sets/src/mage/cards/g/GlaiveOfTheGuildpact.java b/Mage.Sets/src/mage/cards/g/GlaiveOfTheGuildpact.java index 69eaad01676..42effa53c6a 100644 --- a/Mage.Sets/src/mage/cards/g/GlaiveOfTheGuildpact.java +++ b/Mage.Sets/src/mage/cards/g/GlaiveOfTheGuildpact.java @@ -35,7 +35,7 @@ public final class GlaiveOfTheGuildpact extends CardImpl { ).setText("and has vigilance")); ability.addEffect(new GainAbilityAttachedEffect( new MenaceAbility(), AttachmentType.EQUIPMENT - ).setText("and menace")); + ).setText("and menace. (A creature with menace can't be blocked except by two or more creatures.)")); ability.addHint(GateYouControlHint.instance); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/g/GlaringAegis.java b/Mage.Sets/src/mage/cards/g/GlaringAegis.java index 2669f534450..e46838c2b3a 100644 --- a/Mage.Sets/src/mage/cards/g/GlaringAegis.java +++ b/Mage.Sets/src/mage/cards/g/GlaringAegis.java @@ -12,7 +12,7 @@ import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; @@ -21,12 +21,6 @@ import mage.target.common.TargetCreaturePermanent; * @author fireshoes */ public final class GlaringAegis extends CardImpl { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } public GlaringAegis(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{W}"); @@ -41,7 +35,7 @@ public final class GlaringAegis extends CardImpl { // When Glaring Aegis enters the battlefield, tap target creature an opponent controls. Ability ability2 = new EntersBattlefieldTriggeredAbility(new TapTargetEffect()); - ability2.addTarget(new TargetCreaturePermanent(filter)); + ability2.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); this.addAbility(ability2); // Enchanted creature gets +1/+3. diff --git a/Mage.Sets/src/mage/cards/g/GlassCastHeart.java b/Mage.Sets/src/mage/cards/g/GlassCastHeart.java new file mode 100644 index 00000000000..5feed114e69 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GlassCastHeart.java @@ -0,0 +1,79 @@ +package mage.cards.g; + +import mage.abilities.Ability; +import mage.abilities.common.AttacksWithCreaturesTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.CompositeCost; +import mage.abilities.costs.common.PayLifeCost; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.LoseLifeOpponentsEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.permanent.token.BloodToken; +import mage.game.permanent.token.EdgarMarkovsCoffinVampireToken; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GlassCastHeart extends CardImpl { + + private static final FilterCreaturePermanent filter + = new FilterCreaturePermanent(SubType.VAMPIRE, ""); + private static final FilterControlledPermanent filter2 + = new FilterControlledPermanent(SubType.BLOOD, "Blood token"); + + static { + filter2.add(TokenPredicate.TRUE); + } + + public GlassCastHeart(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{B}"); + + // Whenever one or more Vampires you control attack, create a Blood token. + this.addAbility(new AttacksWithCreaturesTriggeredAbility( + new CreateTokenEffect(new BloodToken()), 1, filter + ).setTriggerPhrase("Whenever one or more Vampires you control attack, ")); + + // {B}, {T}, Pay 1 life: Create a 1/1 white and black Vampire creature token with lifelink. + Ability ability = new SimpleActivatedAbility( + new CreateTokenEffect(new EdgarMarkovsCoffinVampireToken()), new ManaCostsImpl<>("{B}") + ); + ability.addCost(new TapSourceCost()); + ability.addCost(new PayLifeCost(1)); + this.addAbility(ability); + + // {B}{B}, {T}, Sacrifice Glass-Cast Heart and thirteen Blood tokens: Each opponent loses 13 life and you gain 13 life. + ability = new SimpleActivatedAbility(new LoseLifeOpponentsEffect(13), new ManaCostsImpl<>("{B}{B}")); + ability.addCost(new TapSourceCost()); + ability.addCost(new CompositeCost( + new SacrificeSourceCost(), + new SacrificeTargetCost( + new TargetControlledPermanent(13, filter2) + ), "sacrifice {this} and thirteen Blood tokens" + )); + ability.addEffect(new GainLifeEffect(13).concatBy("and")); + this.addAbility(ability); + } + + private GlassCastHeart(final GlassCastHeart card) { + super(card); + } + + @Override + public GlassCastHeart copy() { + return new GlassCastHeart(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GleefulSabotage.java b/Mage.Sets/src/mage/cards/g/GleefulSabotage.java index b8740eb911d..86b8463fb5b 100644 --- a/Mage.Sets/src/mage/cards/g/GleefulSabotage.java +++ b/Mage.Sets/src/mage/cards/g/GleefulSabotage.java @@ -25,7 +25,7 @@ public final class GleefulSabotage extends CardImpl { this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_ENCHANTMENT)); // Conspire - this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.ONE)); + this.addAbility(new ConspireAbility(ConspireAbility.ConspireTargets.ONE)); } private GleefulSabotage(final GleefulSabotage card) { diff --git a/Mage.Sets/src/mage/cards/g/GlenElendraArchmage.java b/Mage.Sets/src/mage/cards/g/GlenElendraArchmage.java index 15363240f02..a1a61da72db 100644 --- a/Mage.Sets/src/mage/cards/g/GlenElendraArchmage.java +++ b/Mage.Sets/src/mage/cards/g/GlenElendraArchmage.java @@ -15,8 +15,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.FilterSpell; -import mage.filter.predicate.Predicates; +import mage.filter.StaticFilters; import mage.target.TargetSpell; /** @@ -25,12 +24,6 @@ import mage.target.TargetSpell; */ public final class GlenElendraArchmage extends CardImpl { - private static final FilterSpell filter = new FilterSpell("noncreature spell"); - - static { - filter.add(Predicates.not(CardType.CREATURE.getPredicate())); - } - public GlenElendraArchmage(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{U}"); this.subtype.add(SubType.FAERIE); @@ -44,8 +37,9 @@ public final class GlenElendraArchmage extends CardImpl { // {U}, Sacrifice Glen Elendra Archmage: Counter target noncreature spell. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CounterTargetEffect(), new ManaCostsImpl("{U}")); ability.addCost(new SacrificeSourceCost()); - ability.addTarget(new TargetSpell(filter)); + ability.addTarget(new TargetSpell(StaticFilters.FILTER_SPELL_NON_CREATURE)); this.addAbility(ability); + // Persist this.addAbility(new PersistAbility()); } diff --git a/Mage.Sets/src/mage/cards/g/GlimpseTheCosmos.java b/Mage.Sets/src/mage/cards/g/GlimpseTheCosmos.java index e293a529f20..b60d98f16df 100644 --- a/Mage.Sets/src/mage/cards/g/GlimpseTheCosmos.java +++ b/Mage.Sets/src/mage/cards/g/GlimpseTheCosmos.java @@ -1,5 +1,7 @@ package mage.cards.g; +import java.util.HashSet; +import java.util.Set; import mage.abilities.Ability; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.dynamicvalue.common.StaticValue; @@ -16,18 +18,17 @@ import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; import mage.players.Player; import java.util.UUID; -import mage.Mana; +import mage.MageIdentifier; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.decorator.ConditionalAsThoughEffect; import mage.abilities.effects.AsThoughEffectImpl; -import mage.watchers.common.ManaSpentToCastWatcher; +import mage.watchers.Watcher; /** * * @author jeffwadsworth */ - public class GlimpseTheCosmos extends CardImpl { public GlimpseTheCosmos(UUID ownerId, CardSetInfo setInfo) { @@ -45,7 +46,8 @@ public class GlimpseTheCosmos extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.GRAVEYARD, new ConditionalAsThoughEffect( new GlimpseTheCosmosPlayEffect(), - new PermanentsOnTheBattlefieldCondition(new FilterControlledPermanent(SubType.GIANT))))); + new PermanentsOnTheBattlefieldCondition(new FilterControlledPermanent(SubType.GIANT)))).setIdentifier(MageIdentifier.GlimpseTheCosmosWatcher), + new GlimpseTheCosmosWatcher()); this.addAbility(new SimpleStaticAbility(Zone.ALL, new GlimpseTheCosmosReplacementEffect())); @@ -97,14 +99,13 @@ class GlimpseTheCosmosPlayEffect extends AsThoughEffectImpl { } return false; } - } class GlimpseTheCosmosReplacementEffect extends ReplacementEffectImpl { public GlimpseTheCosmosReplacementEffect() { - super(Duration.OneUse, Outcome.Exile); - staticText = "As long as you control a Giant, you may cast {this} from your graveyard by paying {U} rather than paying its mana cost. If you cast {this} this way and it would be put into your graveyard, exile it instead"; + super(Duration.EndOfGame, Outcome.Benefit); + staticText = "As long as you control a Giant, you may cast {this} from your graveyard by paying {U} rather than paying its mana cost. If you cast {this} this way and it would be put into your graveyard, exile it instead"; } public GlimpseTheCosmosReplacementEffect(final GlimpseTheCosmosReplacementEffect effect) { @@ -127,7 +128,6 @@ class GlimpseTheCosmosReplacementEffect extends ReplacementEffectImpl { if (controller != null) { Card card = game.getCard(event.getTargetId()); if (card != null) { - discard(); return controller.moveCards( card, Zone.EXILED, source, game, false, false, false, event.getAppliedEffects()); } @@ -142,20 +142,45 @@ class GlimpseTheCosmosReplacementEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { - ManaSpentToCastWatcher watcher = game.getState().getWatcher(ManaSpentToCastWatcher.class); - if (watcher == null) { - return false; - } - Mana payment = watcher.getLastManaPayment(source.getSourceId()); - if (payment != null - && payment.getBlue() == 1 // must be blue mana - && payment.count() == 1) { // must be just one - if (event.getTargetId().equals(source.getSourceId()) - && ((ZoneChangeEvent) event).getFromZone() == Zone.STACK - && ((ZoneChangeEvent) event).getToZone() != Zone.EXILED) { - return true; - } + GlimpseTheCosmosWatcher watcher = game.getState().getWatcher(GlimpseTheCosmosWatcher.class); + if (watcher != null + && ((ZoneChangeEvent) event).getFromZone() == Zone.STACK + && ((ZoneChangeEvent) event).getToZone() == Zone.GRAVEYARD + && event.getTargetId().equals(source.getSourceId()) + && watcher.isCardSource(game.getCard(source.getSourceId()))) { + return true; } return false; } } + +class GlimpseTheCosmosWatcher extends Watcher { + + private final Set sourceCards = new HashSet<>(); + + public GlimpseTheCosmosWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.CAST_SPELL + && event.hasApprovingIdentifier(MageIdentifier.GlimpseTheCosmosWatcher)) { + Ability approvingAbility = event.getAdditionalReference().getApprovingAbility(); + if (approvingAbility != null + && approvingAbility.getSourceId().equals(event.getSourceId())) { + sourceCards.add(game.getCard(event.getSourceId())); + } + } + } + + @Override + public void reset() { + super.reset(); + sourceCards.clear(); + } + + public boolean isCardSource(Card card) { + return sourceCards.contains(card); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GlintSleeveSiphoner.java b/Mage.Sets/src/mage/cards/g/GlintSleeveSiphoner.java index 99b1d4bc1d2..fb07399e7bc 100644 --- a/Mage.Sets/src/mage/cards/g/GlintSleeveSiphoner.java +++ b/Mage.Sets/src/mage/cards/g/GlintSleeveSiphoner.java @@ -4,7 +4,6 @@ import mage.MageInt; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.EntersBattlefieldOrAttacksSourceTriggeredAbility; import mage.abilities.costs.common.PayEnergyCost; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.DoIfCostPaid; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.LoseLifeSourceControllerEffect; @@ -15,7 +14,6 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.TargetController; -import mage.constants.Zone; import java.util.UUID; @@ -33,17 +31,15 @@ public final class GlintSleeveSiphoner extends CardImpl { this.toughness = new MageInt(1); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // Whenever Glint-Sleeve Siphoner enters the battlefield or attacks, you get {E}. this.addAbility(new EntersBattlefieldOrAttacksSourceTriggeredAbility(new GetEnergyCountersControllerEffect(1))); // At the beginning of your upkeep, you may pay {E}{E}. If you do, draw a card and you lose 1 life. - DoIfCostPaid doIfCostPaidEffect = new DoIfCostPaid(new DrawCardSourceControllerEffect(1), new PayEnergyCost(2)); - Effect effect = new LoseLifeSourceControllerEffect(1); - doIfCostPaidEffect.addEffect(effect.concatBy("and")); - this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, doIfCostPaidEffect, TargetController.YOU, false, false, - "At the beginning of your upkeep, ")); + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new DoIfCostPaid( + new DrawCardSourceControllerEffect(1).setText("you draw a card"), new PayEnergyCost(2) + ).addEffect(new LoseLifeSourceControllerEffect(1).concatBy("and")), TargetController.YOU, false)); } private GlintSleeveSiphoner(final GlintSleeveSiphoner card) { diff --git a/Mage.Sets/src/mage/cards/g/GlitteringWish.java b/Mage.Sets/src/mage/cards/g/GlitteringWish.java index 4faed702145..f9f6bab8f6a 100644 --- a/Mage.Sets/src/mage/cards/g/GlitteringWish.java +++ b/Mage.Sets/src/mage/cards/g/GlitteringWish.java @@ -17,7 +17,7 @@ import mage.filter.predicate.mageobject.MulticoloredPredicate; */ public final class GlitteringWish extends CardImpl { - private static final FilterCard filter = new FilterCard("a multicolored card"); + private static final FilterCard filter = new FilterCard("multicolored card"); static { filter.add(MulticoloredPredicate.instance); @@ -26,7 +26,7 @@ public final class GlitteringWish extends CardImpl { public GlitteringWish(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{G}{W}"); - // You may choose a multicolored card you own from outside the game, reveal that card, and put it into your hand. + // You may reveal a multicolored card you own from outside the game and put it into your hand. this.getSpellAbility().addEffect(new WishEffect(filter)); this.getSpellAbility().addHint(OpenSideboardHint.instance); diff --git a/Mage.Sets/src/mage/cards/g/Gloomdrifter.java b/Mage.Sets/src/mage/cards/g/Gloomdrifter.java index 48d4b3a7526..7108d5f129d 100644 --- a/Mage.Sets/src/mage/cards/g/Gloomdrifter.java +++ b/Mage.Sets/src/mage/cards/g/Gloomdrifter.java @@ -1,9 +1,7 @@ - package mage.cards.g; import java.util.UUID; import mage.MageInt; -import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; @@ -19,9 +17,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ColorPredicate; +import mage.filter.StaticFilters; /** * @@ -29,12 +25,6 @@ import mage.filter.predicate.mageobject.ColorPredicate; */ public final class Gloomdrifter extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creatures"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public Gloomdrifter(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}"); this.subtype.add(SubType.ZOMBIE); @@ -47,7 +37,7 @@ public final class Gloomdrifter extends CardImpl { // Threshold - As long as seven or more cards are in your graveyard, Gloomdrifter has "When Gloomdrifter enters the battlefield, nonblack creatures get -2/-2 until end of turn." Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect( new GainAbilitySourceEffect(new EntersBattlefieldTriggeredAbility( - new BoostAllEffect(-2, -2, Duration.EndOfTurn, filter, false))), + new BoostAllEffect(-2, -2, Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURES_NON_BLACK, false))), new CardsInControllerGraveyardCondition(7), "As long as seven or more cards are in your graveyard, {this} has \"When {this} enters the battlefield, nonblack creatures get -2/-2 until end of turn.\"")); ability.setAbilityWord(AbilityWord.THRESHOLD); diff --git a/Mage.Sets/src/mage/cards/g/Gloomshrieker.java b/Mage.Sets/src/mage/cards/g/Gloomshrieker.java new file mode 100644 index 00000000000..89d8e8555c7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/Gloomshrieker.java @@ -0,0 +1,90 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.common.FilterPermanentCard; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Gloomshrieker extends CardImpl { + + private static final FilterCard filter = new FilterPermanentCard("permanent card from your graveyard"); + + public Gloomshrieker(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{1}{B}{G}"); + + this.subtype.add(SubType.CAT); + this.subtype.add(SubType.BEAST); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Menace + this.addAbility(new MenaceAbility()); + + // When Gloomshrieker enters the battlefield, return target permanent card from your graveyard to your hand. + Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect()); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + this.addAbility(ability); + + // If Gloomshrieker would die, exile it instead. + this.addAbility(new SimpleStaticAbility(new GloomshriekerEffect())); + } + + private Gloomshrieker(final Gloomshrieker card) { + super(card); + } + + @Override + public Gloomshrieker copy() { + return new Gloomshrieker(this); + } +} + +class GloomshriekerEffect extends ReplacementEffectImpl { + + GloomshriekerEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "If {this} would die, exile it instead"; + } + + private GloomshriekerEffect(final GloomshriekerEffect effect) { + super(effect); + } + + @Override + public GloomshriekerEffect copy() { + return new GloomshriekerEffect(this); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return event.getTargetId().equals(source.getSourceId()) && ((ZoneChangeEvent) event).isDiesEvent(); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + ((ZoneChangeEvent) event).setToZone(Zone.EXILED); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/g/GloriousSunrise.java b/Mage.Sets/src/mage/cards/g/GloriousSunrise.java new file mode 100644 index 00000000000..cdbf55c9926 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GloriousSunrise.java @@ -0,0 +1,86 @@ +package mage.cards.g; + +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.BeginningOfCombatTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.abilities.keyword.TrampleAbility; +import mage.abilities.mana.SimpleManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.mageobject.PowerPredicate; +import mage.target.common.TargetLandPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GloriousSunrise extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledCreaturePermanent(); + + static { + filter.add(new PowerPredicate(ComparisonType.MORE_THAN, 2)); + } + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter); + private static final Hint hint = new ConditionHint( + condition, "You control a creature with power 3 or greater" + ); + + public GloriousSunrise(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{G}{G}"); + + // At the beginning of combat on your turn, choose one — + // • Creatures you control get +1/+1 and gain trample until end of turn. + Ability ability = new BeginningOfCombatTriggeredAbility(new BoostControlledEffect( + 1, 1, Duration.EndOfTurn + ).setText("creatures you control get +1/+1"), TargetController.YOU, false); + ability.addEffect(new GainAbilityControlledEffect( + TrampleAbility.getInstance(), Duration.EndOfTurn, + StaticFilters.FILTER_PERMANENT_CREATURE + ).setText("and gain trample until end of turn")); + + // • Target land gains "{T}: Add {G}{G}{G}" until end of turn. + Mode mode = new Mode(new GainAbilityTargetEffect(new SimpleManaAbility( + Zone.BATTLEFIELD, Mana.GreenMana(3), new TapSourceCost() + ), Duration.EndOfTurn).setText("target land gains \"{T}: Add {G}{G}{G}\" until end of turn")); + mode.addTarget(new TargetLandPermanent()); + ability.addMode(mode); + + // • Draw a card if you control a creature with power 3 or greater. + ability.addMode(new Mode(new ConditionalOneShotEffect( + new DrawCardSourceControllerEffect(1), condition, + "draw a card if you control a creature with power 3 or greater" + ))); + + // • You gain 3 life. + ability.addMode(new Mode(new GainLifeEffect(3))); + this.addAbility(ability.addHint(hint)); + } + + private GloriousSunrise(final GloriousSunrise card) { + super(card); + } + + @Override + public GloriousSunrise copy() { + return new GloriousSunrise(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/Glory.java b/Mage.Sets/src/mage/cards/g/Glory.java index 29504d96225..5f5b5eec623 100644 --- a/Mage.Sets/src/mage/cards/g/Glory.java +++ b/Mage.Sets/src/mage/cards/g/Glory.java @@ -14,7 +14,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Zone; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; /** * @@ -31,7 +31,7 @@ public final class Glory extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); // {2}{W}: Choose a color. Creatures you control gain protection from the chosen color until end of turn. Activate this ability only if Glory is in your graveyard. - Effect effect = new GainProtectionFromColorAllEffect(Duration.EndOfTurn, new FilterControlledCreaturePermanent("Creatures you control")); + Effect effect = new GainProtectionFromColorAllEffect(Duration.EndOfTurn, StaticFilters.FILTER_CONTROLLED_CREATURES); effect.setText("Choose a color. Creatures you control gain protection from the chosen color until end of turn. Activate only if {this} is in your graveyard."); this.addAbility(new SimpleActivatedAbility(Zone.GRAVEYARD, effect, new ManaCostsImpl("{2}{W}"))); } diff --git a/Mage.Sets/src/mage/cards/g/GluttonousGuest.java b/Mage.Sets/src/mage/cards/g/GluttonousGuest.java new file mode 100644 index 00000000000..142239bbd93 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GluttonousGuest.java @@ -0,0 +1,52 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SacrificePermanentTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.permanent.token.BloodToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GluttonousGuest extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent("a Blood token"); + + static { + filter.add(TokenPredicate.TRUE); + filter.add(SubType.BLOOD.getPredicate()); + } + + public GluttonousGuest(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.subtype.add(SubType.VAMPIRE); + this.power = new MageInt(1); + this.toughness = new MageInt(4); + + // When Gluttonous Guest enters the battlefield, create a Blood token. + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new BloodToken()))); + + // Whenever you sacrifice a Blood token, you gain 1 life. + this.addAbility(new SacrificePermanentTriggeredAbility(new GainLifeEffect(1), filter)); + } + + private GluttonousGuest(final GluttonousGuest card) { + super(card); + } + + @Override + public GluttonousGuest copy() { + return new GluttonousGuest(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GluttonousSlug.java b/Mage.Sets/src/mage/cards/g/GluttonousSlug.java index bd9695b4a19..be22de82e07 100644 --- a/Mage.Sets/src/mage/cards/g/GluttonousSlug.java +++ b/Mage.Sets/src/mage/cards/g/GluttonousSlug.java @@ -24,7 +24,7 @@ public final class GluttonousSlug extends CardImpl { this.toughness = new MageInt(3); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // Evolve this.addAbility(new EvolveAbility()); diff --git a/Mage.Sets/src/mage/cards/g/GlyphOfDelusion.java b/Mage.Sets/src/mage/cards/g/GlyphOfDelusion.java index 5310dd43110..13e76804812 100644 --- a/Mage.Sets/src/mage/cards/g/GlyphOfDelusion.java +++ b/Mage.Sets/src/mage/cards/g/GlyphOfDelusion.java @@ -132,13 +132,13 @@ class GlyphOfDelusionEffect extends OneShotEffect { SimpleStaticAbility ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousRuleModifyingEffect(new DontUntapInControllersUntapStepSourceEffect(), new SourceHasCounterCondition(CounterType.GLYPH)).setText("This creature doesn't untap during your untap step if it has a glyph counter on it")); GainAbilityTargetEffect effect = new GainAbilityTargetEffect(ability, Duration.Custom); - effect.setTargetPointer(new FixedTarget(targetPermanent.getId())); + effect.setTargetPointer(new FixedTarget(targetPermanent.getId(), game)); game.addEffect(effect, source); BeginningOfUpkeepTriggeredAbility ability2 = new BeginningOfUpkeepTriggeredAbility(new RemoveCounterSourceEffect(CounterType.GLYPH.createInstance()), TargetController.YOU, false); GainAbilityTargetEffect effect2 = new GainAbilityTargetEffect(ability2, Duration.Custom); - effect2.setTargetPointer(new FixedTarget(targetPermanent.getId())); + effect2.setTargetPointer(new FixedTarget(targetPermanent.getId(), game)); game.addEffect(effect2, source); } } diff --git a/Mage.Sets/src/mage/cards/g/GnarledGrovestrider.java b/Mage.Sets/src/mage/cards/g/GnarledGrovestrider.java new file mode 100644 index 00000000000..ad4fced764d --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GnarledGrovestrider.java @@ -0,0 +1,50 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * + * @author weirddan455 + */ +public class GnarledGrovestrider extends CardImpl { + + public GnarledGrovestrider(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); + this.subtype.add(SubType.TREEFOLK); + this.color.setGreen(true); + + // Back half of Dormant Grove + this.nightCard = true; + + this.power = new MageInt(3); + this.toughness = new MageInt(6); + + this.addAbility(VigilanceAbility.getInstance()); + + // Other creatures you control have vigilance. + this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( + VigilanceAbility.getInstance(), Duration.WhileOnBattlefield, + StaticFilters.FILTER_PERMANENT_CREATURES, true + ))); + } + + private GnarledGrovestrider(final GnarledGrovestrider card) { + super(card); + } + + @Override + public GnarledGrovestrider copy() { + return new GnarledGrovestrider(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GnarlidColony.java b/Mage.Sets/src/mage/cards/g/GnarlidColony.java index 8a3b7bdb59e..f64b199352a 100644 --- a/Mage.Sets/src/mage/cards/g/GnarlidColony.java +++ b/Mage.Sets/src/mage/cards/g/GnarlidColony.java @@ -14,8 +14,7 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; import mage.counters.CounterType; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; import java.util.UUID; @@ -24,12 +23,6 @@ import java.util.UUID; */ public final class GnarlidColony extends CardImpl { - private static final FilterPermanent filter = new FilterControlledCreaturePermanent(); - - static { - filter.add(CounterType.P1P1.getPredicate()); - } - public GnarlidColony(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); @@ -48,9 +41,10 @@ public final class GnarlidColony extends CardImpl { // Each creature you control with a +1/+1 counter on it has trample. this.addAbility(new SimpleStaticAbility(new GainAbilityAllEffect( - TrampleAbility.getInstance(), Duration.WhileOnBattlefield, filter, - "Each creature you control with a +1/+1 counter on it has trample" - ))); + TrampleAbility.getInstance(), + Duration.WhileOnBattlefield, + StaticFilters.FILTER_EACH_CONTROLLED_CREATURE_P1P1)) + ); } private GnarlidColony(final GnarlidColony card) { diff --git a/Mage.Sets/src/mage/cards/g/GoBlank.java b/Mage.Sets/src/mage/cards/g/GoBlank.java index f62dcc7ecee..e1dc8ccddce 100644 --- a/Mage.Sets/src/mage/cards/g/GoBlank.java +++ b/Mage.Sets/src/mage/cards/g/GoBlank.java @@ -20,7 +20,7 @@ public final class GoBlank extends CardImpl { // Target player discards two cards. Then exile all cards from that player's graveyard. this.getSpellAbility().addEffect(new DiscardTargetEffect(2)); this.getSpellAbility().addEffect(new ExileGraveyardAllTargetPlayerEffect() - .setText("Then exile all cards from that player's graveyard")); + .setText("Then exile that player's graveyard")); this.getSpellAbility().addTarget(new TargetPlayer()); } diff --git a/Mage.Sets/src/mage/cards/g/GoShintaiOfAncientWars.java b/Mage.Sets/src/mage/cards/g/GoShintaiOfAncientWars.java new file mode 100644 index 00000000000..43ce72c6b49 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GoShintaiOfAncientWars.java @@ -0,0 +1,66 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.DoWhenCostPaid; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.TargetController; +import mage.filter.common.FilterControlledPermanent; +import mage.target.common.TargetPlayerOrPlaneswalker; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GoShintaiOfAncientWars extends CardImpl { + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount( + new FilterControlledPermanent(SubType.SHRINE) + ); + private static final Hint hint = new ValueHint("Shrines you control", xValue); + + public GoShintaiOfAncientWars(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{2}{R}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.SHRINE); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // First strike + this.addAbility(FirstStrikeAbility.getInstance()); + + // At the beginning of your end step, you may pay {1}. When you do, Go-Shintai of Ancient Wars deals X damage to target player or planeswalker, where X is the number of Shrines you control. + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new DamageTargetEffect(xValue), false, "{this} deals X damage to target " + + "player or planeswalker, where X is the number of Shrines you control" + ); + ability.addTarget(new TargetPlayerOrPlaneswalker()); + this.addAbility(new BeginningOfEndStepTriggeredAbility( + new DoWhenCostPaid(ability, new GenericManaCost(1), "Pay {1}?"), + TargetController.YOU, false + ).addHint(hint)); + } + + private GoShintaiOfAncientWars(final GoShintaiOfAncientWars card) { + super(card); + } + + @Override + public GoShintaiOfAncientWars copy() { + return new GoShintaiOfAncientWars(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GoShintaiOfBoundlessVigor.java b/Mage.Sets/src/mage/cards/g/GoShintaiOfBoundlessVigor.java new file mode 100644 index 00000000000..14884d56221 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GoShintaiOfBoundlessVigor.java @@ -0,0 +1,66 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.DoWhenCostPaid; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.TargetController; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GoShintaiOfBoundlessVigor extends CardImpl { + + private static final FilterPermanent filter + = new FilterPermanent(SubType.SHRINE, "Shrine"); + private static final DynamicValue xValue + = new PermanentsOnBattlefieldCount(new FilterControlledPermanent(SubType.SHRINE)); + + public GoShintaiOfBoundlessVigor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{1}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.SHRINE); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // At the beginning of your end step, you may pay {1}. When you do, put a +1/+1 counter on target Shrine for each Shrine you control. + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new AddCountersTargetEffect( + CounterType.P1P1.createInstance(0), xValue + ), false + ); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(new BeginningOfEndStepTriggeredAbility(new DoWhenCostPaid( + ability, new GenericManaCost(1), "Pay {1}?" + ), TargetController.YOU, false)); + } + + private GoShintaiOfBoundlessVigor(final GoShintaiOfBoundlessVigor card) { + super(card); + } + + @Override + public GoShintaiOfBoundlessVigor copy() { + return new GoShintaiOfBoundlessVigor(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GoShintaiOfHiddenCruelty.java b/Mage.Sets/src/mage/cards/g/GoShintaiOfHiddenCruelty.java new file mode 100644 index 00000000000..fdcf7b47f61 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GoShintaiOfHiddenCruelty.java @@ -0,0 +1,93 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.DoWhenCostPaid; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.DeathtouchAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GoShintaiOfHiddenCruelty extends CardImpl { + + private static final FilterPermanent filter = new FilterCreaturePermanent( + "creature with toughness less than or equal to the number of Shrines you control" + ); + + static { + filter.add(GoShintaiOfHiddenCrueltyPredicate.instance); + } + + public GoShintaiOfHiddenCruelty(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{3}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.SHRINE); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Deathtouch + this.addAbility(DeathtouchAbility.getInstance()); + + // At the beginning of your end step, you may pay {1}. When you do, destroy target creature with toughness X or less, where X is the number of Shrines you control. + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new DestroyTargetEffect(), false, "destroy target creature " + + "with toughness X or less, where X is the number of Shrines you control" + ); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(new BeginningOfEndStepTriggeredAbility( + new DoWhenCostPaid(ability, new GenericManaCost(1), "Pay {1}?"), + TargetController.YOU, false + ).addHint(GoShintaiOfHiddenCrueltyPredicate.getHint())); + } + + private GoShintaiOfHiddenCruelty(final GoShintaiOfHiddenCruelty card) { + super(card); + } + + @Override + public GoShintaiOfHiddenCruelty copy() { + return new GoShintaiOfHiddenCruelty(this); + } +} + +enum GoShintaiOfHiddenCrueltyPredicate implements ObjectSourcePlayerPredicate { + instance; + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.SHRINE); + private static final Hint hint = new ValueHint( + "Shrines you control", new PermanentsOnBattlefieldCount(filter) + ); + + public static Hint getHint() { + return hint; + } + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + return input.getObject().getToughness().getValue() <= game.getBattlefield().count( + filter, input.getSourceId(), input.getPlayerId(), game + ); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GoShintaiOfLifesOrigin.java b/Mage.Sets/src/mage/cards/g/GoShintaiOfLifesOrigin.java new file mode 100644 index 00000000000..5bfa45393be --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GoShintaiOfLifesOrigin.java @@ -0,0 +1,72 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterCard; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.common.FilterEnchantmentCard; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.permanent.token.ShrineToken; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GoShintaiOfLifesOrigin extends CardImpl { + + private static final FilterCard filter + = new FilterEnchantmentCard("enchantment card from your graveyard"); + private static final FilterPermanent filter2 + = new FilterControlledPermanent(SubType.SHRINE, "nontoken Shrine"); + + static { + filter2.add(TokenPredicate.FALSE); + } + + public GoShintaiOfLifesOrigin(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{3}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.SHRINE); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // {W}{U}{B}{R}{G}, {T}: Return target enchantment card from your graveyard to the battlefield. + Ability ability = new SimpleActivatedAbility( + new ReturnFromGraveyardToBattlefieldTargetEffect(), + new ManaCostsImpl<>("{W}{U}{B}{R}{G}") + ); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + this.addAbility(ability); + + // Whenever Go-Shintai of Life's Origin or another nontoken Shrine enters the battlefield under your control, create a 1/1 colorless Shrine enchantment creature token. + this.addAbility(new EntersBattlefieldThisOrAnotherTriggeredAbility( + new CreateTokenEffect(new ShrineToken()), + filter2, false, true + )); + } + + private GoShintaiOfLifesOrigin(final GoShintaiOfLifesOrigin card) { + super(card); + } + + @Override + public GoShintaiOfLifesOrigin copy() { + return new GoShintaiOfLifesOrigin(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GoShintaiOfLostWisdom.java b/Mage.Sets/src/mage/cards/g/GoShintaiOfLostWisdom.java new file mode 100644 index 00000000000..8d313a0b612 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GoShintaiOfLostWisdom.java @@ -0,0 +1,65 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.DoWhenCostPaid; +import mage.abilities.effects.common.MillCardsTargetEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.TargetController; +import mage.filter.common.FilterControlledPermanent; +import mage.target.TargetPlayer; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GoShintaiOfLostWisdom extends CardImpl { + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount( + new FilterControlledPermanent(SubType.SHRINE) + ); + private static final Hint hint = new ValueHint("Shrines you control", xValue); + + public GoShintaiOfLostWisdom(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{1}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.SHRINE); + this.power = new MageInt(0); + this.toughness = new MageInt(4); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // At the beginning of your end step, you may pay {1}. When you do, target player mills X cards, where X is the number of Shrines you control. + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new MillCardsTargetEffect(xValue), false, + "target player mills X cards, where X is the number of Shrines you control" + ); + ability.addTarget(new TargetPlayer()); + this.addAbility(new BeginningOfEndStepTriggeredAbility(new DoWhenCostPaid( + ability, new GenericManaCost(1), "Pay {1}?" + ), TargetController.YOU, false).addHint(hint)); + } + + private GoShintaiOfLostWisdom(final GoShintaiOfLostWisdom card) { + super(card); + } + + @Override + public GoShintaiOfLostWisdom copy() { + return new GoShintaiOfLostWisdom(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GoShintaiOfSharedPurpose.java b/Mage.Sets/src/mage/cards/g/GoShintaiOfSharedPurpose.java new file mode 100644 index 00000000000..6b2f06fe5ba --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GoShintaiOfSharedPurpose.java @@ -0,0 +1,62 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.TargetController; +import mage.filter.common.FilterControlledPermanent; +import mage.game.permanent.token.SpiritToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GoShintaiOfSharedPurpose extends CardImpl { + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount( + new FilterControlledPermanent(SubType.SHRINE) + ); + private static final Hint hint = new ValueHint("Shrines you control", xValue); + + public GoShintaiOfSharedPurpose(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{3}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.SHRINE); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // At the beginning of your end step, you may pay {1}. If you do, create a 1/1 colorless Spirit creature token for each Shrine you control. + this.addAbility(new BeginningOfEndStepTriggeredAbility( + new DoIfCostPaid( + new CreateTokenEffect(new SpiritToken(), xValue), + new GenericManaCost(1) + ), TargetController.YOU, false + ).addHint(hint)); + } + + private GoShintaiOfSharedPurpose(final GoShintaiOfSharedPurpose card) { + super(card); + } + + @Override + public GoShintaiOfSharedPurpose copy() { + return new GoShintaiOfSharedPurpose(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GoblinDarkDwellers.java b/Mage.Sets/src/mage/cards/g/GoblinDarkDwellers.java index 6c0642398b0..d07ba81c428 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinDarkDwellers.java +++ b/Mage.Sets/src/mage/cards/g/GoblinDarkDwellers.java @@ -47,7 +47,7 @@ public final class GoblinDarkDwellers extends CardImpl { this.toughness = new MageInt(4); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // When Goblin Dark-Dwellers enters the battlefield, you may cast target instant or sorcery card with converted mana cost 3 or less // from your graveyard without paying its mana cost. If that card would be put into your graveyard this turn, exile it instead. diff --git a/Mage.Sets/src/mage/cards/g/GoblinFestival.java b/Mage.Sets/src/mage/cards/g/GoblinFestival.java index 66edf00deca..66510d6bec5 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinFestival.java +++ b/Mage.Sets/src/mage/cards/g/GoblinFestival.java @@ -79,7 +79,7 @@ class GoblinFestivalChangeControlEffect extends OneShotEffect { Player chosenOpponent = game.getPlayer(target.getFirstTarget()); if (chosenOpponent != null) { ContinuousEffect effect = new GoblinFestivalGainControlEffect(Duration.Custom, chosenOpponent.getId()); - effect.setTargetPointer(new FixedTarget(sourcePermanent.getId())); + effect.setTargetPointer(new FixedTarget(sourcePermanent.getId(), game)); game.addEffect(effect, source); game.informPlayers(chosenOpponent.getLogName() + " has gained control of " + sourcePermanent.getLogName()); return true; diff --git a/Mage.Sets/src/mage/cards/g/GoblinGame.java b/Mage.Sets/src/mage/cards/g/GoblinGame.java index 432a2d9c6ce..5956c1ccfbc 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinGame.java +++ b/Mage.Sets/src/mage/cards/g/GoblinGame.java @@ -1,9 +1,5 @@ - package mage.cards.g; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; @@ -13,8 +9,10 @@ import mage.constants.Outcome; import mage.game.Game; import mage.players.Player; +import java.util.*; +import java.util.stream.Collectors; + /** - * * @author L_J */ public final class GoblinGame extends CardImpl { @@ -25,7 +23,6 @@ public final class GoblinGame extends CardImpl { // Each player hides at least one item, then all players reveal them simultaneously. Each player loses life equal to the number of items they revealed. The player who revealed the fewest items then loses half their life, rounded up. If two or more players are tied for fewest, each loses half their life, rounded up. // Reinterpreted as: Each player secretly chooses a number greater than 0. Then those numbers are revealed. Each player loses life equal to their chosen number. The player who revealed the lowest number then loses half their life, rounded up. If two or more players are tied for lowest, each loses half their life, rounded up. this.getSpellAbility().addEffect(new GoblinGameEffect()); - } private GoblinGame(final GoblinGame card) { @@ -42,7 +39,10 @@ class GoblinGameEffect extends OneShotEffect { public GoblinGameEffect() { super(Outcome.Detriment); - this.staticText = "Each player hides at least one item, then all players reveal them simultaneously. Each player loses life equal to the number of items they revealed. The player who revealed the fewest items then loses half their life, rounded up. If two or more players are tied for fewest, each loses half their life, rounded up."; + this.staticText = "Each player hides at least one item, then all players reveal them simultaneously. " + + "Each player loses life equal to the number of items they revealed. " + + "The player who revealed the fewest items then loses half their life, rounded up. " + + "If two or more players are tied for fewest, each loses half their life, rounded up."; } public GoblinGameEffect(final GoblinGameEffect effect) { @@ -56,41 +56,42 @@ class GoblinGameEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - int lowestNumber = 0; - int number = 0; - String message = "Choose a number of objects to hide."; - Map numberChosen = new HashMap<>(); + Map numberChosen = new HashMap<>(); - //players choose numbers - for (Player player : game.getPlayers().values()) { - if (player != null) { - // TODO: consider changing 1000 to another cap, or even Integer.MAX_VALUE if the Volcano Hellion binary wraparound gets addressed (although hiding over two billions of items would be rather difficult IRL) - number = player.getAmount(1, 1000, message, game); - numberChosen.put(player, number); - } + // players choose numbers + List players = game + .getState() + .getPlayersInRange(source.getControllerId(), game) + .stream() + .map(game::getPlayer) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + for (Player player : players) { + // TODO: consider changing 1000 to another cap, or even Integer.MAX_VALUE if the Volcano Hellion binary wraparound gets addressed (although hiding over two billions of items would be rather difficult IRL) + numberChosen.put(player.getId(), player.getAmount(1, 1000, "Choose a number of objects to hide.", game)); } - //get lowest number - for (Player player : numberChosen.keySet()) { - if (lowestNumber == 0 || lowestNumber > numberChosen.get(player)) { - lowestNumber = numberChosen.get(player); - } + + // get lowest number + int lowestNumber = numberChosen + .values() + .stream() + .mapToInt(x -> x) + .min() + .orElse(0); + + // reveal numbers to players and follow through with effects + for (Player player : players) { + game.informPlayers(player.getLogName() + " chose " + numberChosen.get(player.getId())); + player.loseLife(numberChosen.get(player.getId()), game, source, false); } - //reveal numbers to players and follow through with effects - for (Player player : game.getPlayers().values()) { - if (player != null) { - game.informPlayers(player.getLogName() + " chose number " + numberChosen.get(player)); - player.loseLife(numberChosen.get(player), game, source, false); + for (Player player : players) { + if (numberChosen.get(player.getId()) > lowestNumber) { + continue; } - } - for (Player player : game.getPlayers().values()) { - if (player != null) { - if (numberChosen.get(player) <= lowestNumber) { - game.informPlayers(player.getLogName() + " chose the lowest number"); - Integer amount = (int) Math.ceil(player.getLife() / 2f); - if (amount > 0) { - player.loseLife(amount, game, source, false); - } - } + game.informPlayers(player.getLogName() + " chose the lowest number"); + Integer amount = (int) Math.ceil(player.getLife() / 2f); + if (amount > 0) { + player.loseLife(amount, game, source, false); } } return true; diff --git a/Mage.Sets/src/mage/cards/g/GoblinGloryChaser.java b/Mage.Sets/src/mage/cards/g/GoblinGloryChaser.java index d29fe0bdcc5..7fd4f6de771 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinGloryChaser.java +++ b/Mage.Sets/src/mage/cards/g/GoblinGloryChaser.java @@ -38,7 +38,8 @@ public final class GoblinGloryChaser extends CardImpl { Effect effect = new ConditionalContinuousEffect( new GainAbilitySourceEffect(new MenaceAbility(), Duration.WhileOnBattlefield), RenownedSourceCondition.instance, - "As long as {this} is renowned, it has menace"); + "As long as {this} is renowned, it has menace. " + + "(It can't be blocked except by two or more creatures.)"); Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, effect); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/g/GoblinGuide.java b/Mage.Sets/src/mage/cards/g/GoblinGuide.java index 015e02d271f..d70184c2b71 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinGuide.java +++ b/Mage.Sets/src/mage/cards/g/GoblinGuide.java @@ -81,7 +81,7 @@ class GoblinGuideTriggeredAbility extends TriggeredAbilityImpl { if (defenderId != null) { for (Effect effect : this.getEffects()) { // set here because attacking creature can be removed until effect resolves - effect.setTargetPointer(new FixedTarget(defenderId)); + effect.setTargetPointer(new FixedTarget(defenderId, game)); } return true; } diff --git a/Mage.Sets/src/mage/cards/g/GoblinKing.java b/Mage.Sets/src/mage/cards/g/GoblinKing.java index 411fd4f5b53..32fa90921b8 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinKing.java +++ b/Mage.Sets/src/mage/cards/g/GoblinKing.java @@ -31,7 +31,7 @@ public final class GoblinKing extends CardImpl { // Other Goblin creatures get +1/+1 and have mountainwalk. Effect effect = new BoostAllEffect(1, 1, Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE_GOBLINS, true); - effect.setText("Other Goblin creatures get +1/+1"); + effect.setText("Other Goblins get +1/+1"); Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, effect); effect = new GainAbilityAllEffect(new MountainwalkAbility(), Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE_GOBLINS, true); effect.setText("and have mountainwalk"); diff --git a/Mage.Sets/src/mage/cards/g/GoblinPiledriver.java b/Mage.Sets/src/mage/cards/g/GoblinPiledriver.java index a6aff231e73..2dc338c92d8 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinPiledriver.java +++ b/Mage.Sets/src/mage/cards/g/GoblinPiledriver.java @@ -5,6 +5,7 @@ import java.util.UUID; import mage.MageInt; import mage.ObjectColor; 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; @@ -30,6 +31,8 @@ public final class GoblinPiledriver extends CardImpl { filter.add(AnotherPredicate.instance); } + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter, 2); + public GoblinPiledriver(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{R}"); this.subtype.add(SubType.GOBLIN); @@ -41,8 +44,7 @@ public final class GoblinPiledriver extends CardImpl { // Protection from blue this.addAbility(ProtectionAbility.from(ObjectColor.BLUE)); // Whenever Goblin Piledriver attacks, it gets +2/+0 until end of turn for each other attacking Goblin. - PermanentsOnBattlefieldCount value = new PermanentsOnBattlefieldCount(filter, 2); - this.addAbility(new AttacksTriggeredAbility(new BoostSourceEffect(value, StaticValue.get(0), Duration.EndOfTurn, true), false)); + this.addAbility(new AttacksTriggeredAbility(new BoostSourceEffect(xValue, StaticValue.get(0), Duration.EndOfTurn, true, "it"), false)); } private GoblinPiledriver(final GoblinPiledriver card) { diff --git a/Mage.Sets/src/mage/cards/g/GoblinRabblemaster.java b/Mage.Sets/src/mage/cards/g/GoblinRabblemaster.java index 77c078fae51..b0b6dcc9536 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinRabblemaster.java +++ b/Mage.Sets/src/mage/cards/g/GoblinRabblemaster.java @@ -1,28 +1,28 @@ - package mage.cards.g; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.common.BeginningOfCombatTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.dynamicvalue.common.StaticValue; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.combat.AttacksIfAbleAllEffect; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +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.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.AttackingPredicate; import mage.game.permanent.token.GoblinToken; -import mage.watchers.common.AttackedThisTurnWatcher; + +import java.util.UUID; /** - * * @author Quercitron */ public final class GoblinRabblemaster extends CardImpl { @@ -47,14 +47,13 @@ public final class GoblinRabblemaster extends CardImpl { this.toughness = new MageInt(2); // Other Goblin creatures you control attack each turn if able. - Effect effect = new AttacksIfAbleAllEffect(otherGoblinFilter); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect), new AttackedThisTurnWatcher()); + this.addAbility(new SimpleStaticAbility(new AttacksIfAbleAllEffect(otherGoblinFilter, Duration.WhileOnBattlefield, true))); // At the beginning of combat on your turn, create a 1/1 red Goblin creature token with haste. this.addAbility(new BeginningOfCombatTriggeredAbility(new CreateTokenEffect(new GoblinToken(true)), TargetController.YOU, false)); // When Goblin Rabblemaster attacks, it gets +1/+0 until end of turn for each other attacking Goblin. - this.addAbility(new AttacksTriggeredAbility(new BoostSourceEffect(new PermanentsOnBattlefieldCount(attackingFilter), StaticValue.get(0), Duration.EndOfTurn, true), false)); + this.addAbility(new AttacksTriggeredAbility(new BoostSourceEffect(new PermanentsOnBattlefieldCount(attackingFilter), StaticValue.get(0), Duration.EndOfTurn, true, "it"), false)); } private GoblinRabblemaster(final GoblinRabblemaster card) { diff --git a/Mage.Sets/src/mage/cards/g/GoblinRazerunners.java b/Mage.Sets/src/mage/cards/g/GoblinRazerunners.java index d60674eeb85..fa861bae0ab 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinRazerunners.java +++ b/Mage.Sets/src/mage/cards/g/GoblinRazerunners.java @@ -44,7 +44,7 @@ public final class GoblinRazerunners extends CardImpl { this.addAbility(ability); // At the beginning of your end step, you may have Goblin Razerunners deal damage equal to the number of +1/+1 counters on it to target player. - ability = new BeginningOfYourEndStepTriggeredAbility(new DamageTargetEffect(new CountersSourceCount(CounterType.P1P1)), true); + ability = new BeginningOfYourEndStepTriggeredAbility(new DamageTargetEffect(new CountersSourceCount(CounterType.P1P1)).setText("you may have {this} deal damage equal to the number of +1/+1 counters on it to target player or planeswalker"), true); ability.addTarget(new TargetPlayerOrPlaneswalker()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/g/GoblinTrashmaster.java b/Mage.Sets/src/mage/cards/g/GoblinTrashmaster.java index a4f0f60603f..cb525385e2a 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinTrashmaster.java +++ b/Mage.Sets/src/mage/cards/g/GoblinTrashmaster.java @@ -26,7 +26,7 @@ import mage.target.common.TargetControlledPermanent; public final class GoblinTrashmaster extends CardImpl { private static final FilterCreaturePermanent filter - = new FilterCreaturePermanent(SubType.GOBLIN, "Goblins you control"); + = new FilterCreaturePermanent(SubType.GOBLIN, "Goblins"); private static final FilterControlledCreaturePermanent filter2 = new FilterControlledCreaturePermanent(SubType.GOBLIN, "a Goblin"); diff --git a/Mage.Sets/src/mage/cards/g/GoblinWelder.java b/Mage.Sets/src/mage/cards/g/GoblinWelder.java index 219958858cf..13160f415ff 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinWelder.java +++ b/Mage.Sets/src/mage/cards/g/GoblinWelder.java @@ -20,6 +20,7 @@ import mage.target.common.TargetCardInGraveyard; import mage.target.targetpointer.EachTargetPointer; import java.util.UUID; +import mage.filter.common.FilterArtifactCard; /** * @author Plopman @@ -54,11 +55,11 @@ public final class GoblinWelder extends CardImpl { public GoblinWelderEffect() { super(Outcome.PutCardInPlay); - staticText = "Choose target artifact a player controls " + - "and target artifact card in that player's graveyard. " + - "If both targets are still legal as this ability resolves, " + - "that player simultaneously sacrifices the artifact " + - "and returns the artifact card to the battlefield"; + staticText = "Choose target artifact a player controls " + + "and target artifact card in that player's graveyard. " + + "If both targets are still legal as this ability resolves, " + + "that player simultaneously sacrifices the artifact " + + "and returns the artifact card to the battlefield"; this.setTargetPointer(new EachTargetPointer()); } @@ -74,7 +75,9 @@ public final class GoblinWelder extends CardImpl { Permanent artifact = game.getPermanent(getTargetPointer().getFirst(game, source)); Card card = game.getCard(getTargetPointer().getTargets(game, source).get(1)); Player controller = game.getPlayer(source.getControllerId()); - if (artifact == null || card == null || controller == null) { + if (artifact == null + || card == null + || controller == null) { return false; } Player owner = game.getPlayer(card.getOwnerId()); @@ -82,6 +85,7 @@ public final class GoblinWelder extends CardImpl { return false; } boolean sacrifice = artifact.sacrifice(source, game); + game.getState().processAction(game); // bug #7672 boolean putOnBF = owner.moveCards(card, Zone.BATTLEFIELD, source, game); if (sacrifice || putOnBF) { return true; @@ -99,7 +103,7 @@ public final class GoblinWelder extends CardImpl { private static class GoblinWelderTarget extends TargetCardInGraveyard { public GoblinWelderTarget() { - super(); + super(new FilterArtifactCard()); targetName = "target artifact card in that player's graveyard"; } diff --git a/Mage.Sets/src/mage/cards/g/Goblinslide.java b/Mage.Sets/src/mage/cards/g/Goblinslide.java index bc29ba41bd1..0d08e8cd1f5 100644 --- a/Mage.Sets/src/mage/cards/g/Goblinslide.java +++ b/Mage.Sets/src/mage/cards/g/Goblinslide.java @@ -9,8 +9,7 @@ import mage.abilities.effects.common.DoIfCostPaid; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.FilterSpell; -import mage.filter.predicate.Predicates; +import mage.filter.StaticFilters; import mage.game.permanent.token.GoblinToken; /** @@ -19,17 +18,16 @@ import mage.game.permanent.token.GoblinToken; */ public final class Goblinslide extends CardImpl { - private static final FilterSpell filter = new FilterSpell("noncreature spell"); - - static { - filter.add(Predicates.not(CardType.CREATURE.getPredicate())); - } - public Goblinslide(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{R}"); // Whenever you cast a noncreature spell, you may pay {1}. If you do, create a 1/1 red Goblin creature token with haste. - this.addAbility(new SpellCastControllerTriggeredAbility(new DoIfCostPaid(new CreateTokenEffect(new GoblinToken(true)), new GenericManaCost(1)), filter, false)); + this.addAbility(new SpellCastControllerTriggeredAbility(new DoIfCostPaid( + new CreateTokenEffect(new GoblinToken(true)), + new GenericManaCost(1)), + StaticFilters.FILTER_SPELL_NON_CREATURE, + false + )); } private Goblinslide(final Goblinslide card) { diff --git a/Mage.Sets/src/mage/cards/g/GodEternalBontu.java b/Mage.Sets/src/mage/cards/g/GodEternalBontu.java index 2244e8db4d0..0aacb71005e 100644 --- a/Mage.Sets/src/mage/cards/g/GodEternalBontu.java +++ b/Mage.Sets/src/mage/cards/g/GodEternalBontu.java @@ -38,7 +38,7 @@ public final class GodEternalBontu extends CardImpl { this.toughness = new MageInt(6); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // When God-Eternal Bontu enters the battlefield, sacrifice any number of other permanents, then draw that many cards. this.addAbility(new EntersBattlefieldTriggeredAbility(new GodEternalBontuEffect())); diff --git a/Mage.Sets/src/mage/cards/g/GodsWilling.java b/Mage.Sets/src/mage/cards/g/GodsWilling.java index a70df1c51e4..5710797a9f7 100644 --- a/Mage.Sets/src/mage/cards/g/GodsWilling.java +++ b/Mage.Sets/src/mage/cards/g/GodsWilling.java @@ -22,7 +22,7 @@ public final class GodsWilling extends CardImpl { // Target creature you control gains protection from the color of your choice until end of turn. Scry 1. this.getSpellAbility().addEffect(new GainProtectionFromColorTargetEffect(Duration.EndOfTurn)); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); - this.getSpellAbility().addEffect(new ScryEffect(1).concatBy("
")); + this.getSpellAbility().addEffect(new ScryEffect(1, false).concatBy("
")); } private GodsWilling(final GodsWilling card) { diff --git a/Mage.Sets/src/mage/cards/g/Godsend.java b/Mage.Sets/src/mage/cards/g/Godsend.java index d16f76fdea5..46ea22c8031 100644 --- a/Mage.Sets/src/mage/cards/g/Godsend.java +++ b/Mage.Sets/src/mage/cards/g/Godsend.java @@ -1,4 +1,3 @@ - package mage.cards.g; import java.util.*; @@ -22,7 +21,6 @@ import mage.game.ExileZone; import mage.game.Game; import mage.game.combat.CombatGroup; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCreaturePermanent; @@ -109,7 +107,7 @@ class GodsendTriggeredAbility extends TriggeredAbilityImpl { if (!possibleTargets.isEmpty()) { this.getTargets().clear(); if (possibleTargets.size() == 1) { - this.getEffects().get(0).setTargetPointer(new FixedTarget(possibleTargets.iterator().next())); + this.getEffects().get(0).setTargetPointer(new FixedTarget(possibleTargets.iterator().next(), game)); } else { this.getEffects().get(0).setTargetPointer(new FirstTargetPointer()); targetName = targetName + " equipped by " + equipment.getName(); diff --git a/Mage.Sets/src/mage/cards/g/GodtrackerOfJund.java b/Mage.Sets/src/mage/cards/g/GodtrackerOfJund.java index faef422294f..93561bd5dbe 100644 --- a/Mage.Sets/src/mage/cards/g/GodtrackerOfJund.java +++ b/Mage.Sets/src/mage/cards/g/GodtrackerOfJund.java @@ -27,7 +27,7 @@ public final class GodtrackerOfJund extends CardImpl { filter.add(new PowerPredicate(ComparisonType.MORE_THAN, 4)); } - String rule = "Whenever a creature with power 5 or greater enters the battlefield under your control, you may put a +1/+1 counter on {this}."; + private static final String rule = "Whenever a creature with power 5 or greater enters the battlefield under your control, you may put a +1/+1 counter on {this}."; public GodtrackerOfJund(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{R}{G}"); diff --git a/Mage.Sets/src/mage/cards/g/GoldenGuardian.java b/Mage.Sets/src/mage/cards/g/GoldenGuardian.java index 1cfe74e73d6..6e575c7c147 100644 --- a/Mage.Sets/src/mage/cards/g/GoldenGuardian.java +++ b/Mage.Sets/src/mage/cards/g/GoldenGuardian.java @@ -47,7 +47,6 @@ public final class GoldenGuardian extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(4); - this.transformable = true; this.secondSideCardClazz = GoldForgeGarrison.class; // Defender diff --git a/Mage.Sets/src/mage/cards/g/GoldenTailDisciple.java b/Mage.Sets/src/mage/cards/g/GoldenTailDisciple.java new file mode 100644 index 00000000000..3ac9a973d3e --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GoldenTailDisciple.java @@ -0,0 +1,37 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GoldenTailDisciple extends CardImpl { + + public GoldenTailDisciple(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{2}{W}"); + + this.subtype.add(SubType.FOX); + this.subtype.add(SubType.MONK); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + } + + private GoldenTailDisciple(final GoldenTailDisciple card) { + super(card); + } + + @Override + public GoldenTailDisciple copy() { + return new GoldenTailDisciple(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GoldenWish.java b/Mage.Sets/src/mage/cards/g/GoldenWish.java index b7e35b52e1d..b38b7c2f9a1 100644 --- a/Mage.Sets/src/mage/cards/g/GoldenWish.java +++ b/Mage.Sets/src/mage/cards/g/GoldenWish.java @@ -17,7 +17,7 @@ import mage.filter.predicate.Predicates; */ public final class GoldenWish extends CardImpl { - private static final FilterCard filter = new FilterCard("an artifact or enchantment card"); + private static final FilterCard filter = new FilterCard("artifact or enchantment card"); static { filter.add(Predicates.or( diff --git a/Mage.Sets/src/mage/cards/g/GolgariCharm.java b/Mage.Sets/src/mage/cards/g/GolgariCharm.java index c804f4522d0..7311f917a06 100644 --- a/Mage.Sets/src/mage/cards/g/GolgariCharm.java +++ b/Mage.Sets/src/mage/cards/g/GolgariCharm.java @@ -1,4 +1,3 @@ - package mage.cards.g; import java.util.UUID; @@ -11,7 +10,6 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.common.FilterCreaturePermanent; import mage.target.common.TargetEnchantmentPermanent; /** @@ -25,7 +23,7 @@ public final class GolgariCharm extends CardImpl { // Choose one — All creatures get -1/-1 until end of turn; - this.getSpellAbility().addEffect(new BoostAllEffect(-1,-1, Duration.EndOfTurn,new FilterCreaturePermanent("All creatures"), false)); + this.getSpellAbility().addEffect(new BoostAllEffect(-1, -1, Duration.EndOfTurn)); // or destroy target enchantment; Mode mode = new Mode(); diff --git a/Mage.Sets/src/mage/cards/g/GolgariRaiders.java b/Mage.Sets/src/mage/cards/g/GolgariRaiders.java index d439c9a7cde..939b098ecea 100644 --- a/Mage.Sets/src/mage/cards/g/GolgariRaiders.java +++ b/Mage.Sets/src/mage/cards/g/GolgariRaiders.java @@ -1,22 +1,22 @@ package mage.cards.g; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; import mage.abilities.effects.common.counter.AddCountersSourceEffect; -import mage.constants.SubType; import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.AbilityWord; import mage.constants.CardType; +import mage.constants.SubType; import mage.counters.CounterType; import mage.filter.StaticFilters; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class GolgariRaiders extends CardImpl { @@ -39,10 +39,9 @@ public final class GolgariRaiders extends CardImpl { new CardsInControllerGraveyardCount( StaticFilters.FILTER_CARD_CREATURE ), true - ), "with a +1/+1 counter on it " - + "for each creature card in your graveyard" + ), null, AbilityWord.UNDERGROWTH.formatWord() + "{this} enters the battlefield " + + "with a +1/+1 counter on it for each creature card in your graveyard.", null ); - ability.setAbilityWord(AbilityWord.UNDERGROWTH); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/g/GorMuldrakAmphinologist.java b/Mage.Sets/src/mage/cards/g/GorMuldrakAmphinologist.java index 3a018582a81..ca7194ecd03 100644 --- a/Mage.Sets/src/mage/cards/g/GorMuldrakAmphinologist.java +++ b/Mage.Sets/src/mage/cards/g/GorMuldrakAmphinologist.java @@ -17,6 +17,7 @@ import mage.game.Controllable; import mage.game.Game; import mage.game.permanent.token.SalamnderWarriorToken; import mage.game.permanent.token.Token; +import mage.util.CardUtil; import java.util.HashMap; import java.util.Map; @@ -95,7 +96,7 @@ class GorMuldrakAmphinologistEffect extends OneShotEffect { ).stream() .filter(Objects::nonNull) .map(Controllable::getControllerId) - .forEach(uuid -> creatureMap.compute(uuid, (u, i) -> i == null ? 1 : Integer.sum(i, 1))); + .forEach(uuid -> creatureMap.compute(uuid, CardUtil::setOrIncrementValue)); int minValue = creatureMap.values().stream().mapToInt(x -> x).min().orElse(0); minValue = Math.max(minValue, 0); Token token = new SalamnderWarriorToken(); diff --git a/Mage.Sets/src/mage/cards/g/GorexTheTombshell.java b/Mage.Sets/src/mage/cards/g/GorexTheTombshell.java new file mode 100644 index 00000000000..9e3d5b9fbc6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GorexTheTombshell.java @@ -0,0 +1,177 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.common.ExileXFromYourGraveCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.keyword.DeathtouchAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.ExileZone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.players.Player; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GorexTheTombshell extends CardImpl { + + public GorexTheTombshell(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{6}{B}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.ZOMBIE); + this.subtype.add(SubType.TURTLE); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // As an additional cost to cast this spell, you may exile any number of creature cards from your graveyard. This spell costs {2} less to cast for each card exiled this way. + Cost cost = new ExileXFromYourGraveCost(StaticFilters.FILTER_CARD_CREATURES, true); + cost.setText("as an additional cost to cast this spell, you may exile any number of creature cards " + + "from your graveyard. This spell costs {2} less to cast for each card exiled this way"); + this.getSpellAbility().addCost(cost); + Ability ability = new SimpleStaticAbility(Zone.ALL, new GorexTheTombshellCostReductionEffect()); + ability.setRuleVisible(false); + this.addAbility(ability); + + // Deathtouch + this.addAbility(DeathtouchAbility.getInstance()); + + // Whenever Gorex, the Tombshell attacks or dies, choose a card at random exiled with Gorex and put that card into its owner's hand. + this.addAbility(new GorexTheTombshellTriggeredAbility()); + } + + private GorexTheTombshell(final GorexTheTombshell card) { + super(card); + } + + @Override + public GorexTheTombshell copy() { + return new GorexTheTombshell(this); + } +} + +class GorexTheTombshellCostReductionEffect extends CostModificationEffectImpl { + + GorexTheTombshellCostReductionEffect() { + super(Duration.WhileOnStack, Outcome.Benefit, CostModificationType.REDUCE_COST); + } + + private GorexTheTombshellCostReductionEffect(final GorexTheTombshellCostReductionEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source, Ability abilityToModify) { + SpellAbility spellAbility = (SpellAbility) abilityToModify; + for (Cost cost : spellAbility.getCosts()) { + if (!(cost instanceof ExileXFromYourGraveCost)) { + continue; + } + if (game.inCheckPlayableState()) { + // allows to cast in getPlayable + int reduction = ((ExileXFromYourGraveCost) cost).getMaxValue(spellAbility, game); + CardUtil.adjustCost(spellAbility, reduction * 2); + } else { + // real cast + int reduction = ((ExileXFromYourGraveCost) cost).getAmount(); + CardUtil.adjustCost(spellAbility, reduction * 2); + } + break; + } + return true; + } + + @Override + public boolean applies(Ability abilityToModify, Ability source, Game game) { + return abilityToModify instanceof SpellAbility && abilityToModify.getSourceId().equals(source.getSourceId()); + } + + @Override + public GorexTheTombshellCostReductionEffect copy() { + return new GorexTheTombshellCostReductionEffect(this); + } +} + +class GorexTheTombshellTriggeredAbility extends TriggeredAbilityImpl { + + GorexTheTombshellTriggeredAbility() { + super(Zone.BATTLEFIELD, new GorexTheTombshellReturnEffect()); + } + + private GorexTheTombshellTriggeredAbility(final GorexTheTombshellTriggeredAbility ability) { + super(ability); + } + + @Override + public GorexTheTombshellTriggeredAbility copy() { + return new GorexTheTombshellTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ATTACKER_DECLARED + || event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.ATTACKER_DECLARED) { + return event.getSourceId().equals(getSourceId()); + } + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + return zEvent.isDiesEvent() && event.getTargetId().equals(getSourceId()); + } + + @Override + public boolean isInUseableZone(Game game, MageObject source, GameEvent event) { + return TriggeredAbilityImpl.isInUseableZoneDiesTrigger(this, event, game); + } + + @Override + public String getRule() { + return "Whenever {this} attacks or dies, choose a card at random " + + "exiled with {this} and put that card into its owner's hand."; + } +} + +class GorexTheTombshellReturnEffect extends OneShotEffect { + + GorexTheTombshellReturnEffect() { + super(Outcome.Benefit); + } + + private GorexTheTombshellReturnEffect(final GorexTheTombshellReturnEffect effect) { + super(effect); + } + + @Override + public GorexTheTombshellReturnEffect copy() { + return new GorexTheTombshellReturnEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId( + game, source.getSourceId(), source.getSourceObjectZoneChangeCounter() - 1 + )); + if (player == null || exileZone == null || exileZone.isEmpty()) { + return false; + } + return player.moveCards(exileZone.getRandom(game), Zone.HAND, source, game); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GorgonRecluse.java b/Mage.Sets/src/mage/cards/g/GorgonRecluse.java index f4b99f0d68b..4e9e042078e 100644 --- a/Mage.Sets/src/mage/cards/g/GorgonRecluse.java +++ b/Mage.Sets/src/mage/cards/g/GorgonRecluse.java @@ -1,9 +1,7 @@ - package mage.cards.g; import java.util.UUID; import mage.MageInt; -import mage.ObjectColor; import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; @@ -15,9 +13,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ColorPredicate; +import mage.filter.StaticFilters; /** * @@ -25,12 +21,6 @@ import mage.filter.predicate.mageobject.ColorPredicate; */ public final class GorgonRecluse extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public GorgonRecluse(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{B}"); this.subtype.add(SubType.GORGON); @@ -40,7 +30,7 @@ public final class GorgonRecluse extends CardImpl { // Whenever Gorgon Recluse blocks or becomes blocked by a nonblack creature, destroy that creature at end of combat. Effect effect = new CreateDelayedTriggeredAbilityEffect(new AtTheEndOfCombatDelayedTriggeredAbility(new DestroyTargetEffect()), true); effect.setText("destroy that creature at end of combat"); - this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(effect, filter, false)); + this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(effect, StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK, false)); // Madness {B}{B} this.addAbility(new MadnessAbility(this, new ManaCostsImpl("{B}{B}"))); diff --git a/Mage.Sets/src/mage/cards/g/GoroGoroDiscipleOfRyusei.java b/Mage.Sets/src/mage/cards/g/GoroGoroDiscipleOfRyusei.java new file mode 100644 index 00000000000..7f6283b5865 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GoroGoroDiscipleOfRyusei.java @@ -0,0 +1,74 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.decorator.ConditionalActivatedAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.continuous.GainAbilityAllEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.permanent.AttackingPredicate; +import mage.filter.predicate.permanent.ModifiedPredicate; +import mage.game.permanent.token.DragonSpiritToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GoroGoroDiscipleOfRyusei extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("if you control an attacking modified creature"); + + static { + filter.add(AttackingPredicate.instance); + filter.add(ModifiedPredicate.instance); + } + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter); + private static final Hint hint = new ConditionHint( + condition, "You control and attacking modified creature" + ); + + public GoroGoroDiscipleOfRyusei(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.GOBLIN); + this.subtype.add(SubType.SAMURAI); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // {R}: Creatures you control gain haste until end of turn. + this.addAbility(new SimpleActivatedAbility(new GainAbilityAllEffect( + HasteAbility.getInstance(), Duration.EndOfTurn, + StaticFilters.FILTER_CONTROLLED_CREATURES + ), new ManaCostsImpl<>("{R}"))); + + // {3}{R}{R}: Create a 5/5 red Dragon Spirit creature token with flying. Activate only if you control an attacking modified creature. + this.addAbility(new ConditionalActivatedAbility( + Zone.BATTLEFIELD, new CreateTokenEffect(new DragonSpiritToken()), + new ManaCostsImpl<>("{3}{R}{R}"), condition + ).addHint(hint)); + } + + private GoroGoroDiscipleOfRyusei(final GoroGoroDiscipleOfRyusei card) { + super(card); + } + + @Override + public GoroGoroDiscipleOfRyusei copy() { + return new GoroGoroDiscipleOfRyusei(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GrafReaver.java b/Mage.Sets/src/mage/cards/g/GrafReaver.java new file mode 100644 index 00000000000..27af345ea7b --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GrafReaver.java @@ -0,0 +1,54 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.ExploitCreatureTriggeredAbility; +import mage.abilities.effects.common.DamageControllerEffect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.keyword.ExploitAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.target.common.TargetPlaneswalkerPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GrafReaver extends CardImpl { + + public GrafReaver(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}"); + + this.subtype.add(SubType.ZOMBIE); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Exploit + this.addAbility(new ExploitAbility()); + + // When Graf Reaver exploits a creature, destroy target planeswalker. + Ability ability = new ExploitCreatureTriggeredAbility(new DestroyTargetEffect()); + ability.addTarget(new TargetPlaneswalkerPermanent()); + this.addAbility(ability); + + // At the beginning of your upkeep, Graf Reaver deals 1 damage to you. + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new DamageControllerEffect(1), TargetController.YOU, false + )); + } + + private GrafReaver(final GrafReaver card) { + super(card); + } + + @Override + public GrafReaver copy() { + return new GrafReaver(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GraftedGrowth.java b/Mage.Sets/src/mage/cards/g/GraftedGrowth.java new file mode 100644 index 00000000000..892d2f595bd --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GraftedGrowth.java @@ -0,0 +1,72 @@ +package mage.cards.g; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.effects.mana.AddManaOfAnyColorEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.abilities.mana.SimpleManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.target.TargetPermanent; +import mage.target.common.TargetLandPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GraftedGrowth extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledPermanent("creature or Vehicle you control"); + + static { + filter.add(Predicates.or( + CardType.CREATURE.getPredicate(), + SubType.VEHICLE.getPredicate() + )); + } + + public GraftedGrowth(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}"); + + this.subtype.add(SubType.AURA); + + // Enchant land + TargetPermanent auraTarget = new TargetLandPermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + this.addAbility(new EnchantAbility(auraTarget.getTargetName())); + + // When Grafted Growth enters the battlefield, put a +1/+1 counter on target creature or Vehicle you control. + Ability ability = new EntersBattlefieldTriggeredAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance()) + ); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + + // Enchanted land has "{T}: Add two mana of any one color." + this.addAbility(new SimpleStaticAbility(new GainAbilityAttachedEffect(new SimpleManaAbility( + Zone.BATTLEFIELD, new AddManaOfAnyColorEffect(2), new TapSourceCost() + ), AttachmentType.AURA).setText("enchanted land has \"{T}: Add two mana of any one color.\""))); + } + + private GraftedGrowth(final GraftedGrowth card) { + super(card); + } + + @Override + public GraftedGrowth copy() { + return new GraftedGrowth(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GrandMasterOfFlowers.java b/Mage.Sets/src/mage/cards/g/GrandMasterOfFlowers.java index 14815a56916..623682eb1a4 100644 --- a/Mage.Sets/src/mage/cards/g/GrandMasterOfFlowers.java +++ b/Mage.Sets/src/mage/cards/g/GrandMasterOfFlowers.java @@ -2,7 +2,6 @@ package mage.cards.g; 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.common.combat.CantAttackTargetEffect; @@ -47,7 +46,7 @@ public final class GrandMasterOfFlowers extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.BAHAMUT); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // As long as Grand Master of Flowers has seven or more loyalty counters on him, he's a 7/7 Dragon God creature with flying and indestructible. this.addAbility(new SimpleStaticAbility(new GrandMasterOfFlowersEffect())); diff --git a/Mage.Sets/src/mage/cards/g/GraniticTitan.java b/Mage.Sets/src/mage/cards/g/GraniticTitan.java index 5bfcebdcfda..816ce3dc927 100644 --- a/Mage.Sets/src/mage/cards/g/GraniticTitan.java +++ b/Mage.Sets/src/mage/cards/g/GraniticTitan.java @@ -19,7 +19,7 @@ public final class GraniticTitan extends CardImpl { toughness = new MageInt(4); // Menace - addAbility(new MenaceAbility()); + addAbility(new MenaceAbility(false)); // Cycling {2} addAbility(new CyclingAbility(new ManaCostsImpl<>("{2}"))); diff --git a/Mage.Sets/src/mage/cards/g/GraveConsequences.java b/Mage.Sets/src/mage/cards/g/GraveConsequences.java new file mode 100644 index 00000000000..68cbdd79372 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GraveConsequences.java @@ -0,0 +1,90 @@ +package mage.cards.g; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.List; +import java.util.Objects; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class GraveConsequences extends CardImpl { + + public GraveConsequences(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{B}"); + + // Each player may exile any number of cards from their graveyard. Then each player loses 1 life for each card in their graveyard. + this.getSpellAbility().addEffect(new GraveConsequencesEffect()); + + // Draw a card. + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("
")); + } + + private GraveConsequences(final GraveConsequences card) { + super(card); + } + + @Override + public GraveConsequences copy() { + return new GraveConsequences(this); + } +} + +class GraveConsequencesEffect extends OneShotEffect { + + GraveConsequencesEffect() { + super(Outcome.Exile); + staticText = "each player may exile any number of cards from their graveyard. " + + "Then each player loses 1 life for each card in their graveyard"; + } + + private GraveConsequencesEffect(final GraveConsequencesEffect effect) { + super(effect); + } + + @Override + public GraveConsequencesEffect copy() { + return new GraveConsequencesEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + List players = game + .getState() + .getPlayersInRange(source.getControllerId(), game) + .stream() + .map(game::getPlayer) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + for (Player player : players) { + TargetCard target = new TargetCardInYourGraveyard(0, Integer.MAX_VALUE); + target.setNotTarget(true); + player.choose(outcome, target, source.getSourceId(), game); + Cards cards = new CardsImpl(target.getTargets()); + if (!cards.isEmpty()) { + player.moveCards(cards, Zone.EXILED, source, game); + } + } + for (Player player : players) { + if (!player.getGraveyard().isEmpty()) { + player.loseLife(player.getLife(), game, source, false); + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/g/GravePeril.java b/Mage.Sets/src/mage/cards/g/GravePeril.java index 58b2baa69da..111fe52a78b 100644 --- a/Mage.Sets/src/mage/cards/g/GravePeril.java +++ b/Mage.Sets/src/mage/cards/g/GravePeril.java @@ -1,8 +1,6 @@ - package mage.cards.g; import java.util.UUID; -import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; import mage.abilities.effects.Effect; @@ -14,9 +12,7 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SetTargetPointer; import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ColorPredicate; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; @@ -25,17 +21,12 @@ import mage.game.permanent.Permanent; * @author emerald000 */ public final class GravePeril extends CardImpl { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a nonblack creature"); - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } public GravePeril(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{B}"); // When a nonblack creature enters the battlefield, sacrifice Grave Peril. If you do, destroy that creature. - this.addAbility(new EntersBattlefieldAllTriggeredAbility(Zone.BATTLEFIELD, new GravePerilEffect(), filter, false, SetTargetPointer.PERMANENT, null)); + this.addAbility(new EntersBattlefieldAllTriggeredAbility(Zone.BATTLEFIELD, new GravePerilEffect(), StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK, false, SetTargetPointer.PERMANENT, null).setTriggerPhrase("When a nonblack creature enters the battlefield, ")); } private GravePeril(final GravePeril card) { @@ -49,21 +40,21 @@ public final class GravePeril extends CardImpl { } class GravePerilEffect extends OneShotEffect { - + GravePerilEffect() { super(Outcome.DestroyPermanent); this.staticText = "sacrifice Grave Peril. If you do, destroy that creature"; } - + GravePerilEffect(final GravePerilEffect effect) { super(effect); } - + @Override public GravePerilEffect copy() { return new GravePerilEffect(this); } - + @Override public boolean apply(Game game, Ability source) { Permanent permanent = (Permanent) source.getSourceObjectIfItStillExists(game); diff --git a/Mage.Sets/src/mage/cards/g/Gravelighter.java b/Mage.Sets/src/mage/cards/g/Gravelighter.java new file mode 100644 index 00000000000..1746e2f8631 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/Gravelighter.java @@ -0,0 +1,51 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.common.MorbidCondition; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.SacrificeAllEffect; +import mage.abilities.hint.common.MorbidHint; +import mage.abilities.keyword.FlyingAbility; +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 Gravelighter extends CardImpl { + + public Gravelighter(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When Gravelighter enters the battlefield, draw a card if a creature died this turn. Otherwise, each player sacrifices a creature. + this.addAbility(new EntersBattlefieldTriggeredAbility(new ConditionalOneShotEffect( + new DrawCardSourceControllerEffect(1), + new SacrificeAllEffect(1, StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT), + MorbidCondition.instance, "draw a card if a creature died this turn. " + + "Otherwise, each player sacrifices a creature" + )).addHint(MorbidHint.instance)); + } + + private Gravelighter(final Gravelighter card) { + super(card); + } + + @Override + public Gravelighter copy() { + return new Gravelighter(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/Gravepurge.java b/Mage.Sets/src/mage/cards/g/Gravepurge.java index 503cb18eaaf..78684394a82 100644 --- a/Mage.Sets/src/mage/cards/g/Gravepurge.java +++ b/Mage.Sets/src/mage/cards/g/Gravepurge.java @@ -1,4 +1,3 @@ - package mage.cards.g; import java.util.UUID; @@ -7,7 +6,7 @@ import mage.abilities.effects.common.PutOnLibraryTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterCreatureCard; +import mage.filter.StaticFilters; import mage.target.common.TargetCardInYourGraveyard; /** @@ -19,10 +18,9 @@ public final class Gravepurge extends CardImpl { public Gravepurge(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{B}"); - // Put any number of target creature cards from your graveyard on top of your library. this.getSpellAbility().addEffect(new PutOnLibraryTargetEffect(true)); - this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, Integer.MAX_VALUE, new FilterCreatureCard("creature cards from your graveyard"))); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, Integer.MAX_VALUE, StaticFilters.FILTER_CARD_CREATURES_YOUR_GRAVEYARD)); // Draw a card. this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); } diff --git a/Mage.Sets/src/mage/cards/g/GraveyardGlutton.java b/Mage.Sets/src/mage/cards/g/GraveyardGlutton.java index 8cd92dd191b..2fd570828e3 100644 --- a/Mage.Sets/src/mage/cards/g/GraveyardGlutton.java +++ b/Mage.Sets/src/mage/cards/g/GraveyardGlutton.java @@ -35,7 +35,6 @@ public final class GraveyardGlutton extends CardImpl { this.toughness = new MageInt(4); this.color.setBlack(true); this.nightCard = true; - this.transformable = true; // Ward—Discard a card. this.addAbility(new WardAbility(new DiscardCardCost())); diff --git a/Mage.Sets/src/mage/cards/g/GraveyardTrespasser.java b/Mage.Sets/src/mage/cards/g/GraveyardTrespasser.java index 4b505e9d417..bec1aa22fc4 100644 --- a/Mage.Sets/src/mage/cards/g/GraveyardTrespasser.java +++ b/Mage.Sets/src/mage/cards/g/GraveyardTrespasser.java @@ -6,7 +6,6 @@ import mage.abilities.common.EntersBattlefieldOrAttacksSourceTriggeredAbility; import mage.abilities.costs.common.DiscardCardCost; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.DayboundAbility; -import mage.abilities.keyword.TransformAbility; import mage.abilities.keyword.WardAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -35,7 +34,6 @@ public final class GraveyardTrespasser extends CardImpl { this.subtype.add(SubType.WEREWOLF); this.power = new MageInt(3); this.toughness = new MageInt(3); - this.transformable = true; this.secondSideCardClazz = mage.cards.g.GraveyardGlutton.class; // Ward—Discard a card. @@ -47,7 +45,6 @@ public final class GraveyardTrespasser extends CardImpl { this.addAbility(ability); // Daybound - this.addAbility(new TransformAbility()); this.addAbility(new DayboundAbility()); } diff --git a/Mage.Sets/src/mage/cards/g/GravitySphere.java b/Mage.Sets/src/mage/cards/g/GravitySphere.java index 702c1e38c5d..77ff21ad3d9 100644 --- a/Mage.Sets/src/mage/cards/g/GravitySphere.java +++ b/Mage.Sets/src/mage/cards/g/GravitySphere.java @@ -1,4 +1,3 @@ - package mage.cards.g; import java.util.UUID; @@ -11,8 +10,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SuperType; -import mage.constants.Zone; -import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; /** * @@ -20,20 +18,14 @@ import mage.filter.FilterPermanent; */ public final class GravitySphere extends CardImpl { - static final private FilterPermanent filter = new FilterPermanent("All creatures"); - - static { - filter.add(CardType.CREATURE.getPredicate()); - } - public GravitySphere(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{R}"); addSuperType(SuperType.WORLD); // All creatures lose flying. - Effect effect = new LoseAbilityAllEffect(FlyingAbility.getInstance(), Duration.WhileOnBattlefield, filter); + Effect effect = new LoseAbilityAllEffect(FlyingAbility.getInstance(), Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_ALL_CREATURES); effect.setText("All creatures lose flying"); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); + this.addAbility(new SimpleStaticAbility(effect)); } diff --git a/Mage.Sets/src/mage/cards/g/GravityWell.java b/Mage.Sets/src/mage/cards/g/GravityWell.java index ab210b70161..8cede16b162 100644 --- a/Mage.Sets/src/mage/cards/g/GravityWell.java +++ b/Mage.Sets/src/mage/cards/g/GravityWell.java @@ -59,7 +59,7 @@ class GravityWellTriggeredAbility extends TriggeredAbilityImpl { Permanent attacker = game.getPermanent(event.getSourceId()); if (attacker != null && attacker.getAbilities().contains(FlyingAbility.getInstance())) { for (Effect effect : getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getSourceId())); + effect.setTargetPointer(new FixedTarget(event.getSourceId(), game)); } return true; } diff --git a/Mage.Sets/src/mage/cards/g/Graxiplon.java b/Mage.Sets/src/mage/cards/g/Graxiplon.java new file mode 100644 index 00000000000..c6c2ede6f3b --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/Graxiplon.java @@ -0,0 +1,58 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalRestrictionEffect; +import mage.abilities.dynamicvalue.common.GreatestSharedCreatureTypeCount; +import mage.abilities.effects.common.combat.CantBeBlockedSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.Game; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Graxiplon extends CardImpl { + + public Graxiplon(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{U}"); + + this.subtype.add(SubType.BEAST); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // Graxiplon can't be blocked unless defending player controls three or more creatures that share a creature type. + this.addAbility(new SimpleStaticAbility(new ConditionalRestrictionEffect( + new CantBeBlockedSourceEffect(), GraxiplonCondition.instance, "{this} can't be blocked " + + "unless defending player controls three or more creatures that share a creature type" + ))); + } + + private Graxiplon(final Graxiplon card) { + super(card); + } + + @Override + public Graxiplon copy() { + return new Graxiplon(this); + } +} + +enum GraxiplonCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(game.getCombat().getDefendingPlayerId(source.getSourceId(), game)); + return player == null || GreatestSharedCreatureTypeCount.getValue( + player.getId(), source.getSourceId(), game + ) < 3; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/g/GreasefangOkibaBoss.java b/Mage.Sets/src/mage/cards/g/GreasefangOkibaBoss.java new file mode 100644 index 00000000000..8226f4924c7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GreasefangOkibaBoss.java @@ -0,0 +1,97 @@ +package mage.cards.g; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfCombatTriggeredAbility; +import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.HasteAbility; +import mage.cards.Card; +import mage.constants.*; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.filter.FilterCard; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCardInYourGraveyard; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author weirddan455 + */ +public final class GreasefangOkibaBoss extends CardImpl { + + private static final FilterCard filter = new FilterCard("Vehicle card from your graveyard"); + + static { + filter.add(SubType.VEHICLE.getPredicate()); + } + + public GreasefangOkibaBoss(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.RAT); + this.subtype.add(SubType.PILOT); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + + // At the beginning of combat on your turn, return target Vehicle card from your graveyard to the battlefield. It gains haste. Return it your hand at beginning of the next end step. + Ability ability = new BeginningOfCombatTriggeredAbility(new GreasefangOkibaBossEffect(), TargetController.YOU, false); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + this.addAbility(ability); + } + + private GreasefangOkibaBoss(final GreasefangOkibaBoss card) { + super(card); + } + + @Override + public GreasefangOkibaBoss copy() { + return new GreasefangOkibaBoss(this); + } +} + +class GreasefangOkibaBossEffect extends OneShotEffect { + + public GreasefangOkibaBossEffect() { + super(Outcome.PutCardInPlay); + this.staticText = "return target Vehicle card from your graveyard to the battlefield. It gains haste. Return it to its owner's hand at the beginning of your next end step"; + } + + private GreasefangOkibaBossEffect(final GreasefangOkibaBossEffect effect) { + super(effect); + } + + @Override + public GreasefangOkibaBossEffect copy() { + return new GreasefangOkibaBossEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Card card = game.getCard(source.getFirstTarget()); + if (controller == null || card == null || game.getState().getZone(card.getId()) != Zone.GRAVEYARD) { + return false; + } + controller.moveCards(card, Zone.BATTLEFIELD, source, game); + Permanent permanent = game.getPermanent(card.getId()); + if (permanent != null) { + ContinuousEffect hasteEffect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.Custom); + hasteEffect.setTargetPointer(new FixedTarget(permanent, game)); + game.addEffect(hasteEffect, source); + Effect bounceEffect = new ReturnToHandTargetEffect().setText("return it to your hand"); + bounceEffect.setTargetPointer(new FixedTarget(permanent, game)); + game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(bounceEffect), source); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/g/GreaterTanuki.java b/Mage.Sets/src/mage/cards/g/GreaterTanuki.java new file mode 100644 index 00000000000..8e068cc2e72 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GreaterTanuki.java @@ -0,0 +1,45 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; +import mage.abilities.keyword.ChannelAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.target.common.TargetCardInLibrary; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GreaterTanuki extends CardImpl { + + public GreaterTanuki(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{4}{G}{G}"); + + this.subtype.add(SubType.DOG); + this.power = new MageInt(6); + this.toughness = new MageInt(5); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Channel — {2}{G}, Discard Greater Tanuki: Search your library for a basic land card, put it onto the battlefield tapped, then shuffle. + this.addAbility(new ChannelAbility("{2}{G}", new SearchLibraryPutInPlayEffect( + new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), true + ))); + } + + private GreaterTanuki(final GreaterTanuki card) { + super(card); + } + + @Override + public GreaterTanuki copy() { + return new GreaterTanuki(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GreenWard.java b/Mage.Sets/src/mage/cards/g/GreenWard.java index 7d0782ee67f..ecbd3d18eea 100644 --- a/Mage.Sets/src/mage/cards/g/GreenWard.java +++ b/Mage.Sets/src/mage/cards/g/GreenWard.java @@ -1,10 +1,7 @@ - package mage.cards.g; -import java.util.UUID; import mage.ObjectColor; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; import mage.abilities.keyword.EnchantAbility; @@ -13,28 +10,20 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.AttachmentType; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; -import mage.constants.Zone; -import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.ColorPredicate; +import mage.constants.SubType; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LoneFox */ public final class GreenWard extends CardImpl { - private static final FilterCard filter = new FilterCard("green"); - - static { - filter.add(new ColorPredicate(ObjectColor.GREEN)); - } - public GreenWard(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}"); this.subtype.add(SubType.AURA); // Enchant creature @@ -42,12 +31,11 @@ public final class GreenWard extends CardImpl { this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.Protect)); this.addAbility(new EnchantAbility(auraTarget.getTargetName())); + // Enchanted creature has protection from green. This effect doesn't remove Green Ward. - ProtectionAbility gainedAbility = new ProtectionAbility(filter); - gainedAbility.setAuraIdNotToBeRemoved(this.getId()); - Effect effect = new GainAbilityAttachedEffect(gainedAbility, AttachmentType.AURA); - effect.setText("Enchanted creature has protection from green. This effect doesn't remove {this}."); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); + this.addAbility(new SimpleStaticAbility(new GainAbilityAttachedEffect( + ProtectionAbility.from(ObjectColor.GREEN), AttachmentType.AURA + ).setDoesntRemoveItself(true))); } private GreenWard(final GreenWard card) { diff --git a/Mage.Sets/src/mage/cards/g/GremlinInfestation.java b/Mage.Sets/src/mage/cards/g/GremlinInfestation.java index c99b79595aa..d0b7af76ebb 100644 --- a/Mage.Sets/src/mage/cards/g/GremlinInfestation.java +++ b/Mage.Sets/src/mage/cards/g/GremlinInfestation.java @@ -39,9 +39,7 @@ public final class GremlinInfestation extends CardImpl { this.addAbility(ability); // At the beginning of your end step, Gremlin Infestation deals 2 damage to enchanted artifact's controller. - Effect effect = new DamageAttachedControllerEffect(2); - effect.setText("{this} deals 2 damage to enchanted artifact's controller"); - this.addAbility(new BeginningOfEndStepTriggeredAbility(new DamageAttachedControllerEffect(2), TargetController.YOU, false)); + this.addAbility(new BeginningOfEndStepTriggeredAbility(new DamageAttachedControllerEffect(2).setText("{this} deals 2 damage to enchanted artifact's controller"), TargetController.YOU, false)); // When enchanted artifact is put into a graveyard, create a 2/2 red Gremlin creature token. this.addAbility(new DiesAttachedTriggeredAbility(new CreateTokenEffect(new GremlinToken()), "enchanted artifact", false, false)); diff --git a/Mage.Sets/src/mage/cards/g/GrevenPredatorCaptain.java b/Mage.Sets/src/mage/cards/g/GrevenPredatorCaptain.java index 1d3da75d417..5e4ff833ca6 100644 --- a/Mage.Sets/src/mage/cards/g/GrevenPredatorCaptain.java +++ b/Mage.Sets/src/mage/cards/g/GrevenPredatorCaptain.java @@ -38,7 +38,7 @@ public final class GrevenPredatorCaptain extends CardImpl { this.toughness = new MageInt(5); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // Greven, Predator Captain gets +X/+0, where X is the amount of life you've lost this turn. this.addAbility(new SimpleStaticAbility(new BoostSourceEffect( diff --git a/Mage.Sets/src/mage/cards/g/Grief.java b/Mage.Sets/src/mage/cards/g/Grief.java index d0ac0eef647..8c2a87c0f7c 100644 --- a/Mage.Sets/src/mage/cards/g/Grief.java +++ b/Mage.Sets/src/mage/cards/g/Grief.java @@ -41,7 +41,7 @@ public final class Grief extends CardImpl { this.toughness = new MageInt(2); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // When Grief enters the battlefield, target opponent reveals their hand. You choose a nonland card from it. That player discards that card. Ability ability = new EntersBattlefieldTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/g/GriffinCanyon.java b/Mage.Sets/src/mage/cards/g/GriffinCanyon.java index f94188cc5b6..adf6d802a3a 100644 --- a/Mage.Sets/src/mage/cards/g/GriffinCanyon.java +++ b/Mage.Sets/src/mage/cards/g/GriffinCanyon.java @@ -12,7 +12,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.FilterPermanent; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; @@ -47,8 +47,6 @@ public final class GriffinCanyon extends CardImpl { } class GriffinCanyonEffect extends OneShotEffect { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature"); public GriffinCanyonEffect() { super(Outcome.Benefit); @@ -69,7 +67,7 @@ class GriffinCanyonEffect extends OneShotEffect { Permanent targetCreature = game.getPermanent(source.getFirstTarget()); if (targetCreature != null) { targetCreature.untap(game); - if (filter.match(targetCreature, game)) { + if (StaticFilters.FILTER_PERMANENT_A_CREATURE.match(targetCreature, game)) { game.addEffect(new BoostTargetEffect(1, 1, Duration.EndOfTurn), source); } return true; diff --git a/Mage.Sets/src/mage/cards/g/GrimContest.java b/Mage.Sets/src/mage/cards/g/GrimContest.java index 69e7b42a215..c77c1722b18 100644 --- a/Mage.Sets/src/mage/cards/g/GrimContest.java +++ b/Mage.Sets/src/mage/cards/g/GrimContest.java @@ -8,8 +8,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -22,19 +21,13 @@ import mage.target.common.TargetControlledCreaturePermanent; */ public final class GrimContest extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public GrimContest(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{B}{G}"); // Choose target creature you control and target creature an opponent controls. Each of those creatures deals damage equal to its toughness to the other. this.getSpellAbility().addEffect(new GrimContestEffect()); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); } private GrimContest(final GrimContest card) { diff --git a/Mage.Sets/src/mage/cards/g/GrimDraugr.java b/Mage.Sets/src/mage/cards/g/GrimDraugr.java index 71bf3f2dfb2..7fa16db81c2 100644 --- a/Mage.Sets/src/mage/cards/g/GrimDraugr.java +++ b/Mage.Sets/src/mage/cards/g/GrimDraugr.java @@ -32,10 +32,13 @@ public final class GrimDraugr extends CardImpl { // {1}{S}: Grim Draugr gets +1/+0 and gains menace until end of turn. Ability ability = new SimpleActivatedAbility( - new BoostSourceEffect(1, 0, Duration.EndOfTurn).setText("{this} gets +1/+0"), new ManaCostsImpl("{1}{S}") + new BoostSourceEffect(1, 0, Duration.EndOfTurn).setText("{this} gets +1/+0"), + new ManaCostsImpl("{1}{S}") ); ability.addEffect(new GainAbilitySourceEffect( - new MenaceAbility(), Duration.EndOfTurn).setText("and gains menace until end of turn") + new MenaceAbility(), Duration.EndOfTurn).setText("and gains menace until end of turn. " + + "(It can't be blocked except by two or more creatures. " + + "{S} can be paid with one mana from a snow source.)") ); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/g/GrimLavamancer.java b/Mage.Sets/src/mage/cards/g/GrimLavamancer.java index 621dca5706a..c9fb81b1001 100644 --- a/Mage.Sets/src/mage/cards/g/GrimLavamancer.java +++ b/Mage.Sets/src/mage/cards/g/GrimLavamancer.java @@ -15,7 +15,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.ColoredManaSymbol; import mage.constants.Zone; -import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.target.common.TargetCardInYourGraveyard; import mage.target.common.TargetAnyTarget; @@ -35,7 +35,7 @@ public final class GrimLavamancer extends CardImpl { Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(2), new ColoredManaCost(ColoredManaSymbol.R)); ability.addCost(new TapSourceCost()); - ability.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard(2, new FilterCard("cards from your graveyard")))); + ability.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard(2, StaticFilters.FILTER_CARDS_FROM_YOUR_GRAVEYARD))); ability.addTarget(new TargetAnyTarget()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/g/GrimTutor.java b/Mage.Sets/src/mage/cards/g/GrimTutor.java index 4ca8bd99de4..bb8c3f5d687 100644 --- a/Mage.Sets/src/mage/cards/g/GrimTutor.java +++ b/Mage.Sets/src/mage/cards/g/GrimTutor.java @@ -1,7 +1,5 @@ - package mage.cards.g; -import java.util.UUID; import mage.abilities.effects.common.LoseLifeSourceControllerEffect; import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; import mage.cards.CardImpl; @@ -9,22 +7,21 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.target.common.TargetCardInLibrary; +import java.util.UUID; + /** - * * @author emerald000 */ public final class GrimTutor extends CardImpl { public GrimTutor(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{B}{B}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}{B}"); // Search your library for a card and put that card into your hand, then shuffle your library. - TargetCardInLibrary target = new TargetCardInLibrary(); - this.getSpellAbility().addEffect(new SearchLibraryPutInHandEffect(target).setText("search your library for a card and put that card into your hand, then shuffle")); + this.getSpellAbility().addEffect(new SearchLibraryPutInHandEffect(new TargetCardInLibrary())); + // You lose 3 life. this.getSpellAbility().addEffect(new LoseLifeSourceControllerEffect(3)); - } private GrimTutor(final GrimTutor card) { diff --git a/Mage.Sets/src/mage/cards/g/GrimlockDinobotLeader.java b/Mage.Sets/src/mage/cards/g/GrimlockDinobotLeader.java index d569fee265c..94c472489d7 100644 --- a/Mage.Sets/src/mage/cards/g/GrimlockDinobotLeader.java +++ b/Mage.Sets/src/mage/cards/g/GrimlockDinobotLeader.java @@ -46,8 +46,7 @@ public final class GrimlockDinobotLeader extends CardImpl{ this.subtype.add(SubType.AUTOBOT); this.power = new MageInt(4); this.toughness = new MageInt(4); - - this.transformable = true; + this.secondSideCardClazz = GrimlockFerociousKing.class; // Dinosaurs, Vehicles and other Transformers creatures you control get +2/+0. @@ -56,7 +55,7 @@ public final class GrimlockDinobotLeader extends CardImpl{ // {2}: Grimlock, Dinobot Leader becomes Grimlock, Ferocious King. this.addAbility(new TransformAbility()); - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new TransformSourceEffect(true), new ManaCostsImpl("{2}"))); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new TransformSourceEffect(), new ManaCostsImpl("{2}"))); } private GrimlockDinobotLeader(final GrimlockDinobotLeader card) { diff --git a/Mage.Sets/src/mage/cards/g/GrimlockFerociousKing.java b/Mage.Sets/src/mage/cards/g/GrimlockFerociousKing.java index ca8ba185e9a..5b16978b087 100644 --- a/Mage.Sets/src/mage/cards/g/GrimlockFerociousKing.java +++ b/Mage.Sets/src/mage/cards/g/GrimlockFerociousKing.java @@ -31,15 +31,14 @@ public final class GrimlockFerociousKing extends CardImpl{ this.color.setGreen(true); this.color.setWhite(true); - this.transformable = true; - this.nightCard = true; + this.nightCard = true; // Trample this.addAbility(TrampleAbility.getInstance()); // {2}: Grimlock, Ferocious King becomes Grimlock, Dinobot Leader. this.addAbility(new TransformAbility()); - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new TransformSourceEffect(false), new ManaCostsImpl("{2}"))); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new TransformSourceEffect(), new ManaCostsImpl("{2}"))); } private GrimlockFerociousKing(final GrimlockFerociousKing card) { diff --git a/Mage.Sets/src/mage/cards/g/GrimoireOfTheDead.java b/Mage.Sets/src/mage/cards/g/GrimoireOfTheDead.java index b9a809e0e37..9ef267f950d 100644 --- a/Mage.Sets/src/mage/cards/g/GrimoireOfTheDead.java +++ b/Mage.Sets/src/mage/cards/g/GrimoireOfTheDead.java @@ -1,13 +1,13 @@ - package mage.cards.g; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.CompositeCost; import mage.abilities.costs.common.DiscardTargetCost; import mage.abilities.costs.common.RemoveCountersSourceCost; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; @@ -16,10 +16,12 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.counters.CounterType; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInHand; +import mage.target.targetpointer.FixedTargets; import java.util.LinkedHashSet; import java.util.Set; @@ -35,17 +37,20 @@ public final class GrimoireOfTheDead extends CardImpl { addSuperType(SuperType.LEGENDARY); // {1}, {tap}, Discard a card: Put a study counter on Grimoire of the Dead. - Ability ability1 = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.STUDY.createInstance()), new ManaCostsImpl("{1}")); + Ability ability1 = new SimpleActivatedAbility( + new AddCountersSourceEffect(CounterType.STUDY.createInstance()), new GenericManaCost(1) + ); ability1.addCost(new TapSourceCost()); ability1.addCost(new DiscardTargetCost(new TargetCardInHand())); this.addAbility(ability1); // {tap}, Remove three study counters from Grimoire of the Dead and sacrifice it: Put all creature cards from all graveyards onto the battlefield under your control. They're black Zombies in addition to their other colors and types. - Ability ability2 = new SimpleActivatedAbility(Zone.BATTLEFIELD, new GrimoireOfTheDeadEffect(), new TapSourceCost()); - ability2.addCost(new RemoveCountersSourceCost(CounterType.STUDY.createInstance(3))); - ability2.addCost(new SacrificeSourceCost()); + Ability ability2 = new SimpleActivatedAbility(new GrimoireOfTheDeadEffect(), new TapSourceCost()); + ability2.addCost(new CompositeCost( + new RemoveCountersSourceCost(CounterType.STUDY.createInstance(3)), + new SacrificeSourceCost(), "Remove three study counters from {this} and sacrifice it" + )); this.addAbility(ability2); - } private GrimoireOfTheDead(final GrimoireOfTheDead card) { @@ -62,7 +67,8 @@ class GrimoireOfTheDeadEffect extends OneShotEffect { public GrimoireOfTheDeadEffect() { super(Outcome.PutCreatureInPlay); - staticText = "Put all creature cards in all graveyards onto the battlefield under your control. They're black Zombies in addition to their other colors and types"; + staticText = "Put all creature cards in all graveyards onto the battlefield under your control. " + + "They're black Zombies in addition to their other colors and types"; } public GrimoireOfTheDeadEffect(final GrimoireOfTheDeadEffect effect) { @@ -72,20 +78,20 @@ class GrimoireOfTheDeadEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Set creatureCards = new LinkedHashSet<>(); - for (Player player : game.getPlayers().values()) { - for (Card card : player.getGraveyard().getCards(game)) { - if (card.isCreature(game)) { - creatureCards.add(card); - game.addEffect(new GrimoireOfTheDeadEffect2(card.getId()), source); - } - } - } - controller.moveCards(creatureCards, Zone.BATTLEFIELD, source, game, false, false, false, null); - return true; + if (controller == null) { + return false; } - return false; + Set creatureCards = new LinkedHashSet<>(); + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + if (player == null) { + continue; + } + creatureCards.addAll(player.getGraveyard().getCards(StaticFilters.FILTER_CARD_CREATURE, game)); + } + controller.moveCards(creatureCards, Zone.BATTLEFIELD, source, game); + game.addEffect(new GrimoireOfTheDeadEffect2().setTargetPointer(new FixedTargets(creatureCards, game)), source); + return true; } @Override @@ -97,17 +103,12 @@ class GrimoireOfTheDeadEffect extends OneShotEffect { class GrimoireOfTheDeadEffect2 extends ContinuousEffectImpl { - private final UUID targetId; - - public GrimoireOfTheDeadEffect2(UUID targetId) { + public GrimoireOfTheDeadEffect2() { super(Duration.Custom, Outcome.Neutral); - this.targetId = targetId; - staticText = "Becomes a black Zombie in addition to its other colors and types"; } public GrimoireOfTheDeadEffect2(final GrimoireOfTheDeadEffect2 effect) { super(effect); - this.targetId = effect.targetId; } @Override @@ -117,8 +118,11 @@ class GrimoireOfTheDeadEffect2 extends ContinuousEffectImpl { @Override public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { - Permanent permanent = game.getPermanent(targetId); - if (permanent != null) { + for (UUID permanentId : getTargetPointer().getTargets(game, source)) { + Permanent permanent = game.getPermanent(permanentId); + if (permanent == null) { + continue; + } switch (layer) { case ColorChangingEffects_5: permanent.getColor(game).setBlack(true); @@ -127,9 +131,8 @@ class GrimoireOfTheDeadEffect2 extends ContinuousEffectImpl { permanent.addSubType(game, SubType.ZOMBIE); break; } - return true; } - return false; + return true; } @Override @@ -141,5 +144,4 @@ class GrimoireOfTheDeadEffect2 extends ContinuousEffectImpl { public boolean hasLayer(Layer layer) { return layer == Layer.ColorChangingEffects_5 || layer == Layer.TypeChangingEffects_4; } - } diff --git a/Mage.Sets/src/mage/cards/g/GrislyRitual.java b/Mage.Sets/src/mage/cards/g/GrislyRitual.java new file mode 100644 index 00000000000..8fde8b7280e --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GrislyRitual.java @@ -0,0 +1,35 @@ +package mage.cards.g; + +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.game.permanent.token.BloodToken; +import mage.target.common.TargetCreatureOrPlaneswalker; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GrislyRitual extends CardImpl { + + public GrislyRitual(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{5}{B}"); + + // Destroy target creature or planeswalker. Create two Blood tokens. + this.getSpellAbility().addEffect(new DestroyTargetEffect()); + this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalker()); + this.getSpellAbility().addEffect(new CreateTokenEffect(new BloodToken(), 2)); + } + + private GrislyRitual(final GrislyRitual card) { + super(card); + } + + @Override + public GrislyRitual copy() { + return new GrislyRitual(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GristTheHungerTide.java b/Mage.Sets/src/mage/cards/g/GristTheHungerTide.java index 2db1bc837a5..c97b6fc2d0e 100644 --- a/Mage.Sets/src/mage/cards/g/GristTheHungerTide.java +++ b/Mage.Sets/src/mage/cards/g/GristTheHungerTide.java @@ -3,7 +3,6 @@ package mage.cards.g; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.costs.common.SacrificeTargetCost; @@ -44,7 +43,7 @@ public final class GristTheHungerTide extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.GRIST); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // As long as Grist, the Hunger Tide isn't on the battlefield, it's a 1/1 Insect creature in addition to its other types. this.addAbility(new SimpleStaticAbility(Zone.ALL, new GristTheHungerTideTypeEffect())); diff --git a/Mage.Sets/src/mage/cards/g/GrizzledAngler.java b/Mage.Sets/src/mage/cards/g/GrizzledAngler.java index 20b7016afb2..3726aae9f96 100644 --- a/Mage.Sets/src/mage/cards/g/GrizzledAngler.java +++ b/Mage.Sets/src/mage/cards/g/GrizzledAngler.java @@ -32,7 +32,6 @@ public final class GrizzledAngler extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(3); - this.transformable = true; this.secondSideCardClazz = GrislyAnglerfish.class; // {T}: Put the top two cards of your library into your graveyard. Then if there is a colorless creature card in your graveyard, transform Grizzled Angler. @@ -78,7 +77,7 @@ class GrizzledAnglerEffect extends OneShotEffect { if (controller != null) { controller.millCards(2, source, game); if (controller.getGraveyard().count(filter, source.getSourceId(), source.getControllerId(), game) >= 1) { - return new TransformSourceEffect(true).apply(game, source); + return new TransformSourceEffect().apply(game, source); } } return false; diff --git a/Mage.Sets/src/mage/cards/g/GrizzledOutcasts.java b/Mage.Sets/src/mage/cards/g/GrizzledOutcasts.java index 947f486a369..ab0bcf6566e 100644 --- a/Mage.Sets/src/mage/cards/g/GrizzledOutcasts.java +++ b/Mage.Sets/src/mage/cards/g/GrizzledOutcasts.java @@ -20,7 +20,6 @@ public final class GrizzledOutcasts extends CardImpl { this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.WEREWOLF); - this.transformable = true; this.secondSideCardClazz = mage.cards.k.KrallenhordeWantons.class; this.power = new MageInt(4); diff --git a/Mage.Sets/src/mage/cards/g/Grollub.java b/Mage.Sets/src/mage/cards/g/Grollub.java index 38bab44caa0..5453f06f9dc 100644 --- a/Mage.Sets/src/mage/cards/g/Grollub.java +++ b/Mage.Sets/src/mage/cards/g/Grollub.java @@ -27,7 +27,7 @@ public final class Grollub extends CardImpl { // Whenever Grollub is dealt damage, each opponent gains that much life. this.addAbility(new DealtDamageToSourceTriggeredAbility( - new EachOpponentGainsLifeEffect(), false, false, true)); + new EachOpponentGainsLifeEffect(), false, false)); } diff --git a/Mage.Sets/src/mage/cards/g/GrolnokTheOmnivore.java b/Mage.Sets/src/mage/cards/g/GrolnokTheOmnivore.java new file mode 100644 index 00000000000..7ff17fa1ce5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GrolnokTheOmnivore.java @@ -0,0 +1,159 @@ +package mage.cards.g; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.AttacksCreatureYouControlTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.MillCardsControllerEffect; +import mage.cards.Card; +import mage.constants.*; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.counters.CounterType; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.players.Player; +import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; + +/** + * + * @author weirddan455 + */ +public final class GrolnokTheOmnivore extends CardImpl { + + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent(SubType.FROG); + + public GrolnokTheOmnivore(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.FROG); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Whenever a Frog you control attacks, mill 3 cards. + this.addAbility(new AttacksCreatureYouControlTriggeredAbility(new MillCardsControllerEffect(3), false, filter)); + + // Whenever a permanent card is put into your graveyard from your library, exile it with a croak counter on it. + this.addAbility(new GrolnokTheOmnivoreTriggeredAbility()); + + // You may play lands and cast spells from among cards you own in exile with croak counters on them. + this.addAbility(new SimpleStaticAbility(new GrolnokTheOmnivorePlayEffect())); + } + + private GrolnokTheOmnivore(final GrolnokTheOmnivore card) { + super(card); + } + + @Override + public GrolnokTheOmnivore copy() { + return new GrolnokTheOmnivore(this); + } +} + +class GrolnokTheOmnivoreTriggeredAbility extends TriggeredAbilityImpl { + + public GrolnokTheOmnivoreTriggeredAbility() { + super(Zone.BATTLEFIELD, new GrolnokTheOmnivoreExileEffect()); + } + + private GrolnokTheOmnivoreTriggeredAbility(final GrolnokTheOmnivoreTriggeredAbility ability) { + super(ability); + } + + @Override + public GrolnokTheOmnivoreTriggeredAbility copy() { + return new GrolnokTheOmnivoreTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + Card card = game.getCard(zEvent.getTargetId()); + if (card != null && zEvent.getToZone() == Zone.GRAVEYARD && zEvent.getFromZone() == Zone.LIBRARY + && card.isOwnedBy(controllerId) && card.isPermanent(game)) { + getEffects().setTargetPointer(new FixedTarget(card, game)); + return true; + } + return false; + } + + @Override + public String getTriggerPhrase() { + return "Whenever a permanent card is put into your graveyard from your library, "; + } +} + +class GrolnokTheOmnivoreExileEffect extends OneShotEffect { + + public GrolnokTheOmnivoreExileEffect() { + super(Outcome.Exile); + staticText = "exile it with a croak counter on it"; + } + + private GrolnokTheOmnivoreExileEffect(final GrolnokTheOmnivoreExileEffect effect) { + super(effect); + } + + @Override + public GrolnokTheOmnivoreExileEffect copy() { + return new GrolnokTheOmnivoreExileEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Card card = game.getCard(targetPointer.getFirst(game, source)); + if (controller != null && card != null) { + return CardUtil.moveCardWithCounter(game, source, controller, card, Zone.EXILED, CounterType.CROAK.createInstance()); + } + return false; + } +} + +class GrolnokTheOmnivorePlayEffect extends AsThoughEffectImpl { + + public GrolnokTheOmnivorePlayEffect() { + super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "You may play lands and cast spells from among cards you own in exile with croak counters on them"; + } + + private GrolnokTheOmnivorePlayEffect(final GrolnokTheOmnivorePlayEffect effect) { + super(effect); + } + + @Override + public GrolnokTheOmnivorePlayEffect copy() { + return new GrolnokTheOmnivorePlayEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + if (affectedControllerId.equals(source.getControllerId())) { + Card card = game.getCard(objectId); + if (card != null) { + Card mainCard = card.getMainCard(); + return game.getState().getZone(mainCard.getId()).equals(Zone.EXILED) && mainCard.isOwnedBy(source.getControllerId()) + && mainCard.getCounters(game).containsKey(CounterType.CROAK); + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/g/GroomsFinery.java b/Mage.Sets/src/mage/cards/g/GroomsFinery.java new file mode 100644 index 00000000000..0ea971de78a --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GroomsFinery.java @@ -0,0 +1,89 @@ +package mage.cards.g; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.continuous.BoostEquippedEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.abilities.keyword.DeathtouchAbility; +import mage.abilities.keyword.EquipAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterEquipmentPermanent; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.filter.predicate.mageobject.NamePredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GroomsFinery extends CardImpl { + + private static final FilterPermanent filter = new FilterEquipmentPermanent(); + + static { + filter.add(new NamePredicate("Bride's Gown")); + filter.add(GroomsFineryPredicate.instance); + } + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter, false); + private static final Hint hint = new ConditionHint( + condition, "An Equipment named Bride's Gown is attached to a creature you control" + ); + + public GroomsFinery(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}{B}"); + + this.subtype.add(SubType.EQUIPMENT); + + // Equipped creature gets +2/+0. It gets an additional +0/+2 and has deathtouch as long as an Equipment named Bride's Gown is attached to a creature you control. + Ability ability = new SimpleStaticAbility(new BoostEquippedEffect(2, 0)); + ability.addEffect(new ConditionalContinuousEffect( + new BoostEquippedEffect(0, 2), + condition, "It gets an additional +0/+2" + )); + ability.addEffect(new ConditionalContinuousEffect( + new GainAbilityAttachedEffect( + DeathtouchAbility.getInstance(), AttachmentType.EQUIPMENT + ), condition, "and has deathtouch as long as an Equipment " + + "named Bride's Gown is attached to a creature you control" + )); + this.addAbility(ability.addHint(hint)); + + // Equip {2} + this.addAbility(new EquipAbility(2)); + } + + private GroomsFinery(final GroomsFinery card) { + super(card); + } + + @Override + public GroomsFinery copy() { + return new GroomsFinery(this); + } +} + +enum GroomsFineryPredicate implements ObjectSourcePlayerPredicate { + instance; + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + Permanent permanent = game.getPermanent(input.getObject().getAttachedTo()); + return permanent != null + && permanent.isCreature(game) + && permanent.isControlledBy(input.getPlayerId()); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GroundAssault.java b/Mage.Sets/src/mage/cards/g/GroundAssault.java index 6ae9aec5212..a70e6769d68 100644 --- a/Mage.Sets/src/mage/cards/g/GroundAssault.java +++ b/Mage.Sets/src/mage/cards/g/GroundAssault.java @@ -1,12 +1,11 @@ package mage.cards.g; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.dynamicvalue.common.LandsYouControlCount; import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.hint.common.LandsYouControlHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; import java.util.UUID; @@ -16,14 +15,13 @@ import java.util.UUID; */ public final class GroundAssault extends CardImpl { - private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND); - public GroundAssault(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{R}{G}"); // Ground Assault deals damage to target creature equal to the number of lands you control. - this.getSpellAbility().addEffect(new DamageTargetEffect(xValue).setText("{this} deals damage to target creature equal to the number of lands you control")); + this.getSpellAbility().addEffect(new DamageTargetEffect(LandsYouControlCount.instance).setText("{this} deals damage to target creature equal to the number of lands you control")); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + this.getSpellAbility().addHint(LandsYouControlHint.instance); } private GroundAssault(final GroundAssault card) { diff --git a/Mage.Sets/src/mage/cards/g/GroundshakerSliver.java b/Mage.Sets/src/mage/cards/g/GroundshakerSliver.java index 5c35d4afa93..e18edf0c197 100644 --- a/Mage.Sets/src/mage/cards/g/GroundshakerSliver.java +++ b/Mage.Sets/src/mage/cards/g/GroundshakerSliver.java @@ -1,7 +1,5 @@ - package mage.cards.g; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; @@ -9,13 +7,13 @@ import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.SubType; import mage.filter.StaticFilters; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class GroundshakerSliver extends CardImpl { @@ -28,9 +26,10 @@ public final class GroundshakerSliver extends CardImpl { this.toughness = new MageInt(5); // Sliver creatures you control have trample. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, - new GainAbilityControlledEffect(TrampleAbility.getInstance(), - Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS))); + this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( + TrampleAbility.getInstance(), Duration.WhileOnBattlefield, + StaticFilters.FILTER_PERMANENT_SLIVERS + ))); } private GroundshakerSliver(final GroundshakerSliver card) { diff --git a/Mage.Sets/src/mage/cards/g/GrowingRanks.java b/Mage.Sets/src/mage/cards/g/GrowingRanks.java index 63d27a29559..477e35c1881 100644 --- a/Mage.Sets/src/mage/cards/g/GrowingRanks.java +++ b/Mage.Sets/src/mage/cards/g/GrowingRanks.java @@ -8,19 +8,12 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; /** * @author LevelX2 */ public final class GrowingRanks extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public GrowingRanks(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{G/W}{G/W}"); diff --git a/Mage.Sets/src/mage/cards/g/GrowingRitesOfItlimoc.java b/Mage.Sets/src/mage/cards/g/GrowingRitesOfItlimoc.java index 6f32b3a86f5..a4216f7cb5b 100644 --- a/Mage.Sets/src/mage/cards/g/GrowingRitesOfItlimoc.java +++ b/Mage.Sets/src/mage/cards/g/GrowingRitesOfItlimoc.java @@ -36,7 +36,6 @@ public final class GrowingRitesOfItlimoc extends CardImpl { this.addSuperType(SuperType.LEGENDARY); - this.transformable = true; this.secondSideCardClazz = mage.cards.i.ItlimocCradleOfTheSun.class; // When Growing Rites of Itlimoc enters the battlefield, look at the top four cards of your library. @@ -47,7 +46,7 @@ public final class GrowingRitesOfItlimoc extends CardImpl { // At the beginning of your end step, if you control four or more creatures, transform Growing Rites of Itlimoc. this.addAbility(new TransformAbility()); this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfEndStepTriggeredAbility(new TransformSourceEffect(true), TargetController.YOU, false), + new BeginningOfEndStepTriggeredAbility(new TransformSourceEffect(), TargetController.YOU, false), new PermanentsOnTheBattlefieldCondition(StaticFilters.FILTER_CONTROLLED_A_CREATURE, ComparisonType.MORE_THAN, 3), "At the beginning of your end step, if you control four or more creatures, transform {this}")); } diff --git a/Mage.Sets/src/mage/cards/g/GrowthSpasm.java b/Mage.Sets/src/mage/cards/g/GrowthSpasm.java index 2b29137e3e2..6fc18f011ce 100644 --- a/Mage.Sets/src/mage/cards/g/GrowthSpasm.java +++ b/Mage.Sets/src/mage/cards/g/GrowthSpasm.java @@ -1,4 +1,3 @@ - package mage.cards.g; import mage.abilities.effects.common.CreateTokenEffect; @@ -21,8 +20,10 @@ public final class GrowthSpasm extends CardImpl { public GrowthSpasm(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{G}"); - + // Search your library for a basic land card, put it onto the battlefield tapped, then shuffle. this.getSpellAbility().addEffect(new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), true)); + + // Create a 0/1 colorless Eldrazi Spawn creature token. It has “Sacrifice this creature: Add {C}.” this.getSpellAbility().addEffect(new CreateTokenEffect(new EldraziSpawnToken())); } diff --git a/Mage.Sets/src/mage/cards/g/GruesomeDiscovery.java b/Mage.Sets/src/mage/cards/g/GruesomeDiscovery.java index 87b6f5fcfb8..7358a243817 100644 --- a/Mage.Sets/src/mage/cards/g/GruesomeDiscovery.java +++ b/Mage.Sets/src/mage/cards/g/GruesomeDiscovery.java @@ -3,20 +3,15 @@ package mage.cards.g; import mage.abilities.Ability; import mage.abilities.condition.common.MorbidCondition; import mage.abilities.decorator.ConditionalOneShotEffect; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.discard.DiscardCardYouChooseTargetEffect; import mage.abilities.effects.common.discard.DiscardTargetEffect; import mage.abilities.hint.common.MorbidHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.cards.CardsImpl; import mage.constants.CardType; -import mage.constants.Outcome; +import mage.constants.TargetController; import mage.filter.StaticFilters; -import mage.game.Game; -import mage.players.Player; -import mage.target.TargetCard; import mage.target.TargetPlayer; -import mage.target.common.TargetCardInHand; import java.util.UUID; @@ -31,7 +26,7 @@ public final class GruesomeDiscovery extends CardImpl { // Target player discards two cards. // Morbid — If a creature died this turn, instead that player reveals their hand, you choose two cards from it, then that player discards those cards. this.getSpellAbility().addEffect(new ConditionalOneShotEffect( - new GruesomeDiscoveryEffect(), new DiscardTargetEffect(2), + new DiscardCardYouChooseTargetEffect(2, TargetController.ANY), new DiscardTargetEffect(2), MorbidCondition.instance, "Target player discards two cards. " + "
Morbid — If a creature died this turn, instead that player reveals their hand, " + "you choose two cards from it, then that player discards those cards" @@ -49,36 +44,3 @@ public final class GruesomeDiscovery extends CardImpl { return new GruesomeDiscovery(this); } } - -class GruesomeDiscoveryEffect extends OneShotEffect { - - GruesomeDiscoveryEffect() { - super(Outcome.Discard); - } - - private GruesomeDiscoveryEffect(final GruesomeDiscoveryEffect effect) { - super(effect); - } - - @Override - public GruesomeDiscoveryEffect copy() { - return new GruesomeDiscoveryEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - Player targetPlayer = game.getPlayer(source.getFirstTarget()); - if (player == null || targetPlayer == null) { - return false; - } - targetPlayer.revealCards(source, targetPlayer.getHand(), game); - if (targetPlayer.getHand().size() <= 2) { - targetPlayer.discard(2, false, false, source, game); - } - TargetCard target = new TargetCardInHand(2, StaticFilters.FILTER_CARD_CARDS); - player.choose(Outcome.Discard, targetPlayer.getHand(), target, game); - targetPlayer.discard(new CardsImpl(target.getTargets()), false, source, game); - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/g/GruesomeEncore.java b/Mage.Sets/src/mage/cards/g/GruesomeEncore.java index 6fd19e1284d..e3b6f03a19e 100644 --- a/Mage.Sets/src/mage/cards/g/GruesomeEncore.java +++ b/Mage.Sets/src/mage/cards/g/GruesomeEncore.java @@ -115,9 +115,8 @@ class GruesomeEncoreReplacementEffect extends ReplacementEffectImpl { @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Player player = game.getPlayer(source.getControllerId()); - Card card = game.getCard(source.getFirstTarget()); - return player != null && card != null && player.moveCards(card, Zone.EXILED, source, game); + ((ZoneChangeEvent) event).setToZone(Zone.EXILED); + return false; } @Override diff --git a/Mage.Sets/src/mage/cards/g/GruesomeMenagerie.java b/Mage.Sets/src/mage/cards/g/GruesomeMenagerie.java index ee7310ed349..d033c58cfae 100644 --- a/Mage.Sets/src/mage/cards/g/GruesomeMenagerie.java +++ b/Mage.Sets/src/mage/cards/g/GruesomeMenagerie.java @@ -62,7 +62,7 @@ class GruesomeMenagerieEffect extends OneShotEffect { super(Outcome.Benefit); this.staticText = "Choose a creature card with mana value 1 " + "in your graveyard, then do the same for creature cards " - + "with mana values 2 and 3. " + + "with mana value 2 and 3. " + "Return those cards to the battlefield."; } diff --git a/Mage.Sets/src/mage/cards/g/GrunnTheLonelyKing.java b/Mage.Sets/src/mage/cards/g/GrunnTheLonelyKing.java index 60ccea44218..30662c6809f 100644 --- a/Mage.Sets/src/mage/cards/g/GrunnTheLonelyKing.java +++ b/Mage.Sets/src/mage/cards/g/GrunnTheLonelyKing.java @@ -3,7 +3,7 @@ package mage.cards.g; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.AttacksAloneTriggeredAbility; +import mage.abilities.common.AttacksAloneSourceTriggeredAbility; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.condition.common.KickedCondition; import mage.abilities.dynamicvalue.common.SourcePermanentPowerCount; @@ -42,7 +42,7 @@ public final class GrunnTheLonelyKing extends CardImpl { SourcePermanentPowerCount power = new SourcePermanentPowerCount(); Effect effect = new BoostSourceEffect(power, SourcePermanentToughnessValue.getInstance(), Duration.EndOfTurn, true); effect.setText("double its power and toughness until end of turn"); - this.addAbility(new AttacksAloneTriggeredAbility(effect)); + this.addAbility(new AttacksAloneSourceTriggeredAbility(effect)); } private GrunnTheLonelyKing(final GrunnTheLonelyKing card) { diff --git a/Mage.Sets/src/mage/cards/g/GruulRagebeast.java b/Mage.Sets/src/mage/cards/g/GruulRagebeast.java index 0429477a369..801776fe013 100644 --- a/Mage.Sets/src/mage/cards/g/GruulRagebeast.java +++ b/Mage.Sets/src/mage/cards/g/GruulRagebeast.java @@ -79,7 +79,7 @@ class GruulRagebeastTriggeredAbility extends TriggeredAbilityImpl { for (Effect effect : this.getEffects()) { if (effect instanceof GruulRagebeastEffect) { effect.setTargetPointer( - new FixedTarget(event.getTargetId()) + new FixedTarget(event.getTargetId(), game) .withData("triggeredName", GameLog.getColoredObjectIdNameForTooltip(sourceObject)) ); } diff --git a/Mage.Sets/src/mage/cards/g/GruulWarChant.java b/Mage.Sets/src/mage/cards/g/GruulWarChant.java index a0e6a7d3570..b5f645a99b7 100644 --- a/Mage.Sets/src/mage/cards/g/GruulWarChant.java +++ b/Mage.Sets/src/mage/cards/g/GruulWarChant.java @@ -33,7 +33,9 @@ public final class GruulWarChant extends CardImpl { // Attacking creatures you control get +1/+0 and have menace. (They can't be blocked except by two or more creatures.) - Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(new MenaceAbility(), Duration.WhileOnBattlefield, filter)); + Ability ability = new SimpleStaticAbility( + Zone.BATTLEFIELD, + new GainAbilityAllEffect(new MenaceAbility(false), Duration.WhileOnBattlefield, filter)); ability.addEffect(new BoostAllEffect(1,0, Duration.WhileOnBattlefield, filter, false)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/g/GryffRider.java b/Mage.Sets/src/mage/cards/g/GryffRider.java new file mode 100644 index 00000000000..183093b1b0d --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GryffRider.java @@ -0,0 +1,41 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.TrainingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GryffRider extends CardImpl { + + public GryffRider(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.KNIGHT); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Training + this.addAbility(new TrainingAbility()); + } + + private GryffRider(final GryffRider card) { + super(card); + } + + @Override + public GryffRider copy() { + return new GryffRider(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GryffwingCavalry.java b/Mage.Sets/src/mage/cards/g/GryffwingCavalry.java new file mode 100644 index 00000000000..12d3ad4002b --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GryffwingCavalry.java @@ -0,0 +1,68 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.TrainingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterAttackingCreature; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GryffwingCavalry extends CardImpl { + + private static final FilterPermanent filter + = new FilterAttackingCreature("attacking creature without flying"); + + static { + filter.add(Predicates.not(new AbilityPredicate(FlyingAbility.class))); + } + + public GryffwingCavalry(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.KNIGHT); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Training + this.addAbility(new TrainingAbility()); + + // Whenever Gryffwing Cavalry attacks, you may pay {1}{W}. If you do, target attacking creature without flying gains flying until end of turn. + Ability ability = new AttacksTriggeredAbility(new DoIfCostPaid( + new GainAbilityTargetEffect( + FlyingAbility.getInstance(), Duration.EndOfTurn + ), new ManaCostsImpl<>("{1}{W}") + )); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + } + + private GryffwingCavalry(final GryffwingCavalry card) { + super(card); + } + + @Override + public GryffwingCavalry copy() { + return new GryffwingCavalry(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GuardianBeast.java b/Mage.Sets/src/mage/cards/g/GuardianBeast.java index 8bc09b6a625..6cf9aeb058f 100644 --- a/Mage.Sets/src/mage/cards/g/GuardianBeast.java +++ b/Mage.Sets/src/mage/cards/g/GuardianBeast.java @@ -1,20 +1,19 @@ package mage.cards.g; -import java.util.Objects; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.condition.InvertCondition; import mage.abilities.condition.common.SourceTappedCondition; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.abilities.keyword.IndestructibleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; import mage.filter.FilterObject; import mage.filter.FilterStackObject; import mage.filter.StaticFilters; @@ -25,8 +24,10 @@ import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.game.stack.StackObject; +import java.util.Objects; +import java.util.UUID; + /** - * * @author spjspj */ public final class GuardianBeast extends CardImpl { @@ -48,13 +49,14 @@ public final class GuardianBeast extends CardImpl { // As long as Guardian Beast is untapped, noncreature artifacts you control can't be enchanted, they're indestructible, and other players can't gain control of them. // This effect doesn't remove Auras already attached to those artifacts. - Effect effect = new ConditionalContinuousEffect(new GainAbilityControlledEffect(IndestructibleAbility.getInstance(), Duration.WhileOnBattlefield, filter), - new InvertCondition(SourceTappedCondition.instance), - "As long as Guardian Beast is untapped, noncreature artifacts you control can't be enchanted, they're indestructible"); - Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, effect); - ability.addEffect(new GuardianBeastConditionalEffect(this.getId())); + Ability ability = new SimpleStaticAbility(new ConditionalContinuousEffect( + new GainAbilityControlledEffect( + IndestructibleAbility.getInstance(), Duration.WhileOnBattlefield, filter + ), SourceTappedCondition.UNTAPPED, "As long as {this} is untapped, " + + "noncreature artifacts you control can't be enchanted, they're indestructible" + )); + ability.addEffect(new GuardianBeastConditionalEffect()); this.addAbility(ability); - } private GuardianBeast(final GuardianBeast card) { @@ -69,17 +71,13 @@ public final class GuardianBeast extends CardImpl { class GuardianBeastConditionalEffect extends ContinuousRuleModifyingEffectImpl { - private final UUID guardianBeastId; - - public GuardianBeastConditionalEffect(UUID guardianBeastId) { + public GuardianBeastConditionalEffect() { super(Duration.WhileOnBattlefield, Outcome.Neutral); staticText = ", and other players can't gain control of them. This effect doesn't remove Auras already attached to those artifacts"; - this.guardianBeastId = guardianBeastId; } public GuardianBeastConditionalEffect(final GuardianBeastConditionalEffect effect) { super(effect); - this.guardianBeastId = effect.guardianBeastId; } @Override @@ -99,15 +97,14 @@ class GuardianBeastConditionalEffect extends ContinuousRuleModifyingEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { - Permanent sourceObject = game.getPermanent(source.getSourceId()); + Permanent permanent = source.getSourcePermanentIfItStillExists(game); Permanent targetPermanent = game.getPermanent(event.getTargetId()); - Permanent guardianBeast = game.getPermanent(guardianBeastId); - if (guardianBeast == null || guardianBeast.isTapped() || sourceObject == null || targetPermanent == null) { + if (permanent == null || permanent.isTapped() || targetPermanent == null) { return false; } - if (!Objects.equals(targetPermanent.getControllerId(), guardianBeast.getControllerId())) { + if (!Objects.equals(targetPermanent.getControllerId(), permanent.getControllerId())) { return false; } diff --git a/Mage.Sets/src/mage/cards/g/GuardianOfSolitude.java b/Mage.Sets/src/mage/cards/g/GuardianOfSolitude.java index 728802a4c14..0782d6fa957 100644 --- a/Mage.Sets/src/mage/cards/g/GuardianOfSolitude.java +++ b/Mage.Sets/src/mage/cards/g/GuardianOfSolitude.java @@ -27,7 +27,7 @@ public final class GuardianOfSolitude extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(2); - Ability ability = new SpellCastControllerTriggeredAbility(new GainAbilityTargetEffect(FlyingAbility.getInstance(), Duration.EndOfTurn), StaticFilters.SPIRIT_OR_ARCANE_CARD, false); + Ability ability = new SpellCastControllerTriggeredAbility(new GainAbilityTargetEffect(FlyingAbility.getInstance(), Duration.EndOfTurn), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/g/GuardianOfTazeem.java b/Mage.Sets/src/mage/cards/g/GuardianOfTazeem.java index c3fb3e74472..dfab6ddaaa8 100644 --- a/Mage.Sets/src/mage/cards/g/GuardianOfTazeem.java +++ b/Mage.Sets/src/mage/cards/g/GuardianOfTazeem.java @@ -14,7 +14,7 @@ import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -27,12 +27,6 @@ import mage.target.targetpointer.FixedTarget; */ public final class GuardianOfTazeem extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public GuardianOfTazeem(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{U}{U}"); this.subtype.add(SubType.SPHINX); @@ -44,7 +38,7 @@ public final class GuardianOfTazeem extends CardImpl { // Landfall — Whenever a land enters the battlefield under you control, tap target creature an opponent controls. If that land is an Island, that creature doesn't untap during its controller's next untap step. Ability ability = new GuardianOfTazeemTriggeredAbility(); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/g/GuardiansOfOboro.java b/Mage.Sets/src/mage/cards/g/GuardiansOfOboro.java new file mode 100644 index 00000000000..136a8da8db1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GuardiansOfOboro.java @@ -0,0 +1,55 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.combat.CanAttackAsThoughItDidntHaveDefenderAllEffect; +import mage.abilities.keyword.DefenderAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.permanent.ModifiedPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GuardiansOfOboro extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("modified creatures you control"); + + static { + filter.add(ModifiedPredicate.instance); + } + + public GuardiansOfOboro(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); + + this.subtype.add(SubType.MOONFOLK); + this.subtype.add(SubType.SAMURAI); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // Defender + this.addAbility(DefenderAbility.getInstance()); + + // Modified creatures you control can attack as though they didn't have defender. + this.addAbility(new SimpleStaticAbility( + new CanAttackAsThoughItDidntHaveDefenderAllEffect(Duration.WhileOnBattlefield, filter) + )); + } + + private GuardiansOfOboro(final GuardiansOfOboro card) { + super(card); + } + + @Override + public GuardiansOfOboro copy() { + return new GuardiansOfOboro(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GuileSonicSoldier.java b/Mage.Sets/src/mage/cards/g/GuileSonicSoldier.java new file mode 100644 index 00000000000..5476369962c --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GuileSonicSoldier.java @@ -0,0 +1,107 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.EntersBattlefieldOrAttacksSourceTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.keyword.IndestructibleAbility; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetAnyTarget; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GuileSonicSoldier extends CardImpl { + + public GuileSonicSoldier(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{R}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Whenever Guile, Sonic Soldier enters the battlefield or attacks, put a charge counter on him or remove one from him. When you remove a counter this way, choose one— + // • Sonic Boom—Guile, Sonic Soldier deals 4 damage to any target. + // • Flash Kick—Guile, Sonic Soldier gains lifelink and indestructible until end of turn. + this.addAbility(new EntersBattlefieldOrAttacksSourceTriggeredAbility(new GuileSonicSoldierEffect())); + } + + private GuileSonicSoldier(final GuileSonicSoldier card) { + super(card); + } + + @Override + public GuileSonicSoldier copy() { + return new GuileSonicSoldier(this); + } +} + +class GuileSonicSoldierEffect extends OneShotEffect { + + private final ReflexiveTriggeredAbility ability = makeAbility(); + + GuileSonicSoldierEffect() { + super(Outcome.Benefit); + staticText = "put a charge counter on him or remove one from him. When you remove a counter this way, " + + CardUtil.getTextWithFirstCharLowerCase(ability.getRule()); + } + + private GuileSonicSoldierEffect(final GuileSonicSoldierEffect effect) { + super(effect); + } + + @Override + public GuileSonicSoldierEffect copy() { + return new GuileSonicSoldierEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (player == null || permanent == null) { + return false; + } + if (!permanent.getCounters(game).containsKey(CounterType.CHARGE) || player.chooseUse( + outcome, "Add or remove a charge counter?", null, + "Add", "Remove", source, game + )) { + permanent.addCounters(CounterType.CHARGE.createInstance(), source, game); + } else { + permanent.removeCounters(CounterType.CHARGE.createInstance(), source, game); + game.fireReflexiveTriggeredAbility(ability, source); + } + return true; + } + + private static final ReflexiveTriggeredAbility makeAbility() { + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new DamageTargetEffect(4), false + ); + ability.addTarget(new TargetAnyTarget()); + Mode mode = new Mode(new GainAbilitySourceEffect( + LifelinkAbility.getInstance(), Duration.EndOfTurn + ).setText("{this} gains lifelink")); + mode.addEffect(new GainAbilitySourceEffect( + IndestructibleAbility.getInstance(), Duration.EndOfTurn + ).setText("and indestructible until end of turn")); + ability.withFirstModeFlavorWord("Sonic Boom").addMode(mode.withFlavorWord("Flash Kick")); + return ability; + } +} diff --git a/Mage.Sets/src/mage/cards/g/GustOfWind.java b/Mage.Sets/src/mage/cards/g/GustOfWind.java index 19c5a506f20..b114d177736 100644 --- a/Mage.Sets/src/mage/cards/g/GustOfWind.java +++ b/Mage.Sets/src/mage/cards/g/GustOfWind.java @@ -51,7 +51,7 @@ public final class GustOfWind extends CardImpl { this.getSpellAbility().addTarget(new TargetPermanent(filter2)); // Draw a card. - this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("
")); } private GustOfWind(final GustOfWind card) { diff --git a/Mage.Sets/src/mage/cards/g/GusthasScepter.java b/Mage.Sets/src/mage/cards/g/GusthasScepter.java index 0cd624e4790..ed64cf25dd3 100644 --- a/Mage.Sets/src/mage/cards/g/GusthasScepter.java +++ b/Mage.Sets/src/mage/cards/g/GusthasScepter.java @@ -1,37 +1,31 @@ package mage.cards.g; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; -import mage.MageObject; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.AsThoughEffectType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.FilterCard; -import mage.game.Game; import mage.game.ExileZone; +import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; import mage.players.Player; -import mage.target.Target; +import mage.target.TargetCard; import mage.target.common.TargetCardInExile; import mage.target.common.TargetCardInHand; +import mage.util.CardUtil; + +import java.util.UUID; /** - * - * @author LevelX2, jeffwadsworth & L_J + * @author LevelX2, jeffwadsworth, L_J & TheElk801 */ public final class GusthasScepter extends CardImpl { @@ -39,16 +33,13 @@ public final class GusthasScepter extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{0}"); // {T}: Exile a card from your hand face down. You may look at it for as long as it remains exiled. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new GusthasScepterExileEffect(), new TapSourceCost())); + this.addAbility(new SimpleActivatedAbility(new GusthasScepterExileEffect(), new TapSourceCost())); // {T}: Return a card you own exiled with Gustha’s Scepter to your hand. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ReturnToHandTargetEffect(), new TapSourceCost()); - ability.addTarget(new TargetCardInGusthasScepterExile(this.getId())); - this.addAbility(ability); + this.addAbility(new SimpleActivatedAbility(new GusthasScepterReturnEffect(), new TapSourceCost())); // When you lose control of Gustha’s Scepter, put all cards exiled with Gustha’s Scepter into their owner’s graveyard. this.addAbility(new GusthasScepterLoseControlAbility()); - } private GusthasScepter(final GusthasScepter card) { @@ -65,7 +56,7 @@ class GusthasScepterExileEffect extends OneShotEffect { public GusthasScepterExileEffect() { super(Outcome.DrawCard); - staticText = "Exile a card from your hand face down. You may look at it for as long as it remains exiled"; + staticText = "exile a card from your hand face down. You may look at it for as long as it remains exiled"; } public GusthasScepterExileEffect(final GusthasScepterExileEffect effect) { @@ -75,25 +66,23 @@ class GusthasScepterExileEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Target target = new TargetCardInHand(new FilterCard("card to exile")); - if (controller.chooseTarget(outcome, target, source, game)) { - Card card = game.getCard(target.getFirstTarget()); - MageObject sourceObject = game.getObject(source.getSourceId()); - if (card != null && sourceObject != null) { - UUID exileId = source.getSourceId(); - if (card.moveToExile(exileId, - sourceObject.getIdName(), - source, - game)) { - card.setFaceDown(true, game); - game.addEffect(new GusthasScepterLookAtCardEffect(card.getId()), source); - return true; - } - } - } + if (controller == null || controller.getHand().isEmpty()) { + return false; } - return false; + TargetCard target = new TargetCardInHand(); + controller.chooseTarget(outcome, controller.getHand(), target, source, game); + Card card = game.getCard(target.getFirstTarget()); + if (card == null) { + return false; + } + controller.moveCardsToExile( + card, source, game, false, + CardUtil.getExileZoneId(game, source), + CardUtil.getSourceName(game, source) + ); + card.setFaceDown(true, game); + game.addEffect(new GusthasScepterLookAtCardEffect(card, game), source); + return true; } @Override @@ -102,81 +91,54 @@ class GusthasScepterExileEffect extends OneShotEffect { } } -class TargetCardInGusthasScepterExile extends TargetCardInExile { +class GusthasScepterReturnEffect extends OneShotEffect { - public TargetCardInGusthasScepterExile(UUID cardId) { - super(1, 1, new FilterCard("card exiled with Gustha's Scepter"), null); + private static final FilterCard filter = new FilterCard("card you own exiled with this permanent"); + + GusthasScepterReturnEffect() { + super(Outcome.Benefit); + staticText = "return a card you own exiled with {this} to your hand"; } - public TargetCardInGusthasScepterExile(final TargetCardInGusthasScepterExile target) { - super(target); + private GusthasScepterReturnEffect(final GusthasScepterReturnEffect effect) { + super(effect); } @Override - public Set possibleTargets(UUID sourceId, UUID sourceControllerId, Game game) { - Set possibleTargets = new HashSet<>(); - Card sourceCard = game.getCard(sourceId); - if (sourceCard != null) { - UUID exileId = sourceId; - ExileZone exile = game.getExile().getExileZone(exileId); - if (exile != null && !exile.isEmpty()) { - possibleTargets.addAll(exile); - } + public GusthasScepterReturnEffect copy() { + return new GusthasScepterReturnEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; } - return possibleTargets; - } - - @Override - public boolean canChoose(UUID sourceId, UUID sourceControllerId, Game game) { - Card sourceCard = game.getCard(sourceId); - if (sourceCard != null) { - UUID exileId = sourceId; - ExileZone exile = game.getExile().getExileZone(exileId); - if (exile != null && !exile.isEmpty()) { - return true; - } + TargetCard target = new TargetCardInExile(filter, CardUtil.getExileZoneId(game, source)); + target.setNotTarget(true); + if (!target.canChoose(source.getSourceId(), source.getControllerId(), game)) { + return false; } - return false; - } - - @Override - public boolean canTarget(UUID id, Ability source, Game game) { - Card card = game.getCard(id); - if (card != null - && game.getState().getZone(card.getId()) == Zone.EXILED) { - ExileZone exile = null; - Card sourceCard = game.getCard(source.getSourceId()); - if (sourceCard != null) { - UUID exileId = source.getSourceId(); - exile = game.getExile().getExileZone(exileId); - } - if (exile != null - && exile.contains(id)) { - return filter.match(card, source.getControllerId(), game); - } - } - return false; - } - - @Override - public TargetCardInGusthasScepterExile copy() { - return new TargetCardInGusthasScepterExile(this); + player.choose(outcome, target, source.getSourceId(), game); + Card card = game.getCard(target.getFirstTarget()); + return card != null && player.moveCards(card, Zone.HAND, source, game); } } class GusthasScepterLookAtCardEffect extends AsThoughEffectImpl { - private final UUID cardId; + private final MageObjectReference mor; - public GusthasScepterLookAtCardEffect(UUID cardId) { + public GusthasScepterLookAtCardEffect(Card card, Game game) { super(AsThoughEffectType.LOOK_AT_FACE_DOWN, Duration.EndOfGame, Outcome.Benefit); - this.cardId = cardId; + this.mor = new MageObjectReference(card, game); staticText = "You may look at it for as long as it remains exiled"; } public GusthasScepterLookAtCardEffect(final GusthasScepterLookAtCardEffect effect) { super(effect); - this.cardId = effect.cardId; + this.mor = effect.mor; } @Override @@ -191,26 +153,16 @@ class GusthasScepterLookAtCardEffect extends AsThoughEffectImpl { @Override public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - if (objectId.equals(cardId) && affectedControllerId.equals(source.getControllerId())) { - MageObject sourceObject = source.getSourceObject(game); - if (sourceObject != null) { - UUID exileId = source.getSourceId(); - ExileZone exileZone = game.getExile().getExileZone(exileId); - if (exileZone != null - && exileZone.contains(cardId)) { - Player controller = game.getPlayer(source.getControllerId()); - Card card = game.getCard(cardId); - if (controller != null - && card != null - && game.getState().getZone(cardId) == Zone.EXILED) { - return true; - } - } else { - discard(); - } - } + if (!mor.zoneCounterIsCurrent(game)) { + discard(); + return false; } - return false; + ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source)); + if (exileZone == null || !exileZone.contains(mor.getSourceId())) { + discard(); + return false; + } + return mor.refersTo(objectId, game) && source.isControlledBy(affectedControllerId); } } @@ -236,14 +188,12 @@ class GusthasScepterLoseControlAbility extends DelayedTriggeredAbility { } public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.LOST_CONTROL) { - return event.getPlayerId().equals(controllerId) - && event.getTargetId().equals(this.getSourceId()); - } else if (event.getType() == GameEvent.EventType.ZONE_CHANGE) { - if (event.getTargetId().equals(this.getSourceId())) { - ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - return (zEvent.getFromZone() == Zone.BATTLEFIELD); - } + switch (event.getType()) { + case LOST_CONTROL: + return event.getPlayerId().equals(controllerId) + && event.getTargetId().equals(this.getSourceId()); + case ZONE_CHANGE: + return event.getTargetId().equals(this.getSourceId()) && ((ZoneChangeEvent) event).getFromZone() == Zone.BATTLEFIELD; } return false; } @@ -258,7 +208,6 @@ class GusthasScepterPutExiledCardsInOwnersGraveyard extends OneShotEffect { public GusthasScepterPutExiledCardsInOwnersGraveyard() { super(Outcome.Neutral); - staticText = " put all cards exiled with {this} into their owner's graveyard"; } public GusthasScepterPutExiledCardsInOwnersGraveyard(final GusthasScepterPutExiledCardsInOwnersGraveyard effect) { @@ -268,14 +217,11 @@ class GusthasScepterPutExiledCardsInOwnersGraveyard extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - UUID exileId = source.getSourceId(); - ExileZone exileZone = game.getExile().getExileZone(exileId); - if (exileZone != null) { - return controller.moveCards(exileZone.getCards(game), Zone.GRAVEYARD, source, game); - } + if (controller == null) { + return false; } - return false; + ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source)); + return exileZone != null && controller.moveCards(exileZone.getCards(game), Zone.GRAVEYARD, source, game); } @Override diff --git a/Mage.Sets/src/mage/cards/g/GutterShortcut.java b/Mage.Sets/src/mage/cards/g/GutterShortcut.java new file mode 100644 index 00000000000..5b2f62cf08d --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GutterShortcut.java @@ -0,0 +1,73 @@ +package mage.cards.g; + +import mage.abilities.Ability; +import mage.abilities.common.PutIntoGraveFromAnywhereSourceAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalRestrictionEffect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.ExileSourceEffect; +import mage.abilities.effects.common.combat.CantBeBlockedAttachedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GutterShortcut extends CardImpl { + + public GutterShortcut(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, ""); + + this.subtype.add(SubType.AURA); + this.color.setBlue(true); + this.nightCard = true; + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // Enchanted creature can't be blocked as long as it's attacking alone. + this.addAbility(new SimpleStaticAbility(new ConditionalRestrictionEffect( + new CantBeBlockedAttachedEffect(AttachmentType.AURA), GutterShortcutCondition.instance, + "enchanted creature can't be blocked as long as it's attacking alone" + ))); + + // If Gutter Shortcut would be put into a graveyard from anywhere, exile it instead. + this.addAbility(new PutIntoGraveFromAnywhereSourceAbility(new ExileSourceEffect().setText("exile it instead"))); + } + + private GutterShortcut(final GutterShortcut card) { + super(card); + } + + @Override + public GutterShortcut copy() { + return new GutterShortcut(this); + } +} + +enum GutterShortcutCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + return permanent != null && game.getCombat().attacksAlone() + && game.getCombat().getAttackers().contains(permanent.getAttachedTo()); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/g/GutterSkulker.java b/Mage.Sets/src/mage/cards/g/GutterSkulker.java new file mode 100644 index 00000000000..674a82f29b8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GutterSkulker.java @@ -0,0 +1,50 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.SourceAttackingAloneCondition; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.decorator.ConditionalRestrictionEffect; +import mage.abilities.effects.common.combat.CantBeBlockedSourceEffect; +import mage.abilities.keyword.DisturbAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GutterSkulker extends CardImpl { + + public GutterSkulker(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); + + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + this.secondSideCardClazz = mage.cards.g.GutterShortcut.class; + + // Gutter Skulker can't be blocked as long as it's attacking alone. + this.addAbility(new SimpleStaticAbility(new ConditionalRestrictionEffect( + new CantBeBlockedSourceEffect(Duration.WhileOnBattlefield), + SourceAttackingAloneCondition.instance, + "{this} can't be blocked as long as it's attacking alone" + ))); + + // Disturb {3}{U} + this.addAbility(new DisturbAbility(this, "{3}{U}")); + } + + private GutterSkulker(final GutterSkulker card) { + super(card); + } + + @Override + public GutterSkulker copy() { + return new GutterSkulker(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GyomeMasterChef.java b/Mage.Sets/src/mage/cards/g/GyomeMasterChef.java index eb9764ca01a..0fa4e6d4c09 100644 --- a/Mage.Sets/src/mage/cards/g/GyomeMasterChef.java +++ b/Mage.Sets/src/mage/cards/g/GyomeMasterChef.java @@ -27,6 +27,7 @@ import mage.game.permanent.PermanentToken; import mage.game.permanent.token.FoodToken; import mage.target.common.TargetControlledPermanent; import mage.target.common.TargetCreaturePermanent; +import mage.util.CardUtil; import mage.watchers.Watcher; import java.util.HashMap; @@ -118,7 +119,7 @@ class GyomeMasterChefWatcher extends Watcher { if (permanent == null || permanent instanceof PermanentToken || !permanent.isCreature(game)) { return; } - playerMap.compute(event.getPlayerId(), (u, i) -> i != null ? Integer.sum(i, 1) : 1); + playerMap.compute(event.getPlayerId(), CardUtil::setOrIncrementValue); } @Override diff --git a/Mage.Sets/src/mage/cards/g/GyrudaDoomOfDepths.java b/Mage.Sets/src/mage/cards/g/GyrudaDoomOfDepths.java index dc715790dfb..1a50dbc0ba5 100644 --- a/Mage.Sets/src/mage/cards/g/GyrudaDoomOfDepths.java +++ b/Mage.Sets/src/mage/cards/g/GyrudaDoomOfDepths.java @@ -15,7 +15,6 @@ import mage.filter.predicate.mageobject.ManaValueParityPredicate; import mage.game.Game; import mage.players.Player; import mage.target.TargetCard; -import mage.target.common.TargetCardInGraveyard; import java.util.Set; import java.util.UUID; @@ -80,8 +79,8 @@ class GyrudaDoomOfDepthsEffect extends OneShotEffect { GyrudaDoomOfDepthsEffect() { super(Outcome.Benefit); - staticText = "each player mills four cards. Put a creature card with an even mana value " + - "from among the milled cards onto the battlefield under your control"; + staticText = "each player mills four cards. Put a creature card with an even mana value " + + "from among the milled cards onto the battlefield under your control"; } private GyrudaDoomOfDepthsEffect(final GyrudaDoomOfDepthsEffect effect) { @@ -107,15 +106,20 @@ class GyrudaDoomOfDepthsEffect extends OneShotEffect { } cards.addAll(player.millCards(4, source, game)); } - cards.removeIf(cardId -> game.getState().getZone(cardId) != Zone.GRAVEYARD - && game.getState().getZone(cardId) != Zone.EXILED); + /* + If a replacement effect causes a player to exile the top four cards of their library + instead of putting them into their graveyard as Gyruda’s triggered ability resolves, + the creature card you choose may be one of those cards in exile. (2020-04-17) + */ if (cards.isEmpty()) { return true; } - TargetCard targetCard = new TargetCardInGraveyard(0, 1, filter); + // the creature card chosen can be in any zone, not just the graveyard + TargetCard targetCard = new TargetCard(0, 1, Zone.ALL, filter); targetCard.setNotTarget(true); controller.choose(outcome, cards, targetCard, game); Card card = game.getCard(targetCard.getFirstTarget()); - return card != null && controller.moveCards(card, Zone.BATTLEFIELD, source, game); + return card != null + && controller.moveCards(card, Zone.BATTLEFIELD, source, game); } } diff --git a/Mage.Sets/src/mage/cards/g/GyrusWakerOfCorpses.java b/Mage.Sets/src/mage/cards/g/GyrusWakerOfCorpses.java index 28f936bdeb6..88f599718fc 100644 --- a/Mage.Sets/src/mage/cards/g/GyrusWakerOfCorpses.java +++ b/Mage.Sets/src/mage/cards/g/GyrusWakerOfCorpses.java @@ -96,7 +96,7 @@ class GyrusWakerOfCorpsesEffect extends OneShotEffect { CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(source.getControllerId(), null, true, 1, true, true); effect.setTargetPointer(new FixedTarget(card, game)); effect.apply(game, source); - for (Permanent addedToken : effect.getAddedPermanent()) { + for (Permanent addedToken : effect.getAddedPermanents()) { Effect exileEffect = new ExileTargetEffect(); exileEffect.setTargetPointer(new FixedTarget(addedToken, game)); new CreateDelayedTriggeredAbilityEffect(new AtTheEndOfCombatDelayedTriggeredAbility(exileEffect), false).apply(game, source); diff --git a/Mage.Sets/src/mage/cards/h/HaazdaMarshal.java b/Mage.Sets/src/mage/cards/h/HaazdaMarshal.java index 5cf0ec92292..4cb72d7e5de 100644 --- a/Mage.Sets/src/mage/cards/h/HaazdaMarshal.java +++ b/Mage.Sets/src/mage/cards/h/HaazdaMarshal.java @@ -70,6 +70,6 @@ class HaazdaMarshalTriggeredAbility extends TriggeredAbilityImpl { @Override public String getRule() { return "Whenever {this} and at least two other creatures attack, " - + "create a 1/1 white Solider creature token with lifelink."; + + "create a 1/1 white Soldier creature token with lifelink."; } } diff --git a/Mage.Sets/src/mage/cards/h/HaazdaSnareSquad.java b/Mage.Sets/src/mage/cards/h/HaazdaSnareSquad.java index 33a829eaac4..e8127b215dd 100644 --- a/Mage.Sets/src/mage/cards/h/HaazdaSnareSquad.java +++ b/Mage.Sets/src/mage/cards/h/HaazdaSnareSquad.java @@ -12,8 +12,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.Target; import mage.target.common.TargetCreaturePermanent; @@ -27,11 +26,6 @@ import java.util.UUID; public final class HaazdaSnareSquad extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public HaazdaSnareSquad (UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}"); this.subtype.add(SubType.HUMAN); @@ -43,7 +37,7 @@ public final class HaazdaSnareSquad extends CardImpl { // Whenever Haazda Snare Squad attacks you may pay {W}. If you do, tap target creature an opponent controls. Ability ability = new AttacksTriggeredAbility(new DoIfCostPaid(new TapTargetEffect(), new ManaCostsImpl("{W}")),false, "Whenever {this} attacks, you may pay {W}. If you do, tap target creature an opponent controls."); - Target target = new TargetCreaturePermanent(filter); + Target target = new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE); ability.addTarget(target); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/h/HadanasClimb.java b/Mage.Sets/src/mage/cards/h/HadanasClimb.java index 633a238a48c..223b770c7c3 100644 --- a/Mage.Sets/src/mage/cards/h/HadanasClimb.java +++ b/Mage.Sets/src/mage/cards/h/HadanasClimb.java @@ -27,14 +27,13 @@ public final class HadanasClimb extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{G}{U}"); this.addSuperType(SuperType.LEGENDARY); - this.transformable = true; this.secondSideCardClazz = mage.cards.w.WingedTempleOfOrazca.class; // At the beginning of combat on your turn, put a +1/+1 counter on target creature you control. Then if that creature has three or more +1/+1 counters on it, transform Hadana's Climb. this.addAbility(new TransformAbility()); Ability ability = new BeginningOfCombatTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance()), TargetController.YOU, false); - ability.addEffect(new ConditionalOneShotEffect(new TransformSourceEffect(true), new TargetHasCounterCondition(CounterType.P1P1, 3, Integer.MAX_VALUE), + ability.addEffect(new ConditionalOneShotEffect(new TransformSourceEffect(), new TargetHasCounterCondition(CounterType.P1P1, 3, Integer.MAX_VALUE), "Then if that creature has three or more +1/+1 counters on it, transform {this}")); ability.addTarget(new TargetControlledCreaturePermanent()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/h/HagHedgeMage.java b/Mage.Sets/src/mage/cards/h/HagHedgeMage.java index bf5be6e35eb..0374a5b1b44 100644 --- a/Mage.Sets/src/mage/cards/h/HagHedgeMage.java +++ b/Mage.Sets/src/mage/cards/h/HagHedgeMage.java @@ -33,8 +33,8 @@ public final class HagHedgeMage extends CardImpl { filter2.add(SubType.FOREST.getPredicate()); } - private String rule = "When {this} enters the battlefield, if you control two or more Swamps, you may have target player discard a card."; - private String rule2 = "When {this} enters the battlefield, if you control two or more Forests, you may put target card from your graveyard on top of your library."; + private static final String rule = "When {this} enters the battlefield, if you control two or more Swamps, you may have target player discard a card."; + private static final String rule2 = "When {this} enters the battlefield, if you control two or more Forests, you may put target card from your graveyard on top of your library."; public HagHedgeMage(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{B/G}"); diff --git a/Mage.Sets/src/mage/cards/h/HagraConstrictor.java b/Mage.Sets/src/mage/cards/h/HagraConstrictor.java index a4a535216a0..a75d9f0d0cc 100644 --- a/Mage.Sets/src/mage/cards/h/HagraConstrictor.java +++ b/Mage.Sets/src/mage/cards/h/HagraConstrictor.java @@ -12,8 +12,7 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; import mage.counters.CounterType; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; import java.util.UUID; @@ -22,12 +21,6 @@ import java.util.UUID; */ public final class HagraConstrictor extends CardImpl { - private static final FilterPermanent filter = new FilterControlledCreaturePermanent(); - - static { - filter.add(CounterType.P1P1.getPredicate()); - } - public HagraConstrictor(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); @@ -43,8 +36,11 @@ public final class HagraConstrictor extends CardImpl { // Each creature you control with a +1/+1 counter on it has menace. this.addAbility(new SimpleStaticAbility(new GainAbilityAllEffect( - new MenaceAbility(), Duration.WhileOnBattlefield, filter, - "Each creature you control with a +1/+1 counter on it has menace" + new MenaceAbility(), + Duration.WhileOnBattlefield, + StaticFilters.FILTER_EACH_CONTROLLED_CREATURE_P1P1, + "Each creature you control with a +1/+1 counter on it has menace. " + + "(A creature with menace can't be blocked except by two or more creatures.)" ))); } diff --git a/Mage.Sets/src/mage/cards/h/HailOfArrows.java b/Mage.Sets/src/mage/cards/h/HailOfArrows.java index a6d58a05a38..0e11d72867f 100644 --- a/Mage.Sets/src/mage/cards/h/HailOfArrows.java +++ b/Mage.Sets/src/mage/cards/h/HailOfArrows.java @@ -1,4 +1,3 @@ - package mage.cards.h; import java.util.UUID; @@ -7,7 +6,7 @@ import mage.abilities.effects.common.DamageMultiEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterAttackingCreature; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanentAmount; /** @@ -21,7 +20,7 @@ public final class HailOfArrows extends CardImpl { // Hail of Arrows deals X damage divided as you choose among any number of target attacking creatures. this.getSpellAbility().addEffect(new DamageMultiEffect(ManacostVariableValue.REGULAR)); - this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount(ManacostVariableValue.REGULAR, new FilterAttackingCreature())); + this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount(ManacostVariableValue.REGULAR, StaticFilters.FILTER_ATTACKING_CREATURES)); } private HailOfArrows(final HailOfArrows card) { diff --git a/Mage.Sets/src/mage/cards/h/HakimLoreweaver.java b/Mage.Sets/src/mage/cards/h/HakimLoreweaver.java index 8828f590c57..be8563dd853 100644 --- a/Mage.Sets/src/mage/cards/h/HakimLoreweaver.java +++ b/Mage.Sets/src/mage/cards/h/HakimLoreweaver.java @@ -1,4 +1,3 @@ - package mage.cards.h; import mage.MageInt; @@ -17,19 +16,18 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.FilterCard; import mage.filter.FilterPermanent; -import mage.filter.predicate.mageobject.CardIdPredicate; +import mage.filter.predicate.Predicate; import mage.filter.predicate.card.AuraCardCanAttachToPermanentId; -import mage.filter.predicate.permanent.AttachedToPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.Target; import mage.target.common.TargetCardInYourGraveyard; +import java.util.Objects; import java.util.UUID; /** - * * @author jeffwadsworth, TheElk801 */ public final class HakimLoreweaver extends CardImpl { @@ -58,22 +56,16 @@ public final class HakimLoreweaver extends CardImpl { Ability ability = new ConditionalActivatedAbility( Zone.BATTLEFIELD, new HakimLoreweaverEffect(), - new ManaCostsImpl("{U}{U}"), - new HakimLoreweaverCondition()); + new ManaCostsImpl<>("{U}{U}"), + HakimLoreweaverCondition.instance + ); ability.addTarget(new TargetCardInYourGraveyard(filter)); this.addAbility(ability); // {U}{U}, {tap}: Destroy all Auras attached to Hakim. - FilterPermanent filterAurasOnHakim = new FilterPermanent("Auras attached to Hakim"); - filterAurasOnHakim.add(CardType.ENCHANTMENT.getPredicate()); - filterAurasOnHakim.add(SubType.AURA.getPredicate()); - FilterPermanent filterSourceId = new FilterPermanent(); - filterSourceId.add(new CardIdPredicate(this.getId())); - filterAurasOnHakim.add(new AttachedToPredicate(filterSourceId)); - Ability ability2 = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyAllEffect(filterAurasOnHakim), new ManaCostsImpl("{U}{U}")); + Ability ability2 = new SimpleActivatedAbility(new HakimLoreweaverEffect(), new ManaCostsImpl<>("{U}{U}")); ability2.addCost(new TapSourceCost()); this.addAbility(ability2); - } private HakimLoreweaver(final HakimLoreweaver card) { @@ -123,8 +115,8 @@ class HakimLoreweaverEffect extends OneShotEffect { } } -class HakimLoreweaverCondition implements Condition { - +enum HakimLoreweaverCondition implements Condition { + instance; static private final FilterPermanent auras = new FilterPermanent(); static { @@ -133,18 +125,18 @@ class HakimLoreweaverCondition implements Condition { @Override public boolean apply(Game game, Ability source) { - Permanent hakimLoreweaver = game.getPermanent(source.getSourceId()); - if (hakimLoreweaver != null) { - for (Permanent permanent : game.getBattlefield().getAllActivePermanents(auras, game)) { - if (permanent != null - && hakimLoreweaver.getAttachments().contains(permanent.getId())) { - return false; - } - } - return PhaseStep.UPKEEP == game.getStep().getType() - && game.isActivePlayer(source.getControllerId()); + if (PhaseStep.UPKEEP != game.getStep().getType() + || !game.isActivePlayer(source.getControllerId())) { + return false; } - return false; + Permanent hakimLoreweaver = source.getSourcePermanentIfItStillExists(game); + return hakimLoreweaver != null + && hakimLoreweaver + .getAttachments() + .stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .anyMatch(permanent -> permanent.hasSubtype(SubType.AURA, game)); } @Override @@ -152,3 +144,46 @@ class HakimLoreweaverCondition implements Condition { return "during your upkeep and only if {this} isn't enchanted"; } } + +class HakimLoreweaverDestroyEffect extends OneShotEffect { + + HakimLoreweaverDestroyEffect() { + super(Outcome.Benefit); + staticText = "destroy all Auras attached to {this}"; + } + + private HakimLoreweaverDestroyEffect(final HakimLoreweaverDestroyEffect effect) { + super(effect); + } + + @Override + public HakimLoreweaverDestroyEffect copy() { + return new HakimLoreweaverDestroyEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (permanent == null) { + return false; + } + FilterPermanent filter = new FilterPermanent(); + filter.add(SubType.AURA.getPredicate()); + filter.add(new HakimLoreweaverPredicate(permanent)); + return new DestroyAllEffect(filter).apply(game, source); + } +} + +class HakimLoreweaverPredicate implements Predicate { + + private final Permanent permanent; + + HakimLoreweaverPredicate(Permanent permanent) { + this.permanent = permanent; + } + + @Override + public boolean apply(Permanent input, Game game) { + return input.isAttachedTo(permanent.getId()); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/h/HalanaAndAlenaPartners.java b/Mage.Sets/src/mage/cards/h/HalanaAndAlenaPartners.java new file mode 100644 index 00000000000..d3ade280166 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HalanaAndAlenaPartners.java @@ -0,0 +1,66 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfCombatTriggeredAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.SourcePermanentPowerCount; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.ReachAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HalanaAndAlenaPartners extends CardImpl { + + private static final DynamicValue xValue = new SourcePermanentPowerCount(); + + public HalanaAndAlenaPartners(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.RANGER); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // First strike + this.addAbility(FirstStrikeAbility.getInstance()); + + // Reach + this.addAbility(ReachAbility.getInstance()); + + // At the beginning of combat on your turn, put X +1/+1 counters on another target creature you control, where X is Halana and Alena's power. That creature gains haste until end of turn. + Ability ability = new BeginningOfCombatTriggeredAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance(0), xValue) + .setText("put X +1/+1 counters on another target creature you control, where X is {this}'s power"), + TargetController.YOU, false + ); + ability.addEffect(new GainAbilityTargetEffect( + HasteAbility.getInstance(), Duration.EndOfTurn + ).setText("That creature gains haste until end of turn")); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE)); + this.addAbility(ability); + } + + private HalanaAndAlenaPartners(final HalanaAndAlenaPartners card) { + super(card); + } + + @Override + public HalanaAndAlenaPartners copy() { + return new HalanaAndAlenaPartners(this); + } +} +// oh my god they were roommates diff --git a/Mage.Sets/src/mage/cards/h/HallOfTheBanditLord.java b/Mage.Sets/src/mage/cards/h/HallOfTheBanditLord.java index 377f17144e3..2f0eef210b8 100644 --- a/Mage.Sets/src/mage/cards/h/HallOfTheBanditLord.java +++ b/Mage.Sets/src/mage/cards/h/HallOfTheBanditLord.java @@ -1,4 +1,3 @@ - package mage.cards.h; import java.util.ArrayList; @@ -33,7 +32,7 @@ import mage.watchers.Watcher; public final class HallOfTheBanditLord extends CardImpl { public HallOfTheBanditLord(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.LAND},""); + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); addSuperType(SuperType.LEGENDARY); // Hall of the Bandit Lord enters the battlefield tapped. @@ -99,7 +98,7 @@ class HallOfTheBanditLordWatcher extends Watcher { if (event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD) { if (creatures.contains(event.getSourceId())) { ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.Custom); - effect.setTargetPointer(new FixedTarget(event.getSourceId())); + effect.setTargetPointer(new FixedTarget(event.getSourceId(), game)); game.addEffect(effect, source); creatures.remove(event.getSourceId()); } diff --git a/Mage.Sets/src/mage/cards/h/HallowedHaunting.java b/Mage.Sets/src/mage/cards/h/HallowedHaunting.java new file mode 100644 index 00000000000..2e01e399f14 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HallowedHaunting.java @@ -0,0 +1,77 @@ +package mage.cards.h; + +import java.util.UUID; + +import mage.abilities.Ability; +import mage.abilities.CompoundAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.filter.FilterSpell; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.SpiritClericToken; + +/** + * + * @author weirddan455 + */ +public final class HallowedHaunting extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("an enchantment spell"); + + static { + filter.add(CardType.ENCHANTMENT.getPredicate()); + } + + public HallowedHaunting(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}{W}"); + + // As long as you control seven or more enchantments, creatures you control have flying and vigilance. + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new GainAbilityControlledEffect(new CompoundAbility(FlyingAbility.getInstance(), VigilanceAbility.getInstance()), Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURES), + HallowedHauntingCondition.instance, + "As long as you control seven or more enchantments, creatures you control have flying and vigilance" + ))); + + // Whenever you cast an enchantment spell, create a white Spirit Cleric creature token with "This creature's power and toughness are each equal to the number of Spirits you control." + this.addAbility(new SpellCastControllerTriggeredAbility(new CreateTokenEffect(new SpiritClericToken()), filter, false)); + } + + private HallowedHaunting(final HallowedHaunting card) { + super(card); + } + + @Override + public HallowedHaunting copy() { + return new HallowedHaunting(this); + } +} + +enum HallowedHauntingCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + int enchantments = 0; + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(source.getControllerId())) { + if (permanent.getCardType(game).contains(CardType.ENCHANTMENT)) { + enchantments++; + if (enchantments >= 7) { + return true; + } + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/h/HallowedRespite.java b/Mage.Sets/src/mage/cards/h/HallowedRespite.java index c7176564068..8b790cd7edb 100644 --- a/Mage.Sets/src/mage/cards/h/HallowedRespite.java +++ b/Mage.Sets/src/mage/cards/h/HallowedRespite.java @@ -38,7 +38,7 @@ public final class HallowedRespite extends CardImpl { // Exile target nonlegendary creature, then return it to the battlefield under its owner's control. If it entered under your control, put a +1/+1 counter on it. Otherwise, tap it. this.getSpellAbility().addEffect(new ExileTargetForSourceEffect()); - this.getSpellAbility().addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false)); + this.getSpellAbility().addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false).setText(", then return it to the battlefield under its owner's control")); this.getSpellAbility().addEffect(new HallowedRespiteEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); diff --git a/Mage.Sets/src/mage/cards/h/HamletVanguard.java b/Mage.Sets/src/mage/cards/h/HamletVanguard.java new file mode 100644 index 00000000000..28fc81507f9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HamletVanguard.java @@ -0,0 +1,66 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.WardAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.filter.predicate.permanent.TokenPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HamletVanguard extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledCreaturePermanent(SubType.HUMAN); + + static { + filter.add(AnotherPredicate.instance); + filter.add(TokenPredicate.FALSE); + } + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter, 2); + private static final Hint hint = new ValueHint( + "Other nontoken Humans you control", new PermanentsOnBattlefieldCount(filter) + ); + + public HamletVanguard(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Ward {2} + this.addAbility(new WardAbility(new ManaCostsImpl<>("{2}"))); + + // Hamlet Vanguard enters the battlefield with two +1/+1 counters on it for each other nontoken Human you control. + this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect( + CounterType.P1P1.createInstance(0), xValue, false + ), "with two +1/+1 counters on it for each other nontoken Human you control").addHint(hint)); + } + + private HamletVanguard(final HamletVanguard card) { + super(card); + } + + @Override + public HamletVanguard copy() { + return new HamletVanguard(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HammerHelper.java b/Mage.Sets/src/mage/cards/h/HammerHelper.java index aed505c95c7..01e47dad6d1 100644 --- a/Mage.Sets/src/mage/cards/h/HammerHelper.java +++ b/Mage.Sets/src/mage/cards/h/HammerHelper.java @@ -1,4 +1,3 @@ - package mage.cards.h; import java.util.UUID; @@ -64,7 +63,7 @@ class HammerHelperEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); Permanent targetCreature = game.getPermanent(source.getFirstTarget()); if (controller != null && targetCreature != null) { - source.getEffects().get(0).setTargetPointer(new FixedTarget(targetCreature.getId())); + source.getEffects().get(0).setTargetPointer(new FixedTarget(targetCreature.getId(), game)); game.addEffect(new GainControlTargetEffect(Duration.EndOfTurn), source); targetCreature.untap(game); int amount = controller.rollDice(outcome, source, game, 6); diff --git a/Mage.Sets/src/mage/cards/h/HamzaGuardianOfArashin.java b/Mage.Sets/src/mage/cards/h/HamzaGuardianOfArashin.java index 72f66411d25..4fae1072b66 100644 --- a/Mage.Sets/src/mage/cards/h/HamzaGuardianOfArashin.java +++ b/Mage.Sets/src/mage/cards/h/HamzaGuardianOfArashin.java @@ -14,9 +14,7 @@ import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.counters.CounterType; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.util.CardUtil; @@ -27,14 +25,7 @@ import java.util.UUID; */ public final class HamzaGuardianOfArashin extends CardImpl { - private static final FilterPermanent filter - = new FilterControlledCreaturePermanent("creature you control with a +1/+1 counter on it"); - - static { - filter.add(CounterType.P1P1.getPredicate()); - } - - private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter); + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(StaticFilters.FILTER_CONTROLLED_CREATURE_P1P1); private static final Hint hint = new ValueHint("Creatures you control with +1/+1 counter on them", xValue); public HamzaGuardianOfArashin(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/h/HandOfDeath.java b/Mage.Sets/src/mage/cards/h/HandOfDeath.java index a0a24b87b1c..91abfc5034b 100644 --- a/Mage.Sets/src/mage/cards/h/HandOfDeath.java +++ b/Mage.Sets/src/mage/cards/h/HandOfDeath.java @@ -1,15 +1,11 @@ - package mage.cards.h; import java.util.UUID; -import mage.ObjectColor; import mage.abilities.effects.common.DestroyTargetEffect; 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; /** @@ -18,18 +14,11 @@ import mage.target.common.TargetCreaturePermanent; */ public final class HandOfDeath extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public HandOfDeath(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{B}"); - // Destroy target nonblack creature. - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); this.getSpellAbility().addEffect(new DestroyTargetEffect()); } diff --git a/Mage.Sets/src/mage/cards/h/HandOfEnlightenment.java b/Mage.Sets/src/mage/cards/h/HandOfEnlightenment.java new file mode 100644 index 00000000000..bd13b8a3582 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HandOfEnlightenment.java @@ -0,0 +1,39 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HandOfEnlightenment extends CardImpl { + + public HandOfEnlightenment(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, ""); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.MONK); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + this.color.setWhite(true); + this.nightCard = true; + + // First strike + this.addAbility(FirstStrikeAbility.getInstance()); + } + + private HandOfEnlightenment(final HandOfEnlightenment card) { + super(card); + } + + @Override + public HandOfEnlightenment copy() { + return new HandOfEnlightenment(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HandsOfBinding.java b/Mage.Sets/src/mage/cards/h/HandsOfBinding.java index 49a8f4265f9..2823bc74225 100644 --- a/Mage.Sets/src/mage/cards/h/HandsOfBinding.java +++ b/Mage.Sets/src/mage/cards/h/HandsOfBinding.java @@ -1,4 +1,3 @@ - package mage.cards.h; import java.util.UUID; @@ -9,7 +8,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; /** @@ -18,23 +17,15 @@ import mage.target.common.TargetCreaturePermanent; */ public final class HandsOfBinding extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - static{ - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public HandsOfBinding (UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{U}"); - //Tap target creature an opponent controls. That creature doesn't untap during its controller's next untap step. this.getSpellAbility().addEffect(new TapTargetEffect()); this.getSpellAbility().addEffect(new DontUntapInControllersNextUntapStepTargetEffect()); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); //Cipher this.getSpellAbility().addEffect(new CipherEffect()); - - } private HandsOfBinding(final HandsOfBinding card) { @@ -42,7 +33,7 @@ public final class HandsOfBinding extends CardImpl { } @Override - public HandsOfBinding copy() { + public HandsOfBinding copy() { return new HandsOfBinding(this); } } diff --git a/Mage.Sets/src/mage/cards/h/Hankyu.java b/Mage.Sets/src/mage/cards/h/Hankyu.java index 7f32ff0c4c9..0738177750b 100644 --- a/Mage.Sets/src/mage/cards/h/Hankyu.java +++ b/Mage.Sets/src/mage/cards/h/Hankyu.java @@ -4,11 +4,14 @@ import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.Cost; -import mage.abilities.costs.CostImpl; +import mage.abilities.costs.UseAttachedCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.abilities.keyword.EquipAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -16,13 +19,14 @@ import mage.constants.*; import mage.counters.CounterType; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.common.TargetAnyTarget; +import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.UUID; /** - * @author North + * @author TheElk801 */ public final class Hankyu extends CardImpl { @@ -30,18 +34,8 @@ public final class Hankyu extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}"); this.subtype.add(SubType.EQUIPMENT); - /* Equipped creature has "{T}: Put an aim counter on Hankyu" and */ - SimpleActivatedAbility ability1 = new SimpleActivatedAbility(Zone.BATTLEFIELD, new HankyuAddCounterEffect(this.getId()), new TapSourceCost()); - ability1.setSourceId(this.getId()); // to know where to put the counters on - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(ability1, AttachmentType.EQUIPMENT))); - - /* "{T}, Remove all aim counters from Hankyu: This creature deals - * damage to any target equal to the number of - * aim counters removed this way." */ - SimpleActivatedAbility ability2 = new SimpleActivatedAbility(Zone.BATTLEFIELD, new HankyuDealsDamageEffect(), new TapSourceCost()); - ability2.addCost(new HankyuCountersSourceCost(this.getId())); - ability2.addTarget(new TargetAnyTarget()); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(ability2, AttachmentType.EQUIPMENT))); + // Equipped creature has "{T}: Put an aim counter on {this}" and "{T}, Remove all aim counters from {this}: This creature deals damage to any target equal to the number of aim counters removed this way." + this.addAbility(new SimpleStaticAbility(new HankyuEffect())); // Equip {4} ({4}: Attach to target creature you control. Equip only as a sorcery.) this.addAbility(new EquipAbility(Outcome.BoostCreature, new GenericManaCost(4))); @@ -57,121 +51,125 @@ public final class Hankyu extends CardImpl { } } -class HankyuAddCounterEffect extends OneShotEffect { +class HankyuEffect extends ContinuousEffectImpl { - private UUID effectGivingEquipmentId; - - public HankyuAddCounterEffect(UUID effectGivingEquipmentId) { - super(Outcome.Benefit); - this.effectGivingEquipmentId = effectGivingEquipmentId; - staticText = "Put an aim counter on Hankyu"; + HankyuEffect() { + super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); + staticText = "equipped creature has \"{T}: Put an aim counter on {this}\" and " + + "\"{T}, Remove all aim counters from {this}: This creature deals damage " + + "to any target equal to the number of aim counters removed this way.\""; } - public HankyuAddCounterEffect(final HankyuAddCounterEffect effect) { + private HankyuEffect(final HankyuEffect effect) { super(effect); - this.effectGivingEquipmentId = effect.effectGivingEquipmentId; + } + + @Override + public HankyuEffect copy() { + return new HankyuEffect(this); } @Override public boolean apply(Game game, Ability source) { - Permanent equipment = game.getPermanent(this.effectGivingEquipmentId); - if (equipment != null) { - equipment.addCounters(CounterType.AIM.createInstance(), source.getControllerId(), source, game); + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (permanent == null) { + return false; } + Permanent creature = game.getPermanent(permanent.getAttachedTo()); + if (creature == null) { + return false; + } + creature.addAbility(new SimpleActivatedAbility( + new AddCountersTargetEffect(CounterType.AIM.createInstance()) + .setTargetPointer(new FixedTarget(permanent, game)) + .setText("put an aim counter on " + permanent.getName()), + new TapSourceCost() + ), source.getSourceId(), game); + Ability ability = new SimpleActivatedAbility( + new DamageTargetEffect(HankyuValue.instance) + .setText("this creature deals damage to any target equal " + + "to the number of aim counters removed this way"), + new TapSourceCost() + ); + ability.addCost(new HankyuCost().setMageObjectReference(source, game)); + ability.addTarget(new TargetAnyTarget()); + creature.addAbility(ability, source.getSourceId(), game); return true; } - - @Override - public HankyuAddCounterEffect copy() { - return new HankyuAddCounterEffect(this); - } - - } +enum HankyuValue implements DynamicValue { + instance; -class HankyuDealsDamageEffect extends OneShotEffect { - - public HankyuDealsDamageEffect() { - super(Outcome.Damage); - staticText = "This creature deals damage to any target equal to the number of aim counters removed this way"; - } - - public HankyuDealsDamageEffect(final HankyuDealsDamageEffect effect) { - super(effect); + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + return CardUtil.castStream( + sourceAbility.getCosts().stream(), HankyuCost.class + ).mapToInt(HankyuCost::getRemovedCounters).sum(); } @Override - public HankyuDealsDamageEffect copy() { - return new HankyuDealsDamageEffect(this); + public HankyuValue copy() { + return this; } @Override - public boolean apply(Game game, Ability source) { - // get the number of removed counters as damage amount - HankyuCountersSourceCost cost = (HankyuCountersSourceCost) source.getCosts().get(1); - if (cost != null) { - int damageAmount = cost.getRemovedCounters(); - if (damageAmount > 0) { - - Permanent permanent = game.getPermanent(source.getFirstTarget()); - if (permanent != null) { - permanent.damage(damageAmount, source.getSourceId(), source, game, false, true); - } - Player player = game.getPlayer(source.getFirstTarget()); - if (player != null) { - player.damage(damageAmount, source.getSourceId(), source, game); - } - } - return true; - } - return false; + public String getMessage() { + return ""; } - } -class HankyuCountersSourceCost extends CostImpl { +class HankyuCost extends UseAttachedCost { - private int removedCounters; - private UUID effectGivingEquipmentId; + private int removedCounters = 0; - public HankyuCountersSourceCost(UUID effectGivingEquipmentId) { + HankyuCost() { super(); - this.removedCounters = 0; - this.effectGivingEquipmentId = effectGivingEquipmentId; - this.text = "Remove all aim counters from Hankyu"; } - public HankyuCountersSourceCost(HankyuCountersSourceCost cost) { + private HankyuCost(final HankyuCost cost) { super(cost); - this.effectGivingEquipmentId = cost.effectGivingEquipmentId; this.removedCounters = cost.removedCounters; } - @Override - public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) { - return true; - } - @Override public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) { - Permanent equipment = game.getPermanent(this.effectGivingEquipmentId); - if (equipment != null) { - this.removedCounters = equipment.getCounters(game).getCount(CounterType.AIM); - if (this.removedCounters > 0) { - equipment.removeCounters("aim", this.removedCounters, source, game); - } + if (mageObjectReference == null) { + return false; } - this.paid = true; - return true; + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent == null) { + return paid; + } + for (UUID attachmentId : permanent.getAttachments()) { + if (!this.mageObjectReference.refersTo(attachmentId, game)) { + continue; + } + Permanent equipment = mageObjectReference.getPermanent(game); + if (equipment == null) { + continue; + } + int count = equipment.getCounters(game).getCount(CounterType.AIM); + equipment.removeCounters(CounterType.AIM.createInstance(count), source, game); + paid = true; + removedCounters = count; + break; + } + + return paid; } @Override - public HankyuCountersSourceCost copy() { - return new HankyuCountersSourceCost(this); + public HankyuCost copy() { + return new HankyuCost(this); + } + + @Override + public String getText() { + return "remove all aim counters from " + this.name; } public int getRemovedCounters() { - return this.removedCounters; + return removedCounters; } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/h/HanweirMilitiaCaptain.java b/Mage.Sets/src/mage/cards/h/HanweirMilitiaCaptain.java index dceb0b20ec4..2e1a11f1925 100644 --- a/Mage.Sets/src/mage/cards/h/HanweirMilitiaCaptain.java +++ b/Mage.Sets/src/mage/cards/h/HanweirMilitiaCaptain.java @@ -31,13 +31,12 @@ public final class HanweirMilitiaCaptain extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); - this.transformable = true; this.secondSideCardClazz = mage.cards.w.WestvaleCultLeader.class; // At the beginning of your upkeep, if you control four or more creatures, transform Hanweir Militia Captain. this.addAbility(new TransformAbility()); this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(true), TargetController.YOU, false), + new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(), TargetController.YOU, false), new PermanentsOnTheBattlefieldCondition(filter, ComparisonType.MORE_THAN, 3), "At the beginning of your upkeep, if you control four or more creatures, transform {this}")); } diff --git a/Mage.Sets/src/mage/cards/h/HanweirWatchkeep.java b/Mage.Sets/src/mage/cards/h/HanweirWatchkeep.java index cd550520cca..85997f17d36 100644 --- a/Mage.Sets/src/mage/cards/h/HanweirWatchkeep.java +++ b/Mage.Sets/src/mage/cards/h/HanweirWatchkeep.java @@ -22,7 +22,6 @@ public final class HanweirWatchkeep extends CardImpl { this.subtype.add(SubType.WARRIOR); this.subtype.add(SubType.WEREWOLF); - this.transformable = true; this.secondSideCardClazz = mage.cards.b.BaneOfHanweir.class; this.power = new MageInt(1); diff --git a/Mage.Sets/src/mage/cards/h/HaraldUnitesTheElves.java b/Mage.Sets/src/mage/cards/h/HaraldUnitesTheElves.java index 0eb54cf3f50..0f3a8467b21 100644 --- a/Mage.Sets/src/mage/cards/h/HaraldUnitesTheElves.java +++ b/Mage.Sets/src/mage/cards/h/HaraldUnitesTheElves.java @@ -39,7 +39,7 @@ public final class HaraldUnitesTheElves extends CardImpl { this.subtype.add(SubType.SAGA); // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) - SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); + SagaAbility sagaAbility = new SagaAbility(this); // I — Mill three cards. You may put an Elf or Tyvar card from your graveyard onto the battlefield. sagaAbility.addChapterEffect( diff --git a/Mage.Sets/src/mage/cards/h/HargildeKindlyRunechanter.java b/Mage.Sets/src/mage/cards/h/HargildeKindlyRunechanter.java new file mode 100644 index 00000000000..a850f611fd5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HargildeKindlyRunechanter.java @@ -0,0 +1,83 @@ +package mage.cards.h; + +import mage.ConditionalMana; +import mage.MageInt; +import mage.MageObject; +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.condition.Condition; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.keyword.FriendsForeverAbility; +import mage.abilities.mana.ConditionalColorlessManaAbility; +import mage.abilities.mana.builder.ConditionalManaBuilder; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.game.Game; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HargildeKindlyRunechanter extends CardImpl { + + public HargildeKindlyRunechanter(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // {T}: Add {C}{C}. Spend this mana only to cast artifact spells or activate abilities of artifacts. + this.addAbility(new ConditionalColorlessManaAbility( + new TapSourceCost(), 2, new HargildeKindlyRunechanterManaBuilder() + )); + + // Friends forever + this.addAbility(FriendsForeverAbility.getInstance()); + } + + private HargildeKindlyRunechanter(final HargildeKindlyRunechanter card) { + super(card); + } + + @Override + public HargildeKindlyRunechanter copy() { + return new HargildeKindlyRunechanter(this); + } +} + +class HargildeKindlyRunechanterManaBuilder extends ConditionalManaBuilder { + + @Override + public ConditionalMana build(Object... options) { + return new HargildeKindlyRunechanterConditionalMana(this.mana); + } + + @Override + public String getRule() { + return "Spend this mana only to cast artifact spells or activate abilities of artifacts"; + } +} + +class HargildeKindlyRunechanterConditionalMana extends ConditionalMana { + + HargildeKindlyRunechanterConditionalMana(Mana mana) { + super(mana); + addCondition(HargildeKindlyRunechanterCondition.instance); + } +} + +enum HargildeKindlyRunechanterCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + MageObject object = game.getObject(source.getSourceId()); + return object != null && object.isArtifact(game); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HarmoniousEmergence.java b/Mage.Sets/src/mage/cards/h/HarmoniousEmergence.java new file mode 100644 index 00000000000..6da9ed6861b --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HarmoniousEmergence.java @@ -0,0 +1,115 @@ +package mage.cards.h; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.continuous.BecomesCreatureAttachedEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.IndestructibleAbility; +import mage.abilities.keyword.VigilanceAbility; +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.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.custom.CreatureToken; +import mage.target.TargetPermanent; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HarmoniousEmergence extends CardImpl { + + public HarmoniousEmergence(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{G}"); + + this.subtype.add(SubType.AURA); + + // Enchant land you control + TargetPermanent auraTarget = new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + this.addAbility(new EnchantAbility(auraTarget.getTargetName())); + + // Enchanted land is a 4/5 green Spirit creature with vigilance and haste. It's still a land. + this.addAbility(new SimpleStaticAbility(new BecomesCreatureAttachedEffect( + new CreatureToken(4, 5) + .withColor("G") + .withSubType(SubType.SPIRIT) + .withAbility(VigilanceAbility.getInstance()) + .withAbility(HasteAbility.getInstance()), + "enchanted land is a 4/5 green Spirit creature with vigilance and haste. It's still a land", + Duration.WhileOnBattlefield, BecomesCreatureAttachedEffect.LoseType.COLOR + ))); + + // If enchanted land would be destroyed, instead sacrifice Harmonious Emergence and that land gains indestructible until end of turn. + this.addAbility(new SimpleStaticAbility(new HarmoniousEmergenceEffect())); + } + + private HarmoniousEmergence(final HarmoniousEmergence card) { + super(card); + } + + @Override + public HarmoniousEmergence copy() { + return new HarmoniousEmergence(this); + } +} + +class HarmoniousEmergenceEffect extends ReplacementEffectImpl { + + HarmoniousEmergenceEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "if enchanted land would be destroyed, instead sacrifice " + + "{this} and that land gains indestructible until end of turn"; + } + + private HarmoniousEmergenceEffect(final HarmoniousEmergenceEffect effect) { + super(effect); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game); + Permanent enchantedPermanent = game.getPermanent(event.getTargetId()); + if (sourcePermanent == null || enchantedPermanent == null) { + return false; + } + sourcePermanent.sacrifice(source, game); + game.addEffect(new GainAbilityTargetEffect(IndestructibleAbility.getInstance()) + .setTargetPointer(new FixedTarget(enchantedPermanent, game)), source); + return true; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DESTROY_PERMANENT; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game); + return sourcePermanent != null && event.getTargetId().equals(sourcePermanent.getAttachedTo()); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public HarmoniousEmergenceEffect copy() { + return new HarmoniousEmergenceEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HarshScrutiny.java b/Mage.Sets/src/mage/cards/h/HarshScrutiny.java index 9025809e23e..b337645e8a2 100644 --- a/Mage.Sets/src/mage/cards/h/HarshScrutiny.java +++ b/Mage.Sets/src/mage/cards/h/HarshScrutiny.java @@ -7,7 +7,7 @@ import mage.abilities.effects.keyword.ScryEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterCreatureCard; +import mage.filter.StaticFilters; import mage.target.common.TargetOpponent; /** @@ -21,8 +21,8 @@ public final class HarshScrutiny extends CardImpl { // Target opponent reveals their hand. You choose a creature card from it. That player discards that card. Scry 1. this.getSpellAbility().addTarget(new TargetOpponent()); - this.getSpellAbility().addEffect(new DiscardCardYouChooseTargetEffect(new FilterCreatureCard("a creature card"))); - this.getSpellAbility().addEffect(new ScryEffect(1)); + this.getSpellAbility().addEffect(new DiscardCardYouChooseTargetEffect(StaticFilters.FILTER_CARD_CREATURE)); + this.getSpellAbility().addEffect(new ScryEffect(1, false)); } private HarshScrutiny(final HarshScrutiny card) { diff --git a/Mage.Sets/src/mage/cards/h/HaruOnna.java b/Mage.Sets/src/mage/cards/h/HaruOnna.java index 211676cac39..718971ba425 100644 --- a/Mage.Sets/src/mage/cards/h/HaruOnna.java +++ b/Mage.Sets/src/mage/cards/h/HaruOnna.java @@ -29,7 +29,7 @@ public final class HaruOnna extends CardImpl { // When Haru-Onna enters the battlefield, draw a card. this.addAbility(new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(1))); // Whenever you cast a Spirit or Arcane spell, you may return Haru-Onna to its owner's hand. - this.addAbility(new SpellCastControllerTriggeredAbility(new ReturnToHandSourceEffect(true), StaticFilters.SPIRIT_OR_ARCANE_CARD, true)); + this.addAbility(new SpellCastControllerTriggeredAbility(new ReturnToHandSourceEffect(true), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true)); } private HaruOnna(final HaruOnna card) { diff --git a/Mage.Sets/src/mage/cards/h/HarvestHand.java b/Mage.Sets/src/mage/cards/h/HarvestHand.java index a5af36de700..10572bb1ed3 100644 --- a/Mage.Sets/src/mage/cards/h/HarvestHand.java +++ b/Mage.Sets/src/mage/cards/h/HarvestHand.java @@ -1,9 +1,7 @@ - package mage.cards.h; -import java.util.UUID; - import mage.MageInt; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; @@ -12,12 +10,14 @@ import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; import mage.players.Player; +import java.util.UUID; + /** * @author halljared */ @@ -29,7 +29,6 @@ public final class HarvestHand extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); - this.transformable = true; this.secondSideCardClazz = mage.cards.s.ScroungedScythe.class; // When Harvest Hand dies, return it to the battlefield transformed under your control. @@ -49,12 +48,12 @@ public final class HarvestHand extends CardImpl { class HarvestHandReturnTransformedEffect extends OneShotEffect { - public HarvestHandReturnTransformedEffect() { + HarvestHandReturnTransformedEffect() { super(Outcome.PutCardInPlay); this.staticText = "Return {this} to the battlefield transformed under your control"; } - public HarvestHandReturnTransformedEffect(final HarvestHandReturnTransformedEffect effect) { + private HarvestHandReturnTransformedEffect(final HarvestHandReturnTransformedEffect effect) { super(effect); } @@ -66,17 +65,15 @@ class HarvestHandReturnTransformedEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - if (game.getState().getZone(source.getSourceId()) == Zone.GRAVEYARD) { - game.getState().setValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + source.getSourceId(), Boolean.TRUE); - Card card = game.getCard(source.getSourceId()); - if (card != null) { - controller.moveCards(card, Zone.BATTLEFIELD, source, game); - } - } - return true; + if (controller == null) { + return false; } - return false; + MageObject sourceObject = source.getSourceObjectIfItStillExists(game); + if (!(sourceObject instanceof Card)) { + return false; + } + game.getState().setValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + source.getSourceId(), Boolean.TRUE); + controller.moveCards((Card) sourceObject, Zone.BATTLEFIELD, source, game); + return true; } - -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/h/HarvestPyre.java b/Mage.Sets/src/mage/cards/h/HarvestPyre.java index ba41305a5ef..97a2971f3a1 100644 --- a/Mage.Sets/src/mage/cards/h/HarvestPyre.java +++ b/Mage.Sets/src/mage/cards/h/HarvestPyre.java @@ -8,7 +8,7 @@ import mage.abilities.effects.common.DamageTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; /** @@ -22,7 +22,7 @@ public final class HarvestPyre extends CardImpl { // As an additional cost to cast Harvest Pyre, exile X cards from your graveyard. - this.getSpellAbility().addCost(new ExileXFromYourGraveCost(new FilterCard("cards from your graveyard"))); + this.getSpellAbility().addCost(new ExileXFromYourGraveCost(StaticFilters.FILTER_CARDS_FROM_YOUR_GRAVEYARD)); // Harvest Pyre deals X damage to target creature. this.getSpellAbility().addTarget(new TargetCreaturePermanent()); diff --git a/Mage.Sets/src/mage/cards/h/HarvestSeason.java b/Mage.Sets/src/mage/cards/h/HarvestSeason.java index f02072af0c5..d7ebdb726c1 100644 --- a/Mage.Sets/src/mage/cards/h/HarvestSeason.java +++ b/Mage.Sets/src/mage/cards/h/HarvestSeason.java @@ -71,9 +71,12 @@ class HarvestSeasonEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - TargetCardInLibrary target = new TargetCardInLibrary(0, new PermanentsOnBattlefieldCount(filter).calculate(game, source, this), StaticFilters.FILTER_CARD_BASIC_LAND); - if (controller.searchLibrary(target, source, game)) { - controller.moveCards(new CardsImpl(target.getTargets()).getCards(game), Zone.BATTLEFIELD, source, game, true, false, false, null); + int tappedCreatures = new PermanentsOnBattlefieldCount(filter).calculate(game, source, this); + if (tappedCreatures > 0) { + TargetCardInLibrary target = new TargetCardInLibrary(0, tappedCreatures, StaticFilters.FILTER_CARD_BASIC_LAND); + if (controller.searchLibrary(target, source, game)) { + controller.moveCards(new CardsImpl(target.getTargets()).getCards(game), Zone.BATTLEFIELD, source, game, true, false, false, null); + } } controller.shuffleLibrary(source, game); return true; diff --git a/Mage.Sets/src/mage/cards/h/HarvesttideAssailant.java b/Mage.Sets/src/mage/cards/h/HarvesttideAssailant.java index 55c37c19d41..c1d10320d80 100644 --- a/Mage.Sets/src/mage/cards/h/HarvesttideAssailant.java +++ b/Mage.Sets/src/mage/cards/h/HarvesttideAssailant.java @@ -22,7 +22,6 @@ public final class HarvesttideAssailant extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(4); this.color.setRed(true); - this.transformable = true; this.nightCard = true; // Trample diff --git a/Mage.Sets/src/mage/cards/h/HarvesttideInfiltrator.java b/Mage.Sets/src/mage/cards/h/HarvesttideInfiltrator.java index 66e8715dc57..f6258d61b69 100644 --- a/Mage.Sets/src/mage/cards/h/HarvesttideInfiltrator.java +++ b/Mage.Sets/src/mage/cards/h/HarvesttideInfiltrator.java @@ -3,7 +3,6 @@ package mage.cards.h; import mage.MageInt; import mage.abilities.keyword.DayboundAbility; import mage.abilities.keyword.TrampleAbility; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -23,14 +22,12 @@ public final class HarvesttideInfiltrator extends CardImpl { this.subtype.add(SubType.WEREWOLF); this.power = new MageInt(3); this.toughness = new MageInt(2); - this.transformable = true; this.secondSideCardClazz = mage.cards.h.HarvesttideAssailant.class; // Trample this.addAbility(TrampleAbility.getInstance()); // Daybound - this.addAbility(new TransformAbility()); this.addAbility(new DayboundAbility()); } diff --git a/Mage.Sets/src/mage/cards/h/HaukensInsight.java b/Mage.Sets/src/mage/cards/h/HaukensInsight.java new file mode 100644 index 00000000000..c7a10dbc219 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HaukensInsight.java @@ -0,0 +1,201 @@ +package mage.cards.h; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +import mage.MageIdentifier; +import mage.MageObject; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.constants.*; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.game.ExileZone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; +import mage.watchers.Watcher; + +/** + * + * @author weirddan455 + */ +public final class HaukensInsight extends CardImpl { + + public HaukensInsight(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, ""); + + this.addSuperType(SuperType.LEGENDARY); + this.color.setBlue(true); + // Back half of Jacob Hauken, Inspector + this.nightCard = true; + + // At the beginning of your upkeep, exile the top card of your library face down. You may look at that card for as long as it remains exiled. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new HaukensInsightExileEffect(), TargetController.YOU, false)); + + // Once during each of your turns, you may play a land or cast a spell from among the cards exiled with this permanent without paying its mana cost. + this.addAbility(new SimpleStaticAbility(new HaukensInsightPlayEffect()) + .setIdentifier(MageIdentifier.HaukensInsightWatcher), + new HaukensInsightWatcher()); + } + + private HaukensInsight(final HaukensInsight card) { + super(card); + } + + @Override + public HaukensInsight copy() { + return new HaukensInsight(this); + } +} + +class HaukensInsightExileEffect extends OneShotEffect { + + public HaukensInsightExileEffect() { + super(Outcome.Benefit); + staticText = "exile the top card of your library face down. You may look at that card for as long as it remains exiled"; + } + + private HaukensInsightExileEffect(final HaukensInsightExileEffect effect) { + super(effect); + } + + @Override + public HaukensInsightExileEffect copy() { + return new HaukensInsightExileEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Card card = controller.getLibrary().getFromTop(game); + if (card != null) { + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + MageObject sourceObject = source.getSourceObject(game); + String exileName = sourceObject == null ? null : sourceObject.getIdName(); + controller.moveCardsToExile(card, source, game, true, exileId, exileName); + if (game.getState().getZone(card.getId()) == Zone.EXILED) { + card.setFaceDown(true, game); + HaukensInsightLookEffect effect = new HaukensInsightLookEffect(controller.getId()); + effect.setTargetPointer(new FixedTarget(card, game)); + game.addEffect(effect, source); + return true; + } + } + } + return false; + } +} + +class HaukensInsightLookEffect extends AsThoughEffectImpl { + + private final UUID authorizedPlayerId; + + public HaukensInsightLookEffect(UUID authorizedPlayerId) { + super(AsThoughEffectType.LOOK_AT_FACE_DOWN, Duration.EndOfGame, Outcome.Benefit); + this.authorizedPlayerId = authorizedPlayerId; + } + + private HaukensInsightLookEffect(final HaukensInsightLookEffect effect) { + super(effect); + this.authorizedPlayerId = effect.authorizedPlayerId; + } + + @Override + public HaukensInsightLookEffect copy() { + return new HaukensInsightLookEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + UUID cardId = getTargetPointer().getFirst(game, source); + if (cardId == null) { + this.discard(); // card is no longer in the origin zone, effect can be discarded + } + return affectedControllerId.equals(authorizedPlayerId) + && objectId.equals(cardId); + } +} + +class HaukensInsightPlayEffect extends AsThoughEffectImpl { + + public HaukensInsightPlayEffect() { + super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.PlayForFree, true); + staticText = "Once during each of your turns, you may play a land or cast a spell from among the cards exiled with this permanent without paying its mana cost"; + } + + private HaukensInsightPlayEffect(final HaukensInsightPlayEffect effect) { + super(effect); + } + + @Override + public HaukensInsightPlayEffect copy() { + return new HaukensInsightPlayEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + if (affectedControllerId.equals(source.getControllerId()) && game.isActivePlayer(source.getControllerId())) { + Player controller = game.getPlayer(source.getControllerId()); + HaukensInsightWatcher watcher = game.getState().getWatcher(HaukensInsightWatcher.class); + Permanent sourceObject = game.getPermanent(source.getSourceId()); + if (controller != null && watcher != null && sourceObject != null && !watcher.isAbilityUsed(new MageObjectReference(sourceObject, game))) { + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), game.getState().getZoneChangeCounter(source.getSourceId())); + ExileZone exileZone = game.getExile().getExileZone(exileId); + if (exileZone != null && exileZone.contains(CardUtil.getMainCardId(game, objectId))) { + allowCardToPlayWithoutMana(objectId, source, affectedControllerId, game); + return true; + } + } + } + return false; + } +} + +class HaukensInsightWatcher extends Watcher { + + private final Set usedFrom = new HashSet<>(); + + public HaukensInsightWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.SPELL_CAST || event.getType() == GameEvent.EventType.LAND_PLAYED) { + if (event.hasApprovingIdentifier(MageIdentifier.HaukensInsightWatcher)) { + usedFrom.add(event.getAdditionalReference().getApprovingMageObjectReference()); + } + } + } + + @Override + public void reset() { + super.reset(); + usedFrom.clear(); + } + + public boolean isAbilityUsed(MageObjectReference mor) { + return usedFrom.contains(mor); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HauntedFengraf.java b/Mage.Sets/src/mage/cards/h/HauntedFengraf.java index 9ab1ed054a2..13664894eb5 100644 --- a/Mage.Sets/src/mage/cards/h/HauntedFengraf.java +++ b/Mage.Sets/src/mage/cards/h/HauntedFengraf.java @@ -73,7 +73,7 @@ class HauntedFengrafEffect extends OneShotEffect { TargetCard target = new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE); target.setNotTarget(true); target.setRandom(true); - player.chooseTarget(outcome, target, source, game); + target.chooseTarget(outcome, player.getId(), source, game); return player.moveCards(game.getCard(target.getFirstTarget()), Zone.HAND, source, game); } } diff --git a/Mage.Sets/src/mage/cards/h/HauntedLibrary.java b/Mage.Sets/src/mage/cards/h/HauntedLibrary.java new file mode 100644 index 00000000000..5f182be3def --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HauntedLibrary.java @@ -0,0 +1,37 @@ +package mage.cards.h; + +import mage.abilities.common.DiesCreatureTriggeredAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.game.permanent.token.SpiritWhiteToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HauntedLibrary extends CardImpl { + + public HauntedLibrary(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); + + // Whenever a creature an opponent controls dies, you may pay {1}. If you do, create a 1/1 white Spirit creature token with flying. + this.addAbility(new DiesCreatureTriggeredAbility(new DoIfCostPaid( + new CreateTokenEffect(new SpiritWhiteToken()), new GenericManaCost(1) + ), false, StaticFilters.FILTER_OPPONENTS_PERMANENT_A_CREATURE)); + } + + private HauntedLibrary(final HauntedLibrary card) { + super(card); + } + + @Override + public HauntedLibrary copy() { + return new HauntedLibrary(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HauntingImitation.java b/Mage.Sets/src/mage/cards/h/HauntingImitation.java new file mode 100644 index 00000000000..1b788fbd4f7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HauntingImitation.java @@ -0,0 +1,95 @@ +package mage.cards.h; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenCopyTargetEffect; +import mage.cards.*; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.permanent.PermanentCard; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HauntingImitation extends CardImpl { + + public HauntingImitation(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{U}"); + + // Each player reveals the top card of their library. For each creature card revealed this way, create a token that's a copy of that card, except it's 1/1, it's a Spirit in addition to its other types, and it has flying. If no creature cards were revealed this way, return Haunting Imitation to its owner's hand. + this.getSpellAbility().addEffect(new HauntingImitationEffect()); + } + + private HauntingImitation(final HauntingImitation card) { + super(card); + } + + @Override + public HauntingImitation copy() { + return new HauntingImitation(this); + } +} + +class HauntingImitationEffect extends OneShotEffect { + + HauntingImitationEffect() { + super(Outcome.Benefit); + staticText = "each player reveals the top card of their library. For each creature card revealed this way, " + + "create a token that's a copy of that card, except it's 1/1, it's a Spirit " + + "in addition to its other types, and it has flying. If no creature cards were revealed this way, " + + "return {this} to its owner's hand"; + } + + private HauntingImitationEffect(final HauntingImitationEffect effect) { + super(effect); + } + + @Override + public HauntingImitationEffect copy() { + return new HauntingImitationEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Cards cards = new CardsImpl(); + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + if (player == null) { + continue; + } + Card card = player.getLibrary().getFromTop(game); + if (card == null) { + continue; + } + player.revealCards(source, new CardsImpl(cards), game); + if (card.isCreature(game)) { + cards.add(card); + } + } + if (cards.isEmpty()) { + Player player = game.getPlayer(source.getControllerId()); + MageObject sourceObject = source.getSourceObjectIfItStillExists(game); + if (player != null && sourceObject instanceof Card) { + player.moveCards((Card) sourceObject, Zone.HAND, source, game); + } + return true; + } + CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect( + source.getControllerId(), null, false, 1, false, + false, null, 1, 1, true + ); + effect.setAdditionalSubType(SubType.SPIRIT); + for (Card card : cards.getCards(game)) { + effect.setSavedPermanent(new PermanentCard(card, source.getControllerId(), game)); + effect.apply(game, source); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/h/HavengulLaboratory.java b/Mage.Sets/src/mage/cards/h/HavengulLaboratory.java new file mode 100644 index 00000000000..014a8e29650 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HavengulLaboratory.java @@ -0,0 +1,110 @@ +package mage.cards.h; + +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.condition.Condition; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.effects.keyword.InvestigateEffect; +import mage.abilities.keyword.TransformAbility; +import mage.abilities.mana.ColorlessManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.util.CardUtil; +import mage.watchers.Watcher; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HavengulLaboratory extends CardImpl { + + public HavengulLaboratory(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + this.addSuperType(SuperType.LEGENDARY); + + this.secondSideCardClazz = mage.cards.h.HavengulMystery.class; + + // {T}: Add {C}. + this.addAbility(new ColorlessManaAbility()); + + // {4}, {T}: Investigate. + Ability ability = new SimpleActivatedAbility(new InvestigateEffect(), new GenericManaCost(4)); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + + // At the beginning of your end step, if you sacrificed three or more Clues this turn, transform Hawkins National Laboratory. + this.addAbility(new TransformAbility()); + this.addAbility(new BeginningOfEndStepTriggeredAbility( + Zone.BATTLEFIELD, new TransformSourceEffect(), + TargetController.YOU, HavengulLaboratoryCondition.instance, false + ), new HavengulLaboratoryWatcher()); + } + + private HavengulLaboratory(final HavengulLaboratory card) { + super(card); + } + + @Override + public HavengulLaboratory copy() { + return new HavengulLaboratory(this); + } +} + +enum HavengulLaboratoryCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + return HavengulLaboratoryWatcher.checkPlayer(source.getControllerId(), game); + } + + @Override + public String toString() { + return "you sacrificed three or more Clues this turn"; + } +} + +class HavengulLaboratoryWatcher extends Watcher { + + private final Map playerMap = new HashMap<>(); + + HavengulLaboratoryWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() != GameEvent.EventType.SACRIFICED_PERMANENT) { + return; + } + Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId()); + if (permanent == null || !permanent.hasSubtype(SubType.CLUE, game)) { + return; + } + playerMap.compute(event.getPlayerId(), CardUtil::setOrIncrementValue); + } + + @Override + public void reset() { + playerMap.clear(); + super.reset(); + } + + static boolean checkPlayer(UUID playerId, Game game) { + return game + .getState() + .getWatcher(HavengulLaboratoryWatcher.class) + .playerMap + .getOrDefault(playerId, 0) >= 3; + } +} diff --git a/Mage.Sets/src/mage/cards/h/HavengulMystery.java b/Mage.Sets/src/mage/cards/h/HavengulMystery.java new file mode 100644 index 00000000000..b63a0aa5ff2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HavengulMystery.java @@ -0,0 +1,176 @@ +package mage.cards.h; + +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.costs.common.PayLifeCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.mana.BlackManaAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SuperType; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HavengulMystery extends CardImpl { + + public HavengulMystery(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + this.addSuperType(SuperType.LEGENDARY); + + this.nightCard = true; + + // When this land transforms into The Upside Down, return target creature card from your graveyard to the battlefield. + this.addAbility(new HavengulMysteryTransformAbility()); + + // When the creature put onto the battlefield with The Upside Down leaves the battlefield, transform The Upside Down. + this.addAbility(new HavengulMysteryLeavesAbility()); + + // {T}, Pay 1 life: Add {B}. + Ability ability = new BlackManaAbility(); + ability.addCost(new PayLifeCost(1)); + this.addAbility(ability); + } + + private HavengulMystery(final HavengulMystery card) { + super(card); + } + + @Override + public HavengulMystery copy() { + return new HavengulMystery(this); + } + + static String makeKey(Ability source, Game game) { + return "HavengulMystery_" + source.getSourceId() + '_' + source.getSourceObjectZoneChangeCounter(); + } +} + +class HavengulMysteryTransformAbility extends TriggeredAbilityImpl { + + public HavengulMysteryTransformAbility() { + super(Zone.BATTLEFIELD, new HavengulMysteryEffect(), false); + this.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); + } + + public HavengulMysteryTransformAbility(final HavengulMysteryTransformAbility ability) { + super(ability); + } + + @Override + public HavengulMysteryTransformAbility copy() { + return new HavengulMysteryTransformAbility(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(this.getSourceId())) { + return false; + } + Permanent permanent = getSourcePermanentIfItStillExists(game); + return permanent != null && !permanent.isTransformed(); + } + + @Override + public String getRule() { + return "When this land transforms into {this}, " + + "return target creature card from your graveyard to the battlefield."; + } +} + +class HavengulMysteryEffect extends OneShotEffect { + + HavengulMysteryEffect() { + super(Outcome.Benefit); + } + + private HavengulMysteryEffect(final HavengulMysteryEffect effect) { + super(effect); + } + + @Override + public HavengulMysteryEffect copy() { + return new HavengulMysteryEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Card card = game.getCard(getTargetPointer().getFirst(game, source)); + if (player == null || card == null) { + return false; + } + player.moveCards(card, Zone.BATTLEFIELD, source, game); + Permanent permanent = game.getPermanent(card.getId()); + if (permanent == null) { + return false; + } + String key = HavengulMystery.makeKey(source, game); + Set morSet; + if (game.getState().getValue(key) != null) { + morSet = (Set) game.getState().getValue(key); + } else { + morSet = new HashSet<>(); + game.getState().setValue(key, morSet); + } + morSet.add(new MageObjectReference(permanent, game)); + return true; + } +} + +class HavengulMysteryLeavesAbility extends TriggeredAbilityImpl { + + HavengulMysteryLeavesAbility() { + super(Zone.BATTLEFIELD, new TransformSourceEffect()); + } + + private HavengulMysteryLeavesAbility(final HavengulMysteryLeavesAbility ability) { + super(ability); + } + + @Override + public HavengulMysteryLeavesAbility copy() { + return new HavengulMysteryLeavesAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + Set morSet = (Set) game.getState().getValue(HavengulMystery.makeKey(this, game)); + return morSet != null + && !morSet.isEmpty() + && morSet.stream().anyMatch(mor -> mor.refersTo(zEvent.getTarget(), game)); + } + + @Override + public String getRule() { + return "When the creature put onto the battlefield with {this} leaves the battlefield, transform {this}."; + } +} diff --git a/Mage.Sets/src/mage/cards/h/HazardousConditions.java b/Mage.Sets/src/mage/cards/h/HazardousConditions.java index 88395f599d7..505857f9c9b 100644 --- a/Mage.Sets/src/mage/cards/h/HazardousConditions.java +++ b/Mage.Sets/src/mage/cards/h/HazardousConditions.java @@ -1,4 +1,3 @@ - package mage.cards.h; import mage.abilities.effects.common.continuous.BoostAllEffect; @@ -17,7 +16,7 @@ import java.util.UUID; */ public final class HazardousConditions extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creatures with no counter"); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creatures with no counters on them"); static { filter.add(Predicates.not(CounterAnyPredicate.instance)); diff --git a/Mage.Sets/src/mage/cards/h/HeadlessRider.java b/Mage.Sets/src/mage/cards/h/HeadlessRider.java new file mode 100644 index 00000000000..30540a34783 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HeadlessRider.java @@ -0,0 +1,50 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.abilities.common.DiesThisOrAnotherCreatureTriggeredAbility; +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.FilterControlledPermanent; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.permanent.token.ZombieToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HeadlessRider extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledPermanent(SubType.ZOMBIE, "nontoken Zombie you control"); + + static { + filter.add(TokenPredicate.FALSE); + } + + public HeadlessRider(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.subtype.add(SubType.ZOMBIE); + this.power = new MageInt(3); + this.toughness = new MageInt(1); + + // Wheenver Headless Rider or another nontoken Zombie you control dies, create a 2/2 black Zombie creature token. + this.addAbility(new DiesThisOrAnotherCreatureTriggeredAbility( + new CreateTokenEffect(new ZombieToken()), false, filter + )); + } + + private HeadlessRider(final HeadlessRider card) { + super(card); + } + + @Override + public HeadlessRider copy() { + return new HeadlessRider(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HeadlongRush.java b/Mage.Sets/src/mage/cards/h/HeadlongRush.java index db98ea22e35..b81903c268e 100644 --- a/Mage.Sets/src/mage/cards/h/HeadlongRush.java +++ b/Mage.Sets/src/mage/cards/h/HeadlongRush.java @@ -8,7 +8,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.common.FilterAttackingCreature; +import mage.filter.StaticFilters; /** * @@ -20,7 +20,7 @@ public final class HeadlongRush extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{R}"); // Attacking creatures gain first strike until end of turn. - this.getSpellAbility().addEffect(new GainAbilityAllEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn, new FilterAttackingCreature())); + this.getSpellAbility().addEffect(new GainAbilityAllEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_ATTACKING_CREATURES)); } private HeadlongRush(final HeadlongRush card) { diff --git a/Mage.Sets/src/mage/cards/h/HeartOfKiran.java b/Mage.Sets/src/mage/cards/h/HeartOfKiran.java index b69f1ee11f2..f95b06761b1 100644 --- a/Mage.Sets/src/mage/cards/h/HeartOfKiran.java +++ b/Mage.Sets/src/mage/cards/h/HeartOfKiran.java @@ -1,28 +1,21 @@ - package mage.cards.h; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.Cost; -import mage.abilities.costs.CostImpl; -import mage.abilities.effects.Effect; -import mage.abilities.effects.common.continuous.AddCardTypeSourceEffect; +import mage.abilities.costs.common.RemoveCounterCost; import mage.abilities.keyword.CrewAbility; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.VigilanceAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; import mage.counters.CounterType; -import mage.filter.common.FilterControlledPlaneswalkerPermanent; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.target.Target; +import mage.filter.StaticFilters; import mage.target.common.TargetControlledPermanent; +import java.util.UUID; + /** * @author JRHerlehy */ @@ -43,13 +36,13 @@ public final class HeartOfKiran extends CardImpl { this.addAbility(VigilanceAbility.getInstance()); // Crew 3 - this.addAbility(new CrewAbility(3)); - // You may remove a loyalty counter from a planeswalker you control rather than pay Heart of Kiran's crew cost. - Cost cost = new HeartOfKiranAlternateCrewCost(CounterType.LOYALTY, 1); - Effect effect = new AddCardTypeSourceEffect(Duration.EndOfTurn, CardType.CREATURE); - effect.setText("You may remove a loyalty counter from a planeswalker you control rather than pay {this}'s crew cost"); - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, cost)); + this.addAbility(new CrewAbility( + 3, + new RemoveCounterCost(new TargetControlledPermanent( + StaticFilters.FILTER_CONTROLLED_PERMANENT_PLANESWALKER + ), CounterType.LOYALTY) + )); } private HeartOfKiran(final HeartOfKiran card) { @@ -61,57 +54,3 @@ public final class HeartOfKiran extends CardImpl { return new HeartOfKiran(this); } } - -class HeartOfKiranAlternateCrewCost extends CostImpl { - - private final CounterType counterTypeToRemove; - private final int countersToRemove; - - private static final FilterControlledPlaneswalkerPermanent filter = new FilterControlledPlaneswalkerPermanent("planeswalker you control"); - - public HeartOfKiranAlternateCrewCost(CounterType counterTypeToRemove, int countersToRemove) { - this.counterTypeToRemove = counterTypeToRemove; - this.countersToRemove = countersToRemove; - } - - public HeartOfKiranAlternateCrewCost(final HeartOfKiranAlternateCrewCost cost) { - super(cost); - this.counterTypeToRemove = cost.counterTypeToRemove; - this.countersToRemove = cost.countersToRemove; - } - - @Override - public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) { - paid = false; - - Target target = new TargetControlledPermanent(1, 1, filter, true); - - if (target.choose(Outcome.Benefit, controllerId, source.getSourceId(), game)) { - Permanent permanent = game.getPermanent(target.getFirstTarget()); - int originalLoyalty = permanent.getCounters(game).getCount(counterTypeToRemove); - - GameEvent event = new GameEvent(GameEvent.EventType.CREW_VEHICLE, target.getFirstTarget(), source, controllerId); - if (!game.replaceEvent(event)) { - permanent.removeCounters(counterTypeToRemove.createInstance(), source, game); - } - - paid = permanent.getCounters(game).getCount(counterTypeToRemove) < originalLoyalty; - - if (paid) { - game.fireEvent(GameEvent.getEvent(GameEvent.EventType.CREWED_VEHICLE, target.getFirstTarget(), source, controllerId)); - } - } - - return paid; - } - - @Override - public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) { - return !game.getBattlefield().getAllActivePermanents(filter, game).isEmpty(); - } - - @Override - public HeartOfKiranAlternateCrewCost copy() { - return new HeartOfKiranAlternateCrewCost(this); - } -} diff --git a/Mage.Sets/src/mage/cards/h/HeartSliver.java b/Mage.Sets/src/mage/cards/h/HeartSliver.java index 47c4e1fd6a9..4427e8a7ff1 100644 --- a/Mage.Sets/src/mage/cards/h/HeartSliver.java +++ b/Mage.Sets/src/mage/cards/h/HeartSliver.java @@ -1,7 +1,5 @@ - package mage.cards.h; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.continuous.GainAbilityAllEffect; @@ -11,27 +9,25 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; + +import java.util.UUID; /** * @author Loki */ public final class HeartSliver extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("All sliver creatures"); - - static { - filter.add(SubType.SLIVER.getPredicate()); - } - public HeartSliver(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); this.subtype.add(SubType.SLIVER); this.power = new MageInt(1); this.toughness = new MageInt(1); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(HasteAbility.getInstance(), Duration.WhileOnBattlefield, filter, false))); + this.addAbility(new SimpleStaticAbility(new GainAbilityAllEffect( + HasteAbility.getInstance(), Duration.WhileOnBattlefield, + StaticFilters.FILTER_PERMANENT_ALL_SLIVERS, false + ))); } private HeartSliver(final HeartSliver card) { diff --git a/Mage.Sets/src/mage/cards/h/HearthCharm.java b/Mage.Sets/src/mage/cards/h/HearthCharm.java index f4ac3026d50..6d034e1b9b7 100644 --- a/Mage.Sets/src/mage/cards/h/HearthCharm.java +++ b/Mage.Sets/src/mage/cards/h/HearthCharm.java @@ -1,4 +1,3 @@ - package mage.cards.h; import java.util.UUID; @@ -11,9 +10,9 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.ComparisonType; import mage.constants.Duration; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.PowerPredicate; -import mage.filter.predicate.permanent.AttackingPredicate; import mage.target.common.TargetCreaturePermanent; /** @@ -22,13 +21,10 @@ import mage.target.common.TargetCreaturePermanent; */ public final class HearthCharm extends CardImpl { - private static final FilterCreaturePermanent filter1 = new FilterCreaturePermanent("artifact creature"); - private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent("attacking creatures"); - private static final FilterCreaturePermanent filter3 = new FilterCreaturePermanent("creature with power 2 or less"); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with power 2 or less"); + static { - filter1.add(CardType.ARTIFACT.getPredicate()); - filter2.add(AttackingPredicate.instance); - filter3.add(new PowerPredicate(ComparisonType.FEWER_THAN, 3)); + filter.add(new PowerPredicate(ComparisonType.FEWER_THAN, 3)); } public HearthCharm(UUID ownerId, CardSetInfo setInfo) { @@ -36,15 +32,15 @@ public final class HearthCharm extends CardImpl { // Choose one - Destroy target artifact creature this.getSpellAbility().addEffect(new DestroyTargetEffect()); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter1)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_CREATURE)); // or attacking creatures get +1/+0 until end of turn Mode mode = new Mode(); - mode.addEffect(new BoostAllEffect(1, 0, Duration.EndOfTurn, filter2, false)); + mode.addEffect(new BoostAllEffect(1, 0, Duration.EndOfTurn, StaticFilters.FILTER_ATTACKING_CREATURES, false)); this.getSpellAbility().addMode(mode); // or target creature with power 2 or less is unblockable this turn. mode = new Mode(); mode.addEffect(new CantBeBlockedTargetEffect()); - mode.addTarget(new TargetCreaturePermanent(filter3)); + mode.addTarget(new TargetCreaturePermanent(filter)); this.getSpellAbility().addMode(mode); } diff --git a/Mage.Sets/src/mage/cards/h/HeartlessPillage.java b/Mage.Sets/src/mage/cards/h/HeartlessPillage.java index 0bb94d9f380..4c611180d40 100644 --- a/Mage.Sets/src/mage/cards/h/HeartlessPillage.java +++ b/Mage.Sets/src/mage/cards/h/HeartlessPillage.java @@ -7,7 +7,6 @@ import mage.abilities.effects.common.discard.DiscardTargetEffect; import mage.abilities.hint.common.RaidHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AbilityWord; import mage.constants.CardType; import mage.game.permanent.token.TreasureToken; import mage.target.common.TargetOpponent; @@ -29,9 +28,9 @@ public final class HeartlessPillage extends CardImpl { // Raid — If you attacked with a creature this turn, create a colorless Treasure artifact token with "{T}, Sacrifice this artifact: Add one mana of any color." this.getSpellAbility().addEffect(new ConditionalOneShotEffect( - new CreateTokenEffect(new TreasureToken()), - RaidCondition.instance, - "

Raid — If you attacked this turn, create a colorless Treasure artifact token with \"{T}, Sacrifice this artifact: Add one mana of any color.\"")); + new CreateTokenEffect(new TreasureToken()), RaidCondition.instance, + "

Raid — If you attacked this turn, create a Treasure token" + )); this.getSpellAbility().addWatcher(new PlayerAttackedWatcher()); this.getSpellAbility().addHint(RaidHint.instance); } diff --git a/Mage.Sets/src/mage/cards/h/HeatShimmer.java b/Mage.Sets/src/mage/cards/h/HeatShimmer.java index 73f6a4fb7e3..cae0d6aed43 100644 --- a/Mage.Sets/src/mage/cards/h/HeatShimmer.java +++ b/Mage.Sets/src/mage/cards/h/HeatShimmer.java @@ -68,7 +68,7 @@ class HeatShimmerEffect extends OneShotEffect { effect.setTargetPointer(new FixedTarget(permanent, game)); effect.apply(game, source); ExileTargetEffect exileEffect = new ExileTargetEffect(); - exileEffect.setTargetPointer(new FixedTarget(effect.getAddedPermanent().get(0), game)); + exileEffect.setTargetPointer(new FixedTarget(effect.getAddedPermanents().get(0), game)); DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); game.addDelayedTriggeredAbility(delayedAbility, source); return true; diff --git a/Mage.Sets/src/mage/cards/h/HeavyInfantry.java b/Mage.Sets/src/mage/cards/h/HeavyInfantry.java index 170650ae953..4ee8a10b8c1 100644 --- a/Mage.Sets/src/mage/cards/h/HeavyInfantry.java +++ b/Mage.Sets/src/mage/cards/h/HeavyInfantry.java @@ -10,8 +10,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; /** @@ -20,12 +19,6 @@ import mage.target.common.TargetCreaturePermanent; */ public final class HeavyInfantry extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public HeavyInfantry(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{W}"); this.subtype.add(SubType.HUMAN); @@ -35,7 +28,7 @@ public final class HeavyInfantry extends CardImpl { // When Heavy Infantry enters the battlefield, tap target creature an opponent controls. Ability ability = new EntersBattlefieldTriggeredAbility(new TapTargetEffect()); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/h/HedonistsTrove.java b/Mage.Sets/src/mage/cards/h/HedonistsTrove.java index 7dbca526845..76ce68200ad 100644 --- a/Mage.Sets/src/mage/cards/h/HedonistsTrove.java +++ b/Mage.Sets/src/mage/cards/h/HedonistsTrove.java @@ -1,6 +1,5 @@ package mage.cards.h; -import mage.MageObject; import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -56,7 +55,7 @@ class HedonistsTroveExileEffect extends OneShotEffect { HedonistsTroveExileEffect() { super(Outcome.Exile); - staticText = "exile all cards from target opponent's graveyard"; + staticText = "exile target opponent's graveyard"; } private HedonistsTroveExileEffect(final HedonistsTroveExileEffect effect) { @@ -72,17 +71,15 @@ class HedonistsTroveExileEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); Player targetPlayer = game.getPlayer(source.getFirstTarget()); - MageObject sourceObject = source.getSourceObject(game); UUID exileId = CardUtil.getExileZoneId(game, source); // save the exileId associated with this specific source game.getState().setValue(source.getSourceId().toString(), exileId); return controller != null && targetPlayer != null - && sourceObject != null && controller.moveCardsToExile( - targetPlayer.getGraveyard().getCards(game), source, game, true, - exileId, sourceObject.getIdName() - ); + targetPlayer.getGraveyard().getCards(game), source, game, true, + exileId, CardUtil.getSourceName(game, source) + ); } } @@ -189,9 +186,9 @@ class HedonistsTroveWatcher extends Watcher { Permanent permanent = source.getSourcePermanentOrLKI(game); return permanent != null && playerMap - .getOrDefault(playerId, emptySet) - .stream() - .noneMatch(mor -> mor.refersTo(permanent, game)); + .getOrDefault(playerId, emptySet) + .stream() + .noneMatch(mor -> mor.refersTo(permanent, game)); } @Override diff --git a/Mage.Sets/src/mage/cards/h/HedronAlignment.java b/Mage.Sets/src/mage/cards/h/HedronAlignment.java index 4817ff00a86..35ff43f0a60 100644 --- a/Mage.Sets/src/mage/cards/h/HedronAlignment.java +++ b/Mage.Sets/src/mage/cards/h/HedronAlignment.java @@ -37,7 +37,7 @@ public final class HedronAlignment extends CardImpl { // At the beginning of your upkeep, you may reveal your hand. If you do, you win the game if you own a card named Hedron Alignment in exile, in your hand, in your graveyard, and on the battlefield. this.addAbility(new BeginningOfUpkeepTriggeredAbility(new HedronAlignmentEffect(), TargetController.YOU, true)); // {1}{U}: Scry 1. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new ScryEffect(1), new ManaCostsImpl("{1}{U}"))); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new ScryEffect(1, false), new ManaCostsImpl("{1}{U}"))); } private HedronAlignment(final HedronAlignment card) { diff --git a/Mage.Sets/src/mage/cards/h/HeikoYamazakiTheGeneral.java b/Mage.Sets/src/mage/cards/h/HeikoYamazakiTheGeneral.java new file mode 100644 index 00000000000..451914f9bf5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HeikoYamazakiTheGeneral.java @@ -0,0 +1,60 @@ +package mage.cards.h; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksAloneControlledTriggeredAbility; +import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; +import mage.constants.*; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.filter.common.FilterArtifactCard; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.target.common.TargetCardInYourGraveyard; + +/** + * + * @author weirddan455 + */ +public final class HeikoYamazakiTheGeneral extends CardImpl { + + private static final FilterControlledPermanent filter = new FilterControlledPermanent("a Samurai or Warrior you control"); + private static final FilterArtifactCard filter2 = new FilterArtifactCard("artifact card from your graveyard"); + + static { + filter.add(Predicates.or(SubType.SAMURAI.getPredicate(), SubType.WARRIOR.getPredicate())); + } + + public HeikoYamazakiTheGeneral(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SAMURAI); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Whenever a Samurai or Warrior you control attacks alone, you may cast target artifact card from your graveyard this turn. + Ability ability = new AttacksAloneControlledTriggeredAbility( + new PlayFromNotOwnHandZoneTargetEffect(Zone.GRAVEYARD, TargetController.YOU, Duration.EndOfTurn, false, true) + .setText("you may cast target artifact card from your graveyard this turn"), + filter, false, false + ); + ability.addTarget(new TargetCardInYourGraveyard(filter2)); + this.addAbility(ability); + } + + private HeikoYamazakiTheGeneral(final HeikoYamazakiTheGeneral card) { + super(card); + } + + @Override + public HeikoYamazakiTheGeneral copy() { + return new HeikoYamazakiTheGeneral(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HeirOfFalkenrath.java b/Mage.Sets/src/mage/cards/h/HeirOfFalkenrath.java index e3a8262c096..f86c9772c4c 100644 --- a/Mage.Sets/src/mage/cards/h/HeirOfFalkenrath.java +++ b/Mage.Sets/src/mage/cards/h/HeirOfFalkenrath.java @@ -25,12 +25,11 @@ public final class HeirOfFalkenrath extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(1); - this.transformable = true; this.secondSideCardClazz = HeirToTheNight.class; // Discard a card: Transform Heir of Falkenrath. Activate this ability only once each turn. this.addAbility(new TransformAbility()); - this.addAbility(new LimitedTimesPerTurnActivatedAbility(Zone.BATTLEFIELD, new TransformSourceEffect(true), new DiscardCardCost())); + this.addAbility(new LimitedTimesPerTurnActivatedAbility(Zone.BATTLEFIELD, new TransformSourceEffect(), new DiscardCardCost())); } private HeirOfFalkenrath(final HeirOfFalkenrath card) { diff --git a/Mage.Sets/src/mage/cards/h/HeirOfTheAncientFang.java b/Mage.Sets/src/mage/cards/h/HeirOfTheAncientFang.java new file mode 100644 index 00000000000..32983489975 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HeirOfTheAncientFang.java @@ -0,0 +1,58 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.permanent.ModifiedPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HeirOfTheAncientFang extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledCreaturePermanent(); + + static { + filter.add(ModifiedPredicate.instance); + } + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter); + private static final Hint hint = new ConditionHint(condition, "You control a modified creature"); + + public HeirOfTheAncientFang(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); + + this.subtype.add(SubType.SNAKE); + this.subtype.add(SubType.SAMURAI); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Heir of the Ancient Fang enters the battlefield with a +1/+1 counter on it if you control a modified creature. + this.addAbility(new EntersBattlefieldAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), condition, + null, "with a +1/+1 counter on it if you control a modified creature" + ).addHint(hint)); + } + + private HeirOfTheAncientFang(final HeirOfTheAncientFang card) { + super(card); + } + + @Override + public HeirOfTheAncientFang copy() { + return new HeirOfTheAncientFang(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HeirloomMirror.java b/Mage.Sets/src/mage/cards/h/HeirloomMirror.java index 43f576e01c1..4d71dcec837 100644 --- a/Mage.Sets/src/mage/cards/h/HeirloomMirror.java +++ b/Mage.Sets/src/mage/cards/h/HeirloomMirror.java @@ -28,7 +28,6 @@ public final class HeirloomMirror extends CardImpl { public HeirloomMirror(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}{B}"); - this.transformable = true; this.secondSideCardClazz = mage.cards.i.InheritedFiend.class; // {1}, {T}, Pay 1 life, Discard a card: Draw a card, mill a card, then put a ritual counter on Heirloom Mirror. Then if it has 3 or more ritual counters on it, remove them and transform it. Activate only as a sorcery. @@ -82,7 +81,7 @@ class HeirloomMirrorEffect extends OneShotEffect { return true; } permanent.removeCounters(CounterType.RITUAL.createInstance(counters), source, game); - new TransformSourceEffect(true).apply(game, source); + new TransformSourceEffect().apply(game, source); return true; } } diff --git a/Mage.Sets/src/mage/cards/h/HeliodGodOfTheSun.java b/Mage.Sets/src/mage/cards/h/HeliodGodOfTheSun.java index a31c8d8c4bd..c4e063ff6da 100644 --- a/Mage.Sets/src/mage/cards/h/HeliodGodOfTheSun.java +++ b/Mage.Sets/src/mage/cards/h/HeliodGodOfTheSun.java @@ -44,7 +44,7 @@ public final class HeliodGodOfTheSun extends CardImpl { // Other creatures you control have vigilance. this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( VigilanceAbility.getInstance(), Duration.WhileOnBattlefield, - StaticFilters.FILTER_PERMANENT_CREATURE, true + StaticFilters.FILTER_PERMANENT_CREATURES, true ))); // {2}{W}{W}: Create a 2/1 white Cleric enchantment creature token. diff --git a/Mage.Sets/src/mage/cards/h/HeliodsEmissary.java b/Mage.Sets/src/mage/cards/h/HeliodsEmissary.java index f40ab7a774d..d8178145eeb 100644 --- a/Mage.Sets/src/mage/cards/h/HeliodsEmissary.java +++ b/Mage.Sets/src/mage/cards/h/HeliodsEmissary.java @@ -16,9 +16,8 @@ import mage.constants.AttachmentType; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.TargetController; import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.Target; import mage.target.common.TargetCreaturePermanent; @@ -28,11 +27,6 @@ import mage.target.common.TargetCreaturePermanent; */ public final class HeliodsEmissary extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public HeliodsEmissary(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT,CardType.CREATURE},"{3}{W}"); this.subtype.add(SubType.ELK); @@ -44,11 +38,11 @@ public final class HeliodsEmissary extends CardImpl { this.addAbility(new BestowAbility(this, "{6}{W}")); // Whenever Heliod's Emissary or enchanted creature attacks, tap target creature an opponent controls. Ability ability = new AttacksTriggeredAbility(new TapTargetEffect(), false); - Target target = new TargetCreaturePermanent(filter); + Target target = new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE); ability.addTarget(target); this.addAbility(ability); ability = new AttacksAttachedTriggeredAbility(new TapTargetEffect(), AttachmentType.AURA, false); - target = new TargetCreaturePermanent(filter); + target = new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE); ability.addTarget(target); this.addAbility(ability); // Enchanted creature gets +3/+3. diff --git a/Mage.Sets/src/mage/cards/h/HeliumSquirter.java b/Mage.Sets/src/mage/cards/h/HeliumSquirter.java index 707fa806e46..8598b428a35 100644 --- a/Mage.Sets/src/mage/cards/h/HeliumSquirter.java +++ b/Mage.Sets/src/mage/cards/h/HeliumSquirter.java @@ -15,8 +15,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Zone; -import mage.counters.CounterType; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; /** @@ -24,11 +23,6 @@ import mage.target.common.TargetCreaturePermanent; * @author JotaPeRL */ public final class HeliumSquirter extends CardImpl { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature with a +1/+1 counter on it"); - static { - filter.add(CounterType.P1P1.getPredicate()); - } public HeliumSquirter(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{U}"); @@ -42,7 +36,7 @@ public final class HeliumSquirter extends CardImpl { // {1}: Target creature with a +1/+1 counter on it gains flying until end of turn. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainAbilityTargetEffect(FlyingAbility.getInstance(), Duration.EndOfTurn), new GenericManaCost(1)); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_P1P1)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/h/HellSwarm.java b/Mage.Sets/src/mage/cards/h/HellSwarm.java index 47be817c09d..8596dd726ea 100644 --- a/Mage.Sets/src/mage/cards/h/HellSwarm.java +++ b/Mage.Sets/src/mage/cards/h/HellSwarm.java @@ -1,4 +1,3 @@ - package mage.cards.h; import java.util.UUID; @@ -7,7 +6,6 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.common.FilterCreaturePermanent; /** * @@ -19,7 +17,7 @@ public final class HellSwarm extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{B}"); // All creatures get -1/-0 until end of turn. - this.getSpellAbility().addEffect(new BoostAllEffect(-1, 0, Duration.EndOfTurn, new FilterCreaturePermanent("All creatures"), false)); + this.getSpellAbility().addEffect(new BoostAllEffect(-1, 0, Duration.EndOfTurn)); } private HellSwarm(final HellSwarm card) { diff --git a/Mage.Sets/src/mage/cards/h/Hellfire.java b/Mage.Sets/src/mage/cards/h/Hellfire.java index 71cc0fadd1f..9e3214cc7cb 100644 --- a/Mage.Sets/src/mage/cards/h/Hellfire.java +++ b/Mage.Sets/src/mage/cards/h/Hellfire.java @@ -1,8 +1,6 @@ - package mage.cards.h; import java.util.UUID; -import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageControllerEffect; @@ -11,9 +9,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ColorPredicate; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -62,9 +58,7 @@ class HellfireEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { int destroyedCreature = 0; - FilterCreaturePermanent filter = new FilterCreaturePermanent("all nonblack creatures"); - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - for (Permanent creature : game.getState().getBattlefield().getActivePermanents(filter, controller.getId(), game)) { + for (Permanent creature : game.getState().getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURES_NON_BLACK, controller.getId(), game)) { if (creature.destroy(source, game, false) && game.getState().getZone(creature.getId()) == Zone.GRAVEYARD) { // If a commander is replaced to command zone, the creature does not die) { destroyedCreature++; diff --git a/Mage.Sets/src/mage/cards/h/HelmOfPossession.java b/Mage.Sets/src/mage/cards/h/HelmOfPossession.java index 9b631d21915..5161c317965 100644 --- a/Mage.Sets/src/mage/cards/h/HelmOfPossession.java +++ b/Mage.Sets/src/mage/cards/h/HelmOfPossession.java @@ -1,11 +1,10 @@ package mage.cards.h; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SkipUntapOptionalAbility; -import mage.abilities.condition.Condition; +import mage.abilities.condition.common.SourceTappedCondition; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; @@ -15,15 +14,14 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Zone; -import static mage.filter.StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetControlledPermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + +import static mage.filter.StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT; + /** - * * @author fireshoes */ public final class HelmOfPossession extends CardImpl { @@ -35,14 +33,13 @@ public final class HelmOfPossession extends CardImpl { this.addAbility(new SkipUntapOptionalAbility()); // {2}, {tap}, Sacrifice a creature: Gain control of target creature for as long as you control Helm of Possession and Helm of Possession remains tapped. - ConditionalContinuousEffect effect = new ConditionalContinuousEffect( - new GainControlTargetEffect(Duration.Custom), - new HelmOfPossessionCondition(), - "Gain control of target creature for as long as you control {this} and {this} remains tapped"); - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new GenericManaCost(2)); + Ability ability = new SimpleActivatedAbility(new ConditionalContinuousEffect( + new GainControlTargetEffect(Duration.WhileControlled), SourceTappedCondition.TAPPED, + "gain control of target creature for as long as you control {this} and {this} remains tapped" + ), new GenericManaCost(2)); ability.addCost(new TapSourceCost()); + ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(FILTER_CONTROLLED_CREATURE_SHORT_TEXT))); ability.addTarget(new TargetCreaturePermanent()); - ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(FILTER_CONTROLLED_CREATURE_SHORT_TEXT))); this.addAbility(ability); } @@ -55,22 +52,3 @@ public final class HelmOfPossession extends CardImpl { return new HelmOfPossession(this); } } - -class HelmOfPossessionCondition implements Condition { - - private UUID controllerId; - - @Override - public boolean apply(Game game, Ability source) { - if (controllerId == null) { - controllerId = source.getControllerId(); - } - Permanent permanent = game.getBattlefield().getPermanent(source.getSourceId()); - if (permanent != null) { - if (permanent.isTapped()) { - return controllerId.equals(source.getControllerId()); - } - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/h/HenrikaDomnathi.java b/Mage.Sets/src/mage/cards/h/HenrikaDomnathi.java new file mode 100644 index 00000000000..bec79f3fd06 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HenrikaDomnathi.java @@ -0,0 +1,66 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.BeginningOfCombatTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.LoseLifeSourceControllerEffect; +import mage.abilities.effects.common.SacrificeAllEffect; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.TransformAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.TargetController; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HenrikaDomnathi extends CardImpl { + + public HenrikaDomnathi(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.VAMPIRE); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + this.secondSideCardClazz = mage.cards.h.HenrikaInfernalSeer.class; + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // At the beginning of combat on your turn, choose one that hasn't been chosen — + // • Each player sacrifices a creature. + Ability ability = new BeginningOfCombatTriggeredAbility(new SacrificeAllEffect( + 1, StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT + ), TargetController.YOU, false); + ability.getModes().setEachModeOnlyOnce(true); + + // • You draw a card and you lose 1 life. + Mode mode = new Mode(new DrawCardSourceControllerEffect(1).setText("you draw a card")); + mode.addEffect(new LoseLifeSourceControllerEffect(1).concatBy("and")); + ability.addMode(mode); + + // • Transform Henrika Domnathi. + ability.addMode(new Mode(new TransformSourceEffect())); + this.addAbility(new TransformAbility()); + this.addAbility(ability); + } + + private HenrikaDomnathi(final HenrikaDomnathi card) { + super(card); + } + + @Override + public HenrikaDomnathi copy() { + return new HenrikaDomnathi(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HenrikaInfernalSeer.java b/Mage.Sets/src/mage/cards/h/HenrikaInfernalSeer.java new file mode 100644 index 00000000000..9d51e0234ee --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HenrikaInfernalSeer.java @@ -0,0 +1,68 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.continuous.BoostAllEffect; +import mage.abilities.keyword.DeathtouchAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.AbilityPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HenrikaInfernalSeer extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); + + static { + filter.add(TargetController.YOU.getControllerPredicate()); + filter.add(Predicates.or( + new AbilityPredicate(FlyingAbility.class), + new AbilityPredicate(DeathtouchAbility.class), + new AbilityPredicate(LifelinkAbility.class) + )); + } + + public HenrikaInfernalSeer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.VAMPIRE); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + this.color.setBlack(true); + this.nightCard = true; + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Deathtouch + this.addAbility(DeathtouchAbility.getInstance()); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // {1}{B}{B}: Each creature you control with flying, deathtouch, and/or lifelink gets +1/+0 until end of turn. + this.addAbility(new SimpleActivatedAbility(new BoostAllEffect( + 0, 1, Duration.EndOfTurn, filter, false + ).setText("each creature you control with flying, deathtouch, and/or lifelink gets +1/+0 until end of turn"), new ManaCostsImpl<>("{1}{B}{B}"))); + } + + private HenrikaInfernalSeer(final HenrikaInfernalSeer card) { + super(card); + } + + @Override + public HenrikaInfernalSeer copy() { + return new HenrikaInfernalSeer(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HeraldOfAnguish.java b/Mage.Sets/src/mage/cards/h/HeraldOfAnguish.java index ca30a58bf73..ebd207e12fb 100644 --- a/Mage.Sets/src/mage/cards/h/HeraldOfAnguish.java +++ b/Mage.Sets/src/mage/cards/h/HeraldOfAnguish.java @@ -1,7 +1,5 @@ - package mage.cards.h; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.BeginningOfYourEndStepTriggeredAbility; @@ -14,17 +12,14 @@ import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.ImproviseAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.TargetController; -import mage.constants.Zone; -import mage.filter.common.FilterControlledArtifactPermanent; +import mage.constants.*; +import mage.filter.StaticFilters; import mage.target.common.TargetControlledPermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author fireshoes */ public final class HeraldOfAnguish extends CardImpl { @@ -47,7 +42,7 @@ public final class HeraldOfAnguish extends CardImpl { // {1}{B}, Sacrifice an artifact: Target creature gets -2/-2 until end of turn. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostTargetEffect(-2, -2, Duration.EndOfTurn), new ManaCostsImpl("{1}{B}")); - ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(new FilterControlledArtifactPermanent()))); + ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT_AN))); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/h/HeraldOfLeshrac.java b/Mage.Sets/src/mage/cards/h/HeraldOfLeshrac.java index 3e22e1bc98d..703f309f677 100644 --- a/Mage.Sets/src/mage/cards/h/HeraldOfLeshrac.java +++ b/Mage.Sets/src/mage/cards/h/HeraldOfLeshrac.java @@ -91,7 +91,7 @@ class HeraldOfLeshracCumulativeCost extends CostImpl { Target target = new TargetPermanent(filter); if (target.choose(Outcome.GainControl, controllerId, source.getSourceId(), game)) { ContinuousEffect effect = new GainControlTargetEffect(Duration.EndOfGame); - effect.setTargetPointer(new FixedTarget(target.getFirstTarget())); + effect.setTargetPointer(new FixedTarget(target.getFirstTarget(), game)); game.addEffect(effect, ability); game.getState().processAction(game); paid = true; diff --git a/Mage.Sets/src/mage/cards/h/HermitOfTheNatterknolls.java b/Mage.Sets/src/mage/cards/h/HermitOfTheNatterknolls.java index 5b2becc29b4..e14800c3298 100644 --- a/Mage.Sets/src/mage/cards/h/HermitOfTheNatterknolls.java +++ b/Mage.Sets/src/mage/cards/h/HermitOfTheNatterknolls.java @@ -12,7 +12,6 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.filter.FilterSpell; import mage.filter.StaticFilters; import java.util.UUID; @@ -29,7 +28,6 @@ public final class HermitOfTheNatterknolls extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(3); - this.transformable = true; this.secondSideCardClazz = mage.cards.l.LoneWolfOfTheNatterknolls.class; // Whenever an opponent casts a spell during your turn, draw a card. diff --git a/Mage.Sets/src/mage/cards/h/HeroOfGomaFada.java b/Mage.Sets/src/mage/cards/h/HeroOfGomaFada.java index d5a67abe64a..8e0ec00845f 100644 --- a/Mage.Sets/src/mage/cards/h/HeroOfGomaFada.java +++ b/Mage.Sets/src/mage/cards/h/HeroOfGomaFada.java @@ -12,7 +12,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; /** * @@ -31,7 +31,7 @@ public final class HeroOfGomaFada extends CardImpl { // Rally — Whenever Hero of Goma Fada or another Ally enters the battlefield under your control, creatures you control gain indestructible until end of turn. Ability ability = new AllyEntersBattlefieldTriggeredAbility( new GainAbilityAllEffect(IndestructibleAbility.getInstance(), Duration.EndOfTurn, - new FilterControlledCreaturePermanent("creatures you control")), false); + StaticFilters.FILTER_CONTROLLED_CREATURES), false); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/h/HeronBlessedGeist.java b/Mage.Sets/src/mage/cards/h/HeronBlessedGeist.java new file mode 100644 index 00000000000..d63452acafd --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HeronBlessedGeist.java @@ -0,0 +1,63 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.abilities.ActivatedAbility; +import mage.abilities.common.ActivateIfConditionActivatedAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.costs.common.ExileSourceFromGraveCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TimingRule; +import mage.constants.Zone; +import mage.filter.common.FilterEnchantmentPermanent; +import mage.game.permanent.token.SpiritWhiteToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HeronBlessedGeist extends CardImpl { + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition( + new FilterEnchantmentPermanent("you control an enchantment and only as a sorcery") + ); + private static final Hint hint = new ConditionHint(condition, "You control an enchantment"); + + public HeronBlessedGeist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{W}"); + + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // {3}{W}, Exile Heron-Blessed Geist from your graveyard: Create two 1/1 white Spirit creature tokens with flying. Activate only if you control an enchantment and only as a sorcery. + ActivatedAbility ability = new ActivateIfConditionActivatedAbility( + Zone.GRAVEYARD, new CreateTokenEffect(new SpiritWhiteToken(), 2), + new ManaCostsImpl<>("{3}{W}"), condition + ); + ability.addCost(new ExileSourceFromGraveCost()); + ability.setTiming(TimingRule.SORCERY); + this.addAbility(ability.addHint(hint)); + } + + private HeronBlessedGeist(final HeronBlessedGeist card) { + super(card); + } + + @Override + public HeronBlessedGeist copy() { + return new HeronBlessedGeist(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HeronOfHope.java b/Mage.Sets/src/mage/cards/h/HeronOfHope.java new file mode 100644 index 00000000000..bb50322bd81 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HeronOfHope.java @@ -0,0 +1,89 @@ +package mage.cards.h; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.keyword.LifelinkAbility; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.game.Game; +import mage.game.events.GameEvent; + +/** + * + * @author weirddan455 + */ +public final class HeronOfHope extends CardImpl { + + public HeronOfHope(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); + + this.subtype.add(SubType.BIRD); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // If you would gain life, you gain that much life plus 1 instead. + this.addAbility(new SimpleStaticAbility(new HeronOfHopeEffect())); + + // {1}{W}: Heron of Hope gains lifelink until end of turn. + this.addAbility(new SimpleActivatedAbility( + new GainAbilitySourceEffect(LifelinkAbility.getInstance(), Duration.EndOfTurn), + new ManaCostsImpl<>("{1}{W}") + )); + } + + private HeronOfHope(final HeronOfHope card) { + super(card); + } + + @Override + public HeronOfHope copy() { + return new HeronOfHope(this); + } +} + +class HeronOfHopeEffect extends ReplacementEffectImpl { + + public HeronOfHopeEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "If you would gain life, you gain that much life plus 1 instead"; + } + + private HeronOfHopeEffect(final HeronOfHopeEffect effect) { + super(effect); + } + + @Override + public HeronOfHopeEffect copy() { + return new HeronOfHopeEffect(this); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + event.setAmount(event.getAmount() + 1); + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.GAIN_LIFE; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return source.isControlledBy(event.getPlayerId()); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HeronbladeElite.java b/Mage.Sets/src/mage/cards/h/HeronbladeElite.java index 588ffda9415..68dc4b71964 100644 --- a/Mage.Sets/src/mage/cards/h/HeronbladeElite.java +++ b/Mage.Sets/src/mage/cards/h/HeronbladeElite.java @@ -18,6 +18,7 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import java.util.UUID; +import mage.filter.predicate.mageobject.AnotherPredicate; /** * @author TheElk801 @@ -25,6 +26,11 @@ import java.util.UUID; public final class HeronbladeElite extends CardImpl { private static final FilterPermanent filter = new FilterCreaturePermanent(SubType.HUMAN, "another Human"); + + static { + filter.add(AnotherPredicate.instance); + } + private static final DynamicValue xValue = new SourcePermanentPowerCount(); public HeronbladeElite(UUID ownerId, CardSetInfo setInfo) { @@ -45,8 +51,8 @@ public final class HeronbladeElite extends CardImpl { // {T}: Add X mana of any one color, where X is Heronblade Elite's power. this.addAbility(new DynamicManaAbility( - Mana.AnyMana(1), xValue, new TapSourceCost(), "Add X mana " + - "of any one color, where X is {this}'s power", true + new Mana(0, 0, 0, 0, 0, 0, 1, 0), xValue, new TapSourceCost(), "Add X mana " + + "of any one color, where X is {this}'s power", true )); } diff --git a/Mage.Sets/src/mage/cards/h/HiddenAncients.java b/Mage.Sets/src/mage/cards/h/HiddenAncients.java index fde2233bc24..93d9df66244 100644 --- a/Mage.Sets/src/mage/cards/h/HiddenAncients.java +++ b/Mage.Sets/src/mage/cards/h/HiddenAncients.java @@ -37,7 +37,7 @@ public final class HiddenAncients extends CardImpl { // When an opponent casts an enchantment spell, if Hidden Ancients is an enchantment, Hidden Ancients becomes a 5/5 Treefolk creature. TriggeredAbility ability = new SpellCastOpponentTriggeredAbility(new BecomesCreatureSourceEffect(new HiddenAncientsTreefolkToken(), "", Duration.WhileOnBattlefield, true, false), filter, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_ENCHANTMENT_PERMANENT), + this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_PERMANENT_ENCHANTMENT), "When an opponent casts an enchantment spell, if {this} is an enchantment, {this} becomes a 5/5 Treefolk creature.")); } diff --git a/Mage.Sets/src/mage/cards/h/HiddenGibbons.java b/Mage.Sets/src/mage/cards/h/HiddenGibbons.java index 5c82f4a1d2a..52a9622ad85 100644 --- a/Mage.Sets/src/mage/cards/h/HiddenGibbons.java +++ b/Mage.Sets/src/mage/cards/h/HiddenGibbons.java @@ -37,7 +37,7 @@ public final class HiddenGibbons extends CardImpl { // When an opponent casts an instant spell, if Hidden Gibbons is an enchantment, Hidden Gibbons becomes a 4/4 Ape creature. TriggeredAbility ability = new SpellCastOpponentTriggeredAbility(new BecomesCreatureSourceEffect(new HiddenGibbonsApe(), "", Duration.WhileOnBattlefield, true, false), filter, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_ENCHANTMENT_PERMANENT), + this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_PERMANENT_ENCHANTMENT), "When an opponent casts an instant spell, if {this} is an enchantment, {this} becomes a 4/4 Ape creature.")); } diff --git a/Mage.Sets/src/mage/cards/h/HiddenGuerrillas.java b/Mage.Sets/src/mage/cards/h/HiddenGuerrillas.java index d99076d6983..ac57ba650ca 100644 --- a/Mage.Sets/src/mage/cards/h/HiddenGuerrillas.java +++ b/Mage.Sets/src/mage/cards/h/HiddenGuerrillas.java @@ -32,7 +32,7 @@ public final class HiddenGuerrillas extends CardImpl { // When an opponent casts an artifact spell, if Hidden Guerrillas is an enchantment, Hidden Guerrillas becomes a 5/3 Soldier creature with trample. TriggeredAbility ability = new SpellCastOpponentTriggeredAbility(new BecomesCreatureSourceEffect(new HiddenGuerrillasSoldier(), "", Duration.WhileOnBattlefield, true, false), new FilterArtifactSpell(), false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_ENCHANTMENT_PERMANENT), + this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_PERMANENT_ENCHANTMENT), "When an opponent casts an artifact spell, if {this} is an enchantment, {this} becomes a 5/3 Soldier creature with trample.")); } diff --git a/Mage.Sets/src/mage/cards/h/HiddenHerd.java b/Mage.Sets/src/mage/cards/h/HiddenHerd.java index 1f3a72e3d14..ccd88510612 100644 --- a/Mage.Sets/src/mage/cards/h/HiddenHerd.java +++ b/Mage.Sets/src/mage/cards/h/HiddenHerd.java @@ -32,7 +32,7 @@ public final class HiddenHerd extends CardImpl { // When an opponent plays a nonbasic land, if Hidden Herd is an enchantment, Hidden Herd becomes a 3/3 Beast creature. this.addAbility(new ConditionalInterveningIfTriggeredAbility( new HiddenHerdAbility(), - new SourceMatchesFilterCondition(StaticFilters.FILTER_ENCHANTMENT_PERMANENT), + new SourceMatchesFilterCondition(StaticFilters.FILTER_PERMANENT_ENCHANTMENT), "When an opponent plays a nonbasic land, if {this} is an enchantment, {this} becomes a 3/3 Beast creature." )); } diff --git a/Mage.Sets/src/mage/cards/h/HiddenSpider.java b/Mage.Sets/src/mage/cards/h/HiddenSpider.java index b8793a5d5ce..7b8742030a2 100644 --- a/Mage.Sets/src/mage/cards/h/HiddenSpider.java +++ b/Mage.Sets/src/mage/cards/h/HiddenSpider.java @@ -40,7 +40,7 @@ public final class HiddenSpider extends CardImpl { // When an opponent casts a creature spell with flying, if Hidden Spider is an enchantment, Hidden Spider becomes a 3/5 Spider creature with reach. TriggeredAbility ability = new SpellCastOpponentTriggeredAbility(new BecomesCreatureSourceEffect(new HiddenSpiderToken(), "", Duration.WhileOnBattlefield, true, false), filter, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_ENCHANTMENT_PERMANENT), + this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_PERMANENT_ENCHANTMENT), "When an opponent casts a creature spell with flying, if {this} is an enchantment, {this} becomes a 3/5 Spider creature with reach.")); } diff --git a/Mage.Sets/src/mage/cards/h/HiddenStag.java b/Mage.Sets/src/mage/cards/h/HiddenStag.java index 4f9dcd39d2d..25a595200b8 100644 --- a/Mage.Sets/src/mage/cards/h/HiddenStag.java +++ b/Mage.Sets/src/mage/cards/h/HiddenStag.java @@ -31,7 +31,7 @@ public final class HiddenStag extends CardImpl { // Whenever an opponent plays a land, if Hidden Stag is an enchantment, Hidden Stag becomes a 3/2 Elk Beast creature. Effect effect = new BecomesCreatureSourceEffect(new ElkBeastToken(), "", Duration.WhileOnBattlefield, true, false); TriggeredAbility ability = new OpponentPlaysLandTriggeredAbility(Zone.BATTLEFIELD, effect, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_ENCHANTMENT_PERMANENT), + this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_PERMANENT_ENCHANTMENT), "Whenever an opponent plays a land, if Hidden Stag is an enchantment, Hidden Stag becomes a 3/2 Elk Beast creature.")); // Whenever you play a land, if Hidden Stag is a creature, Hidden Stag becomes an enchantment. diff --git a/Mage.Sets/src/mage/cards/h/HiddenStockpile.java b/Mage.Sets/src/mage/cards/h/HiddenStockpile.java index 9d50a5e0e5c..5708f76fab6 100644 --- a/Mage.Sets/src/mage/cards/h/HiddenStockpile.java +++ b/Mage.Sets/src/mage/cards/h/HiddenStockpile.java @@ -1,7 +1,5 @@ - package mage.cards.h; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.BeginningOfYourEndStepTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; @@ -15,14 +13,15 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.AbilityWord; import mage.constants.CardType; -import mage.constants.Zone; -import static mage.filter.StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT; import mage.game.permanent.token.ServoToken; import mage.target.common.TargetControlledCreaturePermanent; import mage.watchers.common.RevoltWatcher; +import java.util.UUID; + +import static mage.filter.StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT; + /** - * * @author LevelX2 */ public final class HiddenStockpile extends CardImpl { @@ -31,14 +30,14 @@ public final class HiddenStockpile extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}{B}"); // Revolt — At the beginning of your end step, if a permanent you controlled left the battlefield this turn, create a 1/1 colorless Servo artifact creature token. - Ability ability = new ConditionalInterveningIfTriggeredAbility(new BeginningOfYourEndStepTriggeredAbility(new CreateTokenEffect(new ServoToken()), false), RevoltCondition.instance, - "Revolt — At the beginning of your end step, if a permanent you controlled left the battlefield this turn, create a 1/1 colorless Servo artifact creature token."); - ability.setAbilityWord(AbilityWord.REVOLT); - ability.addWatcher(new RevoltWatcher()); - this.addAbility(ability); + this.addAbility(new ConditionalInterveningIfTriggeredAbility( + new BeginningOfYourEndStepTriggeredAbility(new CreateTokenEffect(new ServoToken()), false), + RevoltCondition.instance, "At the beginning of your end step, if a permanent you controlled " + + "left the battlefield this turn, create a 1/1 colorless Servo artifact creature token." + ).setAbilityWord(AbilityWord.REVOLT), new RevoltWatcher()); // {1}, Sacrifice a creature: Scry 1. - ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ScryEffect(1), new GenericManaCost(1)); + Ability ability = new SimpleActivatedAbility(new ScryEffect(1, false), new GenericManaCost(1)); ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(FILTER_CONTROLLED_CREATURE_SHORT_TEXT))); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/h/HideousEnd.java b/Mage.Sets/src/mage/cards/h/HideousEnd.java index 550944f1cfa..49c15b446f2 100644 --- a/Mage.Sets/src/mage/cards/h/HideousEnd.java +++ b/Mage.Sets/src/mage/cards/h/HideousEnd.java @@ -1,16 +1,12 @@ - package mage.cards.h; import java.util.UUID; -import mage.ObjectColor; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.LoseLifeTargetControllerEffect; 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; /** @@ -19,18 +15,11 @@ import mage.target.common.TargetCreaturePermanent; */ public final class HideousEnd extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public HideousEnd(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{B}{B}"); - // Destroy target nonblack creature. Its controller loses 2 life. - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); this.getSpellAbility().addEffect(new DestroyTargetEffect()); this.getSpellAbility().addEffect(new LoseLifeTargetControllerEffect(2)); } diff --git a/Mage.Sets/src/mage/cards/h/HideousVisage.java b/Mage.Sets/src/mage/cards/h/HideousVisage.java index babcd2c50d1..f67f7fa44c9 100644 --- a/Mage.Sets/src/mage/cards/h/HideousVisage.java +++ b/Mage.Sets/src/mage/cards/h/HideousVisage.java @@ -8,7 +8,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; /** * @@ -16,11 +16,17 @@ import mage.filter.common.FilterControlledCreaturePermanent; */ public final class HideousVisage extends CardImpl { + private static final String rule = "creatures you control gain intimidate until end of turn. " + + "(Each of those creatures can't be blocked except by artifact creatures and/or " + + "creatures that share a color with it.)"; + public HideousVisage(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{B}"); - - this.getSpellAbility().addEffect(new GainAbilityControlledEffect(IntimidateAbility.getInstance(), Duration.EndOfTurn, new FilterControlledCreaturePermanent("Creatures"))); + this.getSpellAbility().addEffect(new GainAbilityControlledEffect( + IntimidateAbility.getInstance(), + Duration.EndOfTurn, + StaticFilters.FILTER_PERMANENT_CREATURES).setText(rule)); } private HideousVisage(final HideousVisage card) { diff --git a/Mage.Sets/src/mage/cards/h/HidetsuguConsumesAll.java b/Mage.Sets/src/mage/cards/h/HidetsuguConsumesAll.java new file mode 100644 index 00000000000..16e6ba35dc0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HidetsuguConsumesAll.java @@ -0,0 +1,62 @@ +package mage.cards.h; + +import mage.abilities.common.SagaAbility; +import mage.abilities.effects.common.DestroyAllEffect; +import mage.abilities.effects.common.ExileGraveyardAllPlayersEffect; +import mage.abilities.effects.common.ExileSagaAndReturnTransformedEffect; +import mage.abilities.keyword.TransformAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.SagaChapter; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterNonlandPermanent; +import mage.filter.predicate.mageobject.ManaValuePredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HidetsuguConsumesAll extends CardImpl { + + private static final FilterPermanent filter = new FilterNonlandPermanent(); + + static { + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 2)); + } + + public HidetsuguConsumesAll(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}{R}"); + + this.subtype.add(SubType.SAGA); + this.secondSideCardClazz = mage.cards.v.VesselOfTheAllConsuming.class; + + // (As this Saga enters and after your draw step, add a lore counter.) + SagaAbility sagaAbility = new SagaAbility(this); + + // I — Destroy each nonland permanent with mana value 1 or less. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, new DestroyAllEffect(filter) + .setText("destroy each nonland permanent with mana value 1 or less")); + + // II — Exile all graveyards. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_II, new ExileGraveyardAllPlayersEffect()); + + // III — Exile this Saga, then return it to the battlefield transformed under your control. + this.addAbility(new TransformAbility()); + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, new ExileSagaAndReturnTransformedEffect()); + + this.addAbility(sagaAbility, mage.cards.v.VesselOfTheAllConsuming.makeWatcher()); + } + + private HidetsuguConsumesAll(final HidetsuguConsumesAll card) { + super(card); + } + + @Override + public HidetsuguConsumesAll copy() { + return new HidetsuguConsumesAll(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HidetsuguDevouringChaos.java b/Mage.Sets/src/mage/cards/h/HidetsuguDevouringChaos.java new file mode 100644 index 00000000000..562045546f8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HidetsuguDevouringChaos.java @@ -0,0 +1,105 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; +import mage.abilities.effects.keyword.ScryEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetAnyTarget; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HidetsuguDevouringChaos extends CardImpl { + + public HidetsuguDevouringChaos(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.OGRE); + this.subtype.add(SubType.DEMON); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // {B}, Sacrifice a creature: Scry 2. + Ability ability = new SimpleActivatedAbility(new ScryEffect(2), new ManaCostsImpl<>("{B}")); + ability.addCost(new SacrificeTargetCost( + new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT) + )); + this.addAbility(ability); + + // {2}{R}, {T}: Exile the top card of your library. You may play that card this turn. When you exile a nonland card this way, Hidetsugu, Devouring Chaos deals damage equal to the exiled card's mana value to any target. + ability = new SimpleActivatedAbility(new HidetsuguDevouringChaosEffect(), new ManaCostsImpl<>("{2}{R}")); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + } + + private HidetsuguDevouringChaos(final HidetsuguDevouringChaos card) { + super(card); + } + + @Override + public HidetsuguDevouringChaos copy() { + return new HidetsuguDevouringChaos(this); + } +} + +class HidetsuguDevouringChaosEffect extends OneShotEffect { + + HidetsuguDevouringChaosEffect() { + super(Outcome.Benefit); + staticText = "exile the top card of your library. You may play that card this turn. " + + "When you exile a nonland card this way, {this} deals damage equal " + + "to the exiled card's mana value to any target"; + } + + private HidetsuguDevouringChaosEffect(final HidetsuguDevouringChaosEffect effect) { + super(effect); + } + + @Override + public HidetsuguDevouringChaosEffect copy() { + return new HidetsuguDevouringChaosEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Card card = player.getLibrary().getFromTop(game); + if (card == null) { + return false; + } + PlayFromNotOwnHandZoneTargetEffect.exileAndPlayFromExile( + game, source, card, TargetController.YOU, Duration.EndOfTurn, + false, false, false + ); + if (card.isLand(game)) { + return true; + } + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new DamageTargetEffect(card.getManaValue()), false + ); + ability.addTarget(new TargetAnyTarget()); + game.fireReflexiveTriggeredAbility(ability, source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/h/HighSentinelsOfArashin.java b/Mage.Sets/src/mage/cards/h/HighSentinelsOfArashin.java index 6c5681f93b9..8e36375c7ca 100644 --- a/Mage.Sets/src/mage/cards/h/HighSentinelsOfArashin.java +++ b/Mage.Sets/src/mage/cards/h/HighSentinelsOfArashin.java @@ -19,8 +19,7 @@ import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Zone; import mage.counters.CounterType; -import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; /** @@ -28,12 +27,6 @@ import mage.target.common.TargetCreaturePermanent; * @author emerald000 */ public final class HighSentinelsOfArashin extends CardImpl { - - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("other creature you control with a +1/+1 counter on it"); - static { - filter.add(AnotherPredicate.instance); - filter.add(CounterType.P1P1.getPredicate()); - } public HighSentinelsOfArashin(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{W}"); @@ -47,7 +40,7 @@ public final class HighSentinelsOfArashin extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // High Sentinels of Arashin gets +1/+1 for each other creature you control with a +1/+1 counter on it. - DynamicValue count = new PermanentsOnBattlefieldCount(filter); + DynamicValue count = new PermanentsOnBattlefieldCount(StaticFilters.FILTER_OTHER_CONTROLLED_CREATURE_P1P1); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostSourceEffect(count, count, Duration.WhileOnBattlefield))); // {3}{W}: Put a +1/+1 counter on target creature. diff --git a/Mage.Sets/src/mage/cards/h/HighSpeedHoverbike.java b/Mage.Sets/src/mage/cards/h/HighSpeedHoverbike.java new file mode 100644 index 00000000000..3aa8c87d62b --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HighSpeedHoverbike.java @@ -0,0 +1,53 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.keyword.CrewAbility; +import mage.abilities.keyword.FlashAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HighSpeedHoverbike extends CardImpl { + + public HighSpeedHoverbike(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); + + this.subtype.add(SubType.VEHICLE); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When High-Speed Hoverbike enters the battlefield, tap up to one target creature. + Ability ability = new EntersBattlefieldTriggeredAbility(new TapTargetEffect()); + ability.addTarget(new TargetCreaturePermanent(0, 1)); + this.addAbility(ability); + + // Crew 1 + this.addAbility(new CrewAbility(1)); + } + + private HighSpeedHoverbike(final HighSpeedHoverbike card) { + super(card); + } + + @Override + public HighSpeedHoverbike copy() { + return new HighSpeedHoverbike(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HigureTheStillWind.java b/Mage.Sets/src/mage/cards/h/HigureTheStillWind.java index a23bc7130e4..cceb8966dee 100644 --- a/Mage.Sets/src/mage/cards/h/HigureTheStillWind.java +++ b/Mage.Sets/src/mage/cards/h/HigureTheStillWind.java @@ -46,7 +46,7 @@ public final class HigureTheStillWind extends CardImpl { this.toughness = new MageInt(4); // Ninjutsu {2}{U}{U} ({2}{U}{U}, Return an unblocked attacker you control to hand: Put this card onto the battlefield from your hand tapped and attacking.) - this.addAbility(new NinjutsuAbility(new ManaCostsImpl("{2}{U}{U}"))); + this.addAbility(new NinjutsuAbility("{2}{U}{U}")); // Whenever Higure, the Still Wind deals combat damage to a player, you may search your library for a Ninja card, reveal it, and put it into your hand. If you do, shuffle your library. this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filter), true, false), true)); diff --git a/Mage.Sets/src/mage/cards/h/HikariTwilightGuardian.java b/Mage.Sets/src/mage/cards/h/HikariTwilightGuardian.java index b6babcaa4b3..116f61bd0c9 100644 --- a/Mage.Sets/src/mage/cards/h/HikariTwilightGuardian.java +++ b/Mage.Sets/src/mage/cards/h/HikariTwilightGuardian.java @@ -33,7 +33,7 @@ public final class HikariTwilightGuardian extends CardImpl { // Whenever you cast a Spirit or Arcane spell, you may exile Hikari, Twilight Guardian. If you do, return it to the battlefield under its owner's control at the beginning of the next end step. Effect effect = new ExileReturnBattlefieldOwnerNextEndStepSourceEffect(true); effect.setText("you may exile {this}. If you do, return it to the battlefield under its owner's control at the beginning of the next end step"); - this.addAbility(new SpellCastControllerTriggeredAbility(effect, StaticFilters.SPIRIT_OR_ARCANE_CARD, true)); + this.addAbility(new SpellCastControllerTriggeredAbility(effect, StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true)); } private HikariTwilightGuardian(final HikariTwilightGuardian card) { diff --git a/Mage.Sets/src/mage/cards/h/HinataDawnCrowned.java b/Mage.Sets/src/mage/cards/h/HinataDawnCrowned.java new file mode 100644 index 00000000000..a657e97f230 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HinataDawnCrowned.java @@ -0,0 +1,122 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.util.CardUtil; + +import java.util.Set; +import java.util.UUID; + +/** + * @author Arketec + */ +public final class HinataDawnCrowned extends CardImpl { + + public HinataDawnCrowned(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{R}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.KIRIN); + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Spells you cast cost {1} less to cast for each target. + this.addAbility(new SimpleStaticAbility(new HinataDawnCrownedOwnEffect())); + + // Spells your opponents cast cost {1} more to cast for each target + this.addAbility(new SimpleStaticAbility(new HinataDawnCrownedOpponentsEffect())); + } + + private HinataDawnCrowned(final HinataDawnCrowned card) { + super(card); + } + + @Override + public HinataDawnCrowned copy() { + return new HinataDawnCrowned(this); + } +} + +class HinataDawnCrownedOwnEffect extends CostModificationEffectImpl { + + HinataDawnCrownedOwnEffect() { + super(Duration.WhileOnBattlefield, Outcome.Neutral, CostModificationType.REDUCE_COST); + staticText = "Spells you cast cost {1} less to cast for each target"; + } + + private HinataDawnCrownedOwnEffect(HinataDawnCrownedOwnEffect effect) { + super(effect); + } + + @Override + public HinataDawnCrownedOwnEffect copy() { + return new HinataDawnCrownedOwnEffect(this); + } + + @Override + public boolean apply(Game game, Ability source, Ability abilityToModify) { + CardUtil.reduceCost(abilityToModify, HinataDawnCrownedEffectUtility.getTargetCount(game, abilityToModify)); + return true; + } + + @Override + public boolean applies(Ability abilityToModify, Ability source, Game game) { + return abilityToModify instanceof SpellAbility + && abilityToModify.isControlledBy(source.getControllerId()); + } +} + +class HinataDawnCrownedOpponentsEffect extends CostModificationEffectImpl { + + HinataDawnCrownedOpponentsEffect() { + super(Duration.WhileOnBattlefield, Outcome.Neutral, CostModificationType.INCREASE_COST); + staticText = "Spells your opponents cast cost {1} more to cast for each target"; + } + + private HinataDawnCrownedOpponentsEffect(HinataDawnCrownedOpponentsEffect effect) { + super(effect); + } + + @Override + public HinataDawnCrownedOpponentsEffect copy() { + return new HinataDawnCrownedOpponentsEffect(this); + } + + @Override + public boolean apply(Game game, Ability source, Ability abilityToModify) { + CardUtil.increaseCost(abilityToModify, HinataDawnCrownedEffectUtility.getTargetCount(game, abilityToModify)); + return true; + } + + @Override + public boolean applies(Ability abilityToModify, Ability source, Game game) { + return abilityToModify instanceof SpellAbility + && !abilityToModify.isControlledBy(source.getControllerId()); + } +} + +final class HinataDawnCrownedEffectUtility +{ + public static int getTargetCount(Game game, Ability abilityToModify) + { + return (int)(game.inCheckPlayableState() ? + CardUtil.getAllPossibleTargets(abilityToModify, game).stream().count(): + CardUtil.getAllSelectedTargets(abilityToModify, game).stream().count()); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HintOfInsanity.java b/Mage.Sets/src/mage/cards/h/HintOfInsanity.java index e236b811632..5d8af4de02f 100644 --- a/Mage.Sets/src/mage/cards/h/HintOfInsanity.java +++ b/Mage.Sets/src/mage/cards/h/HintOfInsanity.java @@ -12,6 +12,7 @@ import mage.constants.Outcome; import mage.game.Game; import mage.players.Player; import mage.target.TargetPlayer; +import mage.util.CardUtil; import java.util.HashMap; import java.util.Map; @@ -70,7 +71,7 @@ class HintOfInsanityEffect extends OneShotEffect { .getCards(game) .stream() .map(MageObject::getName) - .forEach(s -> nameCounts.compute(s, (u, i) -> i == null ? 1 : Integer.sum(i, 1))); + .forEach(s -> nameCounts.compute(s, CardUtil::setOrIncrementValue)); Cards cards = new CardsImpl( player.getHand() .getCards(game) diff --git a/Mage.Sets/src/mage/cards/h/HinterlandDrake.java b/Mage.Sets/src/mage/cards/h/HinterlandDrake.java index df9ac61ec05..dc136208efc 100644 --- a/Mage.Sets/src/mage/cards/h/HinterlandDrake.java +++ b/Mage.Sets/src/mage/cards/h/HinterlandDrake.java @@ -32,7 +32,7 @@ public final class HinterlandDrake extends CardImpl { // Hinterland Drake can't block artifact creatures. Effect effect = new CantBlockCreaturesSourceEffect(StaticFilters.FILTER_PERMANENT_ARTIFACT_CREATURE); - effect.setText("{this} can't block artifact creatures"); + effect.setText("{this} can't block artifact creatures"); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); } diff --git a/Mage.Sets/src/mage/cards/h/HinterlandHermit.java b/Mage.Sets/src/mage/cards/h/HinterlandHermit.java index f1d42887767..549ec1ed1bd 100644 --- a/Mage.Sets/src/mage/cards/h/HinterlandHermit.java +++ b/Mage.Sets/src/mage/cards/h/HinterlandHermit.java @@ -20,7 +20,6 @@ public final class HinterlandHermit extends CardImpl { this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.WEREWOLF); - this.transformable = true; this.secondSideCardClazz = HinterlandScourge.class; this.power = new MageInt(2); diff --git a/Mage.Sets/src/mage/cards/h/HinterlandLogger.java b/Mage.Sets/src/mage/cards/h/HinterlandLogger.java index a8e62953227..51df4a09630 100644 --- a/Mage.Sets/src/mage/cards/h/HinterlandLogger.java +++ b/Mage.Sets/src/mage/cards/h/HinterlandLogger.java @@ -22,7 +22,6 @@ public final class HinterlandLogger extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(1); - this.transformable = true; this.secondSideCardClazz = mage.cards.t.TimberShredder.class; // At the beginning of each upkeep, if no spells were cast last turn, transform Hinterland Logger. diff --git a/Mage.Sets/src/mage/cards/h/HinterlandScourge.java b/Mage.Sets/src/mage/cards/h/HinterlandScourge.java index 155deff8108..942dfb7e1ef 100644 --- a/Mage.Sets/src/mage/cards/h/HinterlandScourge.java +++ b/Mage.Sets/src/mage/cards/h/HinterlandScourge.java @@ -25,7 +25,6 @@ public final class HinterlandScourge extends CardImpl { // this card is the second face of double-faced card of Hinterland Hermit this.nightCard = true; - this.transformable = true; this.power = new MageInt(3); this.toughness = new MageInt(2); diff --git a/Mage.Sets/src/mage/cards/h/HiredMuscle.java b/Mage.Sets/src/mage/cards/h/HiredMuscle.java index 372d3854eb5..09f17bae569 100644 --- a/Mage.Sets/src/mage/cards/h/HiredMuscle.java +++ b/Mage.Sets/src/mage/cards/h/HiredMuscle.java @@ -44,7 +44,7 @@ public final class HiredMuscle extends CardImpl { this.flipCardName = "Scarmaker"; // Whenever you cast a Spirit or Arcane spell, you may put a ki counter on Hired Muscle. - this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.KI.createInstance()), StaticFilters.SPIRIT_OR_ARCANE_CARD, true)); + this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.KI.createInstance()), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true)); // At the beginning of the end step, if there are two or more ki counters on Hired Muscle, you may flip it. this.addAbility(new ConditionalInterveningIfTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/h/HistoriansWisdom.java b/Mage.Sets/src/mage/cards/h/HistoriansWisdom.java new file mode 100644 index 00000000000..540838ad4eb --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HistoriansWisdom.java @@ -0,0 +1,94 @@ +package mage.cards.h; + +import java.util.UUID; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.AttachedToMatchesFilterCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.constants.ComparisonType; +import mage.constants.SubType; +import mage.abilities.effects.common.AttachEffect; +import mage.constants.Outcome; +import mage.filter.StaticFilters; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.PowerPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; + +/** + * + * @author weirddan455 + */ +public final class HistoriansWisdom extends CardImpl { + + private static final Condition creatureCondition = new AttachedToMatchesFilterCondition(StaticFilters.FILTER_PERMANENT_CREATURE); + + public HistoriansWisdom(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}"); + + 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())); + + // When Historian's Wisdom enters the battlefield, if enchanted permanent is a creature with the greatest power among creatures on the battlefield, draw a card. + this.addAbility(new ConditionalInterveningIfTriggeredAbility( + new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(1)), + HistoriansWisdomCondition.instance, + "When {this} enters the battlefield, if enchanted permanent is a creature with the greatest power among creatures on the battlefield, draw a card." + )); + + // As long as enchanted permanent is a creature, it gets +2/+1. + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new BoostEnchantedEffect(2, 1), + creatureCondition, + "As long as enchanted permanent is a creature, it gets +2/+1" + ))); + } + + private HistoriansWisdom(final HistoriansWisdom card) { + super(card); + } + + @Override + public HistoriansWisdom copy() { + return new HistoriansWisdom(this); + } +} + +enum HistoriansWisdomCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + game.applyEffects(); // Make sure +2/+1 buff gets applied first + Permanent enchantment = source.getSourcePermanentIfItStillExists(game); + if (enchantment == null) { + return false; + } + Permanent creature = game.getPermanent(enchantment.getAttachedTo()); + if (creature == null) { + return false; + } + if (!creature.isCreature(game)) { + return false; + } + FilterCreaturePermanent filter = new FilterCreaturePermanent(); + filter.add(new PowerPredicate(ComparisonType.MORE_THAN, creature.getPower().getValue())); + return game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game).isEmpty(); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HistoryOfBenalia.java b/Mage.Sets/src/mage/cards/h/HistoryOfBenalia.java index 4cb472fb0dd..db9757813a2 100644 --- a/Mage.Sets/src/mage/cards/h/HistoryOfBenalia.java +++ b/Mage.Sets/src/mage/cards/h/HistoryOfBenalia.java @@ -26,7 +26,7 @@ public final class HistoryOfBenalia extends CardImpl { this.subtype.add(SubType.SAGA); // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) - SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); + SagaAbility sagaAbility = new SagaAbility(this); // I, II — Create a 2/2 white Knight creature token with vigilance. sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_II, new CreateTokenEffect(new KnightToken())); // III — Knights you control get +2/+1 until end of turn. diff --git a/Mage.Sets/src/mage/cards/h/HiveheartShaman.java b/Mage.Sets/src/mage/cards/h/HiveheartShaman.java new file mode 100644 index 00000000000..35f5f608a75 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HiveheartShaman.java @@ -0,0 +1,132 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.DomainValue; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; +import mage.abilities.hint.common.DomainHint; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.InsectToken; +import mage.game.permanent.token.Token; +import mage.target.common.TargetCardInLibrary; + +import java.util.Collection; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HiveheartShaman extends CardImpl { + + private static final FilterCard filter = new FilterCard( + "basic land card that doesn't share a land type with a land you control" + ); + + static { + filter.add(HiveheartShamanPredicate.instance); + } + + public HiveheartShaman(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SHAMAN); + this.power = new MageInt(3); + this.toughness = new MageInt(5); + + // Whenever Hiveheart Shaman attacks, you may search your library for a basic land card that doesn't share a land type with a land you control, put that card onto the battlefield, then shuffle. + this.addAbility(new AttacksTriggeredAbility( + new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(filter)), true + )); + + // {5}{G}: Create a 1/1 green Insect creature token. Put X +1/+1 counters on it, where X is the number of basic land types among lands you control. Activate only as a sorcery. + this.addAbility(new ActivateAsSorceryActivatedAbility( + new HiveheartShamanEffect(), new ManaCostsImpl<>("{5}{G}") + ).addHint(DomainHint.instance)); + } + + private HiveheartShaman(final HiveheartShaman card) { + super(card); + } + + @Override + public HiveheartShaman copy() { + return new HiveheartShaman(this); + } +} + +enum HiveheartShamanPredicate implements ObjectSourcePlayerPredicate { + instance; + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + if (!input.getObject().isBasic() || !input.getObject().isLand(game)) { + return false; + } + return game.getBattlefield() + .getActivePermanents( + StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND, + input.getPlayerId(), input.getSourceId(), game + ) + .stream() + .map(permanent -> permanent.getSubtype(game)) + .flatMap(Collection::stream) + .filter(subType -> subType.getSubTypeSet().isLand()) + .noneMatch(subType -> input.getObject().hasSubtype(subType, game)); + } +} + +class HiveheartShamanEffect extends OneShotEffect { + + private static final DynamicValue xValue = new DomainValue(); + + HiveheartShamanEffect() { + super(Outcome.Benefit); + staticText = "create a 1/1 green Insect creature token. Put X +1/+1 counters on it, " + + "where X is the number of basic land types among lands you control"; + } + + private HiveheartShamanEffect(final HiveheartShamanEffect effect) { + super(effect); + } + + @Override + public HiveheartShamanEffect copy() { + return new HiveheartShamanEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Token token = new InsectToken(); + token.putOntoBattlefield(1, game, source); + int domainCount = xValue.calculate(game, source, this); + if (domainCount < 1) { + return true; + } + for (UUID tokenId : token.getLastAddedTokenIds()) { + Permanent permanent = game.getPermanent(tokenId); + if (permanent == null) { + continue; + } + permanent.addCounters(CounterType.P1P1.createInstance(domainCount), source, game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/h/HivisOfTheScale.java b/Mage.Sets/src/mage/cards/h/HivisOfTheScale.java index 07c11c9ebbb..928c1a9b2b3 100644 --- a/Mage.Sets/src/mage/cards/h/HivisOfTheScale.java +++ b/Mage.Sets/src/mage/cards/h/HivisOfTheScale.java @@ -1,16 +1,13 @@ package mage.cards.h; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SkipUntapOptionalAbility; -import mage.abilities.condition.Condition; -import mage.abilities.condition.common.SourceMatchesFilterCondition; +import mage.abilities.condition.common.SourceTappedCondition; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.decorator.ConditionalContinuousEffect; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.GainControlTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -18,31 +15,25 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; import mage.constants.SuperType; -import mage.constants.TargetController; -import mage.constants.Zone; import mage.filter.FilterPermanent; -import mage.filter.predicate.permanent.TappedPredicate; import mage.target.TargetPermanent; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class HivisOfTheScale extends CardImpl { - - private static final FilterPermanent filter = new FilterPermanent(); + private static final FilterPermanent filterDragon = new FilterPermanent(); - private static final String rule = "Gain control of target Dragon for as long as you control Hivis and Hivis remains tapped."; - + static { - filter.add(TappedPredicate.TAPPED); - filter.add(TargetController.YOU.getControllerPredicate()); filterDragon.add(SubType.DRAGON.getPredicate()); } public HivisOfTheScale(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{R}"); - + addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.VIASHINO); this.subtype.add(SubType.SHAMAN); @@ -51,14 +42,14 @@ public final class HivisOfTheScale extends CardImpl { // You may choose not to untap Hivis of the Scale during your untap step. this.addAbility(new SkipUntapOptionalAbility()); - + // {tap}: Gain control of target Dragon for as long as you control Hivis and Hivis remains tapped. - Condition condition = new SourceMatchesFilterCondition(filter); - Effect effect = new ConditionalContinuousEffect(new GainControlTargetEffect(Duration.Custom), condition, rule); - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new TapSourceCost()); + Ability ability = new SimpleActivatedAbility(new ConditionalContinuousEffect( + new GainControlTargetEffect(Duration.WhileControlled), SourceTappedCondition.TAPPED, + "gain control of target Dragon for as long as you control {this} and {this} remains tapped" + ), new TapSourceCost()); ability.addTarget(new TargetPermanent(filterDragon)); this.addAbility(ability); - } private HivisOfTheScale(final HivisOfTheScale card) { diff --git a/Mage.Sets/src/mage/cards/h/HixusPrisonWarden.java b/Mage.Sets/src/mage/cards/h/HixusPrisonWarden.java index d6d498a2d03..a100d064ff6 100644 --- a/Mage.Sets/src/mage/cards/h/HixusPrisonWarden.java +++ b/Mage.Sets/src/mage/cards/h/HixusPrisonWarden.java @@ -92,7 +92,7 @@ class HixusPrisonWardenTriggeredAbility extends TriggeredAbilityImpl { && damageEvent.isCombatDamage() && sourcePermanent != null && sourcePermanent.isCreature(game)) { - getEffects().get(0).setTargetPointer(new FixedTarget(event.getSourceId())); + getEffects().get(0).setTargetPointer(new FixedTarget(event.getSourceId(), game)); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/h/HoldTheLine.java b/Mage.Sets/src/mage/cards/h/HoldTheLine.java index 6be43cad89e..a43199e87da 100644 --- a/Mage.Sets/src/mage/cards/h/HoldTheLine.java +++ b/Mage.Sets/src/mage/cards/h/HoldTheLine.java @@ -1,4 +1,3 @@ - package mage.cards.h; import java.util.UUID; @@ -7,7 +6,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.common.FilterBlockingCreature; +import mage.filter.StaticFilters; /** * @@ -15,13 +14,11 @@ import mage.filter.common.FilterBlockingCreature; */ public final class HoldTheLine extends CardImpl { - private static final FilterBlockingCreature filter = new FilterBlockingCreature("Blocking creatures"); - public HoldTheLine(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{W}{W}"); // Blocking creatures get +7/+7 until end of turn. - this.getSpellAbility().addEffect(new BoostAllEffect(7, 7, Duration.EndOfTurn, filter, false)); + this.getSpellAbility().addEffect(new BoostAllEffect(7, 7, Duration.EndOfTurn, StaticFilters.FILTER_BLOCKING_CREATURES, false)); } private HoldTheLine(final HoldTheLine card) { diff --git a/Mage.Sets/src/mage/cards/h/HolisticWisdom.java b/Mage.Sets/src/mage/cards/h/HolisticWisdom.java index 4e4dd5a4034..b1731349722 100644 --- a/Mage.Sets/src/mage/cards/h/HolisticWisdom.java +++ b/Mage.Sets/src/mage/cards/h/HolisticWisdom.java @@ -80,7 +80,7 @@ class HolisticWisdomEffect extends OneShotEffect { for (CardType cardtype : card.getCardType(game)) { if (cardtypes.contains(cardtype)) { Effect effect = new ReturnToHandTargetEffect(); - effect.setTargetPointer(new FixedTarget(card.getId())); + effect.setTargetPointer(new FixedTarget(card.getId(), game)); return effect.apply(game, source); } } diff --git a/Mage.Sets/src/mage/cards/h/HollowSpecter.java b/Mage.Sets/src/mage/cards/h/HollowSpecter.java index 3e7a9300cda..418cfcee0ac 100644 --- a/Mage.Sets/src/mage/cards/h/HollowSpecter.java +++ b/Mage.Sets/src/mage/cards/h/HollowSpecter.java @@ -4,16 +4,15 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.discard.DiscardCardYouChooseTargetEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.*; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.FilterCard; +import mage.constants.TargetController; import mage.game.Game; import mage.players.Player; -import mage.target.TargetCard; import mage.util.ManaUtil; import java.util.List; @@ -70,50 +69,13 @@ class HollowSpecterEffect extends OneShotEffect { Player targetPlayer = game.getPlayer(this.getTargetPointer().getFirst(game, source)); Player controller = game.getPlayer(source.getControllerId()); - if (targetPlayer != null && controller != null && controller.chooseUse(Outcome.Benefit, "Do you want to to pay {X}?", source, game)) { + if (targetPlayer != null && controller != null && controller.chooseUse(Outcome.Benefit, "Pay {X}?", source, game)) { int payCount = ManaUtil.playerPaysXGenericMana(true, "Hollow Specter", controller, source, game); if (payCount > 0) { - // find to reveal - Cards revealedCards = new CardsImpl(); - if (targetPlayer.getHand().size() > payCount) { - Cards cardsInHand = new CardsImpl(); - cardsInHand.addAll(targetPlayer.getHand()); - TargetCard target = new TargetCard(payCount, Zone.HAND, new FilterCard()); - if (targetPlayer.choose(Outcome.Discard, cardsInHand, target, game)) { - List targets = target.getTargets(); - for (UUID targetId : targets) { - Card card = game.getCard(targetId); - if (card != null) { - revealedCards.add(card); - } - } - } else { - // take any cards on disconnect - targetPlayer.getHand().stream().limit(payCount).forEach(revealedCards::add); - } - } else { - revealedCards.addAll(targetPlayer.getHand()); - } - - // select to discard - TargetCard targetInHand = new TargetCard(Zone.HAND, new FilterCard("card to discard")); - if (!revealedCards.isEmpty()) { - targetPlayer.revealCards("Hollow Specter", revealedCards, game); - Card card; - if (revealedCards.size() > 1) { - controller.choose(Outcome.Discard, revealedCards, targetInHand, game); - card = revealedCards.get(targetInHand.getFirstTarget(), game); - } else { - card = revealedCards.getRandom(game); - } - - targetPlayer.discard(card, false, source, game); - - } + return new DiscardCardYouChooseTargetEffect(TargetController.ANY, payCount).setTargetPointer(targetPointer).apply(game, source); } - return true; } return false; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/h/HollowTrees.java b/Mage.Sets/src/mage/cards/h/HollowTrees.java index c814eb29b6e..317908a85fc 100644 --- a/Mage.Sets/src/mage/cards/h/HollowTrees.java +++ b/Mage.Sets/src/mage/cards/h/HollowTrees.java @@ -39,7 +39,7 @@ public final class HollowTrees extends CardImpl { this.addAbility(new SkipUntapOptionalAbility()); // At the beginning of your upkeep, if Hollow Trees is tapped, put a storage counter on it. OneShotEffect addStorageCounter = new AddCountersSourceEffect(CounterType.STORAGE.createInstance()); - Effect effect = new ConditionalOneShotEffect(addStorageCounter, SourceTappedCondition.instance, "if {this} is tapped, put a storage counter on it"); + Effect effect = new ConditionalOneShotEffect(addStorageCounter, SourceTappedCondition.TAPPED, "if {this} is tapped, put a storage counter on it"); this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, effect, TargetController.YOU, false)); // {tap}, Remove any number of storage counters from Hollow Trees: Add {G} for each storage counter removed this way. Ability ability = new DynamicManaAbility( diff --git a/Mage.Sets/src/mage/cards/h/HollowheadSliver.java b/Mage.Sets/src/mage/cards/h/HollowheadSliver.java index 33003fb3629..aace31bb7d8 100644 --- a/Mage.Sets/src/mage/cards/h/HollowheadSliver.java +++ b/Mage.Sets/src/mage/cards/h/HollowheadSliver.java @@ -36,7 +36,7 @@ public final class HollowheadSliver extends CardImpl { ability.addCost(new DiscardCardCost()); this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( ability, Duration.WhileOnBattlefield, - StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS + StaticFilters.FILTER_PERMANENT_SLIVERS ))); } diff --git a/Mage.Sets/src/mage/cards/h/HollowhengeHuntmaster.java b/Mage.Sets/src/mage/cards/h/HollowhengeHuntmaster.java new file mode 100644 index 00000000000..251ab14f86e --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HollowhengeHuntmaster.java @@ -0,0 +1,64 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.abilities.common.BeginningOfCombatTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.effects.common.counter.AddCountersAllEffect; +import mage.abilities.keyword.HexproofAbility; +import mage.abilities.keyword.NightboundAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.counters.CounterType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HollowhengeHuntmaster extends CardImpl { + + public HollowhengeHuntmaster(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); + + this.subtype.add(SubType.WEREWOLF); + this.power = new MageInt(6); + this.toughness = new MageInt(6); + this.color.setGreen(true); + this.nightCard = true; + + // Hexproof + this.addAbility(HexproofAbility.getInstance()); + + // Other permanents you control have hexproof. + this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( + HexproofAbility.getInstance(), Duration.WhileOnBattlefield, + StaticFilters.FILTER_PERMANENTS, true + ))); + + // At the beginning of combat on your turn, put two +1/+1 counters on each creature you control. + this.addAbility(new BeginningOfCombatTriggeredAbility( + new AddCountersAllEffect( + CounterType.P1P1.createInstance(2), + StaticFilters.FILTER_CONTROLLED_CREATURE + ), TargetController.YOU, false + )); + + // Nightbound + this.addAbility(new NightboundAbility()); + } + + private HollowhengeHuntmaster(final HollowhengeHuntmaster card) { + super(card); + } + + @Override + public HollowhengeHuntmaster copy() { + return new HollowhengeHuntmaster(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HollowhengeOverlord.java b/Mage.Sets/src/mage/cards/h/HollowhengeOverlord.java new file mode 100644 index 00000000000..5dbdc38a4bb --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HollowhengeOverlord.java @@ -0,0 +1,69 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.FlashAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.game.permanent.token.WolfToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HollowhengeOverlord extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledCreaturePermanent(); + + static { + filter.add(Predicates.or( + SubType.WOLF.getPredicate(), + SubType.WEREWOLF.getPredicate() + )); + } + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter); + private static final Hint hint = new ValueHint( + "Wolves and Werewolves you control", xValue + ); + + public HollowhengeOverlord(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}{G}"); + + this.subtype.add(SubType.WOLF); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // At the beginning of your upkeep, for each creature you control that's a Wolf or a Werewolf, create a 2/2 green Wolf creature token. + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new CreateTokenEffect(new WolfToken(), xValue) + .setText("for each creature you control that's a Wolf or a Werewolf, " + + "create a 2/2 green Wolf creature token"), + TargetController.YOU, false + ).addHint(hint)); + } + + private HollowhengeOverlord(final HollowhengeOverlord card) { + super(card); + } + + @Override + public HollowhengeOverlord copy() { + return new HollowhengeOverlord(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HomewardPath.java b/Mage.Sets/src/mage/cards/h/HomewardPath.java index 464a5a49812..4a854b0ff58 100644 --- a/Mage.Sets/src/mage/cards/h/HomewardPath.java +++ b/Mage.Sets/src/mage/cards/h/HomewardPath.java @@ -1,43 +1,31 @@ - package mage.cards.h; -import java.util.Iterator; -import java.util.UUID; -import mage.MageObjectReference; -import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.common.continuous.GainControlAllOwnedEffect; import mage.abilities.mana.ColorlessManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Layer; -import mage.constants.Outcome; -import mage.constants.SubLayer; -import mage.constants.Zone; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.card.OwnerIdPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class HomewardPath extends CardImpl { public HomewardPath(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.LAND},""); + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); // {tap}: Add {C}. this.addAbility(new ColorlessManaAbility()); - // {tap}: Each player gains control of all creatures they own. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new HomewardPathControlEffect(), new TapSourceCost())); + // {tap}: Each player gains control of all creatures they own. + this.addAbility(new SimpleActivatedAbility(new GainControlAllOwnedEffect( + StaticFilters.FILTER_PERMANENT_CREATURES + ), new TapSourceCost())); } private HomewardPath(final HomewardPath card) { @@ -49,56 +37,3 @@ public final class HomewardPath extends CardImpl { return new HomewardPath(this); } } - -class HomewardPathControlEffect extends ContinuousEffectImpl { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); - - public HomewardPathControlEffect() { - super(Duration.EndOfGame, Layer.ControlChangingEffects_2, SubLayer.NA, Outcome.GainControl); - this.staticText = "Each player gains control of all creatures they own"; - } - - public HomewardPathControlEffect(final HomewardPathControlEffect effect) { - super(effect); - } - - @Override - public HomewardPathControlEffect copy() { - return new HomewardPathControlEffect(this); - } - - @Override - public void init(Ability source, Game game) { - super.init(source, game); - // add all creatures in range - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - FilterPermanent playerFilter = filter.copy(); - playerFilter.add(new OwnerIdPredicate(playerId)); - for (Permanent permanent :game.getBattlefield().getActivePermanents(playerFilter, playerId, game)) { - affectedObjectList.add(new MageObjectReference(permanent, game)); - } - } - } - } - - @Override - public boolean apply(Game game, Ability source) { - for (Iterator it = affectedObjectList.iterator(); it.hasNext();) { - Permanent creature = it.next().getPermanent(game); - if (creature != null) { - if (!creature.isControlledBy(creature.getOwnerId())) { - creature.changeControllerId(creature.getOwnerId(), game, source); - } - } else { - it.remove(); - } - } - if (affectedObjectList.isEmpty()) { - this.discard(); - } - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/h/HomicidalBrute.java b/Mage.Sets/src/mage/cards/h/HomicidalBrute.java index b4ec7d0412a..7e0ebe1baf0 100644 --- a/Mage.Sets/src/mage/cards/h/HomicidalBrute.java +++ b/Mage.Sets/src/mage/cards/h/HomicidalBrute.java @@ -10,10 +10,10 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; +import mage.constants.WatcherScope; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.watchers.Watcher; /** @@ -28,14 +28,13 @@ public final class HomicidalBrute extends CardImpl { // this card is the second face of double-faced card this.nightCard = true; - this.transformable = true; this.color.setRed(true); this.power = new MageInt(5); this.toughness = new MageInt(1); // At the beginning of your end step, if Homicidal Brute didn't attack this turn, tap Homicidal Brute, then transform it. - this.addAbility(new HomicidalBruteTriggeredAbility()); + this.addAbility(new HomicidalBruteTriggeredAbility(), new HomicidalBruteWatcher()); } private HomicidalBrute(final HomicidalBrute card) { @@ -53,7 +52,7 @@ class HomicidalBruteTriggeredAbility extends TriggeredAbilityImpl { public HomicidalBruteTriggeredAbility() { super(Zone.BATTLEFIELD, new TapSourceEffect(), false); - addEffect(new TransformSourceEffect(false)); + addEffect(new TransformSourceEffect()); } public HomicidalBruteTriggeredAbility(HomicidalBruteTriggeredAbility ability) { @@ -87,5 +86,19 @@ class HomicidalBruteTriggeredAbility extends TriggeredAbilityImpl { } } +class HomicidalBruteWatcher extends Watcher { + public HomicidalBruteWatcher() { + super(WatcherScope.CARD); + } + @Override + public void watch(GameEvent event, Game game) { + if (condition) { + return; + } + if (event.getType() == GameEvent.EventType.ATTACKER_DECLARED && event.getSourceId().equals(sourceId)) { + condition = true; + } + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/h/HomicidalBruteWatcher.java b/Mage.Sets/src/mage/cards/h/HomicidalBruteWatcher.java deleted file mode 100644 index 4425e8cba38..00000000000 --- a/Mage.Sets/src/mage/cards/h/HomicidalBruteWatcher.java +++ /dev/null @@ -1,23 +0,0 @@ -package mage.cards.h; - -import mage.constants.WatcherScope; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.watchers.Watcher; - -public class HomicidalBruteWatcher extends Watcher { - - public HomicidalBruteWatcher() { - super(WatcherScope.CARD); - } - - @Override - public void watch(GameEvent event, Game game) { - if (condition) { - return; - } - if (event.getType() == GameEvent.EventType.ATTACKER_DECLARED && event.getSourceId().equals(sourceId)) { - condition = true; - } - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/h/HomicidalSeclusion.java b/Mage.Sets/src/mage/cards/h/HomicidalSeclusion.java index c750030e003..8e946f4fc45 100644 --- a/Mage.Sets/src/mage/cards/h/HomicidalSeclusion.java +++ b/Mage.Sets/src/mage/cards/h/HomicidalSeclusion.java @@ -1,41 +1,43 @@ - package mage.cards.h; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.CreatureCountCondition; import mage.abilities.decorator.ConditionalContinuousEffect; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.BoostControlledEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.hint.common.CreaturesYouControlHint; import mage.abilities.keyword.LifelinkAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.TargetController; -import mage.constants.Zone; +import mage.filter.StaticFilters; + +import java.util.UUID; /** * @author noxx */ public final class HomicidalSeclusion extends CardImpl { - private static final String rule = "As long as you control exactly one creature, that creature gets +3/+1"; + private static final Condition condition = new CreatureCountCondition(1, TargetController.YOU); public HomicidalSeclusion(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{4}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{B}"); // As long as you control exactly one creature, that creature gets +3/+1 and has lifelink. - ContinuousEffect boostEffect = new BoostControlledEffect(3, 1, Duration.WhileOnBattlefield); - Effect effect = new ConditionalContinuousEffect(boostEffect, new CreatureCountCondition(1, TargetController.YOU), rule); - Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, effect); - ContinuousEffect lifelinkEffect = new GainAbilityControlledEffect(LifelinkAbility.getInstance(), Duration.WhileOnBattlefield); - effect = new ConditionalContinuousEffect(lifelinkEffect, new CreatureCountCondition(1, TargetController.YOU), "and has lifelink"); - ability.addEffect(effect); - this.addAbility(ability); + Ability ability = new SimpleStaticAbility(new ConditionalContinuousEffect( + new BoostControlledEffect(3, 1, Duration.WhileOnBattlefield), + condition, "As long as you control exactly one creature, that creature gets +3/+1" + )); + ability.addEffect(new ConditionalContinuousEffect(new GainAbilityControlledEffect( + LifelinkAbility.getInstance(), Duration.WhileOnBattlefield, + StaticFilters.FILTER_CONTROLLED_CREATURE + ), condition, "and has lifelink")); + this.addAbility(ability.addHint(CreaturesYouControlHint.instance)); } private HomicidalSeclusion(final HomicidalSeclusion card) { diff --git a/Mage.Sets/src/mage/cards/h/HoneymoonHearse.java b/Mage.Sets/src/mage/cards/h/HoneymoonHearse.java new file mode 100644 index 00000000000..cc4be54024a --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HoneymoonHearse.java @@ -0,0 +1,52 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapTargetCost; +import mage.abilities.effects.common.continuous.AddCardTypeSourceEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HoneymoonHearse extends CardImpl { + + public HoneymoonHearse(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{R}"); + + this.subtype.add(SubType.VEHICLE); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Tap two untapped creatures you control: Honeymoon Hearse becomes an artifact creature until end of turn. + this.addAbility(new SimpleActivatedAbility( + new AddCardTypeSourceEffect( + Duration.EndOfTurn, CardType.ARTIFACT, CardType.CREATURE + ).setText("{this} becomes an artifact creature until end of turn"), + new TapTargetCost(new TargetControlledPermanent( + 2, StaticFilters.FILTER_CONTROLLED_UNTAPPED_CREATURES + )) + )); + } + + private HoneymoonHearse(final HoneymoonHearse card) { + super(card); + } + + @Override + public HoneymoonHearse copy() { + return new HoneymoonHearse(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HonoredCropCaptain.java b/Mage.Sets/src/mage/cards/h/HonoredCropCaptain.java index 1125f2a7fda..7dcd1d5f8ba 100644 --- a/Mage.Sets/src/mage/cards/h/HonoredCropCaptain.java +++ b/Mage.Sets/src/mage/cards/h/HonoredCropCaptain.java @@ -1,4 +1,3 @@ - package mage.cards.h; import java.util.UUID; @@ -10,7 +9,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; -import mage.filter.common.FilterAttackingCreature; +import mage.filter.StaticFilters; /** * @@ -27,7 +26,7 @@ public final class HonoredCropCaptain extends CardImpl { this.toughness = new MageInt(2); // Whenever Honored Crop-Captain attacks, other attacking creatures get +1/+0 until end of turn. - this.addAbility(new AttacksTriggeredAbility(new BoostAllEffect(1, 0, Duration.EndOfTurn, new FilterAttackingCreature(), true), false)); + this.addAbility(new AttacksTriggeredAbility(new BoostAllEffect(1, 0, Duration.EndOfTurn, StaticFilters.FILTER_ATTACKING_CREATURES, true), false)); } diff --git a/Mage.Sets/src/mage/cards/h/HonoredHeirloom.java b/Mage.Sets/src/mage/cards/h/HonoredHeirloom.java new file mode 100644 index 00000000000..8c65c109b3e --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HonoredHeirloom.java @@ -0,0 +1,42 @@ +package mage.cards.h; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.ExileTargetEffect; +import mage.abilities.mana.AnyColorManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.target.common.TargetCardInGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HonoredHeirloom extends CardImpl { + + public HonoredHeirloom(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); + + // {T}: Add one mana of any color. + this.addAbility(new AnyColorManaAbility()); + + // {2}, {T}: Exile target card from a graveyard. + Ability ability = new SimpleActivatedAbility(new ExileTargetEffect(), new GenericManaCost(2)); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetCardInGraveyard()); + this.addAbility(ability); + } + + private HonoredHeirloom(final HonoredHeirloom card) { + super(card); + } + + @Override + public HonoredHeirloom copy() { + return new HonoredHeirloom(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HoodedHydra.java b/Mage.Sets/src/mage/cards/h/HoodedHydra.java index 06612e909f6..514b447d9b6 100644 --- a/Mage.Sets/src/mage/cards/h/HoodedHydra.java +++ b/Mage.Sets/src/mage/cards/h/HoodedHydra.java @@ -41,7 +41,7 @@ public final class HoodedHydra extends CardImpl { this.addAbility(new EntersBattlefieldAbility(new EntersBattlefieldWithXCountersEffect(CounterType.P1P1.createInstance()))); // When Hooded Hydra dies, create a 1/1 green Snake creature token for each +1/+1 counter on it. - this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new SnakeToken("KTK"), new CountersSourceCount(CounterType.P1P1)), false)); + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new SnakeToken(), new CountersSourceCount(CounterType.P1P1)), false)); // Morph {3}{G}{G} this.addAbility(new MorphAbility(this, new ManaCostsImpl("{3}{G}{G}"))); diff --git a/Mage.Sets/src/mage/cards/h/HookHauntDrifter.java b/Mage.Sets/src/mage/cards/h/HookHauntDrifter.java index bd3eaf73dd4..20484b801dd 100644 --- a/Mage.Sets/src/mage/cards/h/HookHauntDrifter.java +++ b/Mage.Sets/src/mage/cards/h/HookHauntDrifter.java @@ -23,7 +23,6 @@ public final class HookHauntDrifter extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(2); this.color.setBlue(true); - this.transformable = true; this.nightCard = true; // Flying diff --git a/Mage.Sets/src/mage/cards/h/HookhandMariner.java b/Mage.Sets/src/mage/cards/h/HookhandMariner.java new file mode 100644 index 00000000000..c2fc61ab523 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HookhandMariner.java @@ -0,0 +1,38 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.abilities.keyword.DayboundAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HookhandMariner extends CardImpl { + + public HookhandMariner(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WEREWOLF); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + this.secondSideCardClazz = mage.cards.r.RiphookRaider.class; + + // Daybound + this.addAbility(new DayboundAbility()); + } + + private HookhandMariner(final HookhandMariner card) { + super(card); + } + + @Override + public HookhandMariner copy() { + return new HookhandMariner(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HopefulInitiate.java b/Mage.Sets/src/mage/cards/h/HopefulInitiate.java new file mode 100644 index 00000000000..01197fc5adf --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HopefulInitiate.java @@ -0,0 +1,55 @@ +package mage.cards.h; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.RemoveCounterCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.constants.SubType; +import mage.abilities.keyword.TrainingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; +import mage.target.common.TargetControlledCreaturePermanent; + +/** + * + * @author weirddan455 + */ +public final class HopefulInitiate extends CardImpl { + + public HopefulInitiate(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARLOCK); + this.power = new MageInt(1); + this.toughness = new MageInt(2); + + // Training + this.addAbility(new TrainingAbility()); + + // {2}{W}, Remove two +1/+1 counters from among creatures you control: Destroy target artifact or enchantment. + Ability ability = new SimpleActivatedAbility(new DestroyTargetEffect(), new ManaCostsImpl<>("{2}{W}")); + ability.addCost(new RemoveCounterCost( + new TargetControlledCreaturePermanent(1, 2, StaticFilters.FILTER_CONTROLLED_CREATURES, true), + CounterType.P1P1, 2 + ).setText("Remove two +1/+1 counters from among creatures you control")); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_ENCHANTMENT)); + this.addAbility(ability); + } + + private HopefulInitiate(final HopefulInitiate card) { + super(card); + } + + @Override + public HopefulInitiate copy() { + return new HopefulInitiate(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HorizonSeed.java b/Mage.Sets/src/mage/cards/h/HorizonSeed.java index 02a4e031916..be6ed2dfa74 100644 --- a/Mage.Sets/src/mage/cards/h/HorizonSeed.java +++ b/Mage.Sets/src/mage/cards/h/HorizonSeed.java @@ -25,7 +25,7 @@ public final class HorizonSeed extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(1); - Ability ability = new SpellCastControllerTriggeredAbility(new RegenerateTargetEffect(), StaticFilters.SPIRIT_OR_ARCANE_CARD, false); + Ability ability = new SpellCastControllerTriggeredAbility(new RegenerateTargetEffect(), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/h/HornetNest.java b/Mage.Sets/src/mage/cards/h/HornetNest.java index 58ca524bb4d..83b448036e2 100644 --- a/Mage.Sets/src/mage/cards/h/HornetNest.java +++ b/Mage.Sets/src/mage/cards/h/HornetNest.java @@ -33,7 +33,7 @@ public final class HornetNest extends CardImpl { this.addAbility(DefenderAbility.getInstance()); // Whenever Hornet Nest is dealt damage, create that many 1/1 green Insect creature tokens with flying and deathtouch. - this.addAbility(new DealtDamageToSourceTriggeredAbility(new HornetNestDealDamageEffect(), false, false, true)); + this.addAbility(new DealtDamageToSourceTriggeredAbility(new HornetNestDealDamageEffect(), false, false)); } private HornetNest(final HornetNest card) { diff --git a/Mage.Sets/src/mage/cards/h/Hornswoggle.java b/Mage.Sets/src/mage/cards/h/Hornswoggle.java index 942e8006e2b..339c40b7645 100644 --- a/Mage.Sets/src/mage/cards/h/Hornswoggle.java +++ b/Mage.Sets/src/mage/cards/h/Hornswoggle.java @@ -1,7 +1,5 @@ - package mage.cards.h; -import java.util.UUID; import mage.abilities.effects.common.CounterTargetEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; @@ -11,8 +9,9 @@ import mage.filter.StaticFilters; import mage.game.permanent.token.TreasureToken; import mage.target.TargetSpell; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class Hornswoggle extends CardImpl { @@ -23,8 +22,7 @@ public final class Hornswoggle extends CardImpl { // Counter target creature spell. You create a colorless Treasure artifact token with "{T}, Sacrifice this artifact: Add one mana of any color." this.getSpellAbility().addEffect(new CounterTargetEffect()); this.getSpellAbility().addTarget(new TargetSpell(StaticFilters.FILTER_SPELL_CREATURE)); - this.getSpellAbility().addEffect(new CreateTokenEffect(new TreasureToken()) - .setText("You create a colorless Treasure artifact token with \"{T}, Sacrifice this artifact: Add one mana of any color.\"")); + this.getSpellAbility().addEffect(new CreateTokenEffect(new TreasureToken()).concatBy("You")); } private Hornswoggle(final Hornswoggle card) { diff --git a/Mage.Sets/src/mage/cards/h/HorobiDeathsWail.java b/Mage.Sets/src/mage/cards/h/HorobiDeathsWail.java index 6b235433914..b0221f4a075 100644 --- a/Mage.Sets/src/mage/cards/h/HorobiDeathsWail.java +++ b/Mage.Sets/src/mage/cards/h/HorobiDeathsWail.java @@ -1,4 +1,3 @@ - package mage.cards.h; import java.util.UUID; @@ -25,7 +24,7 @@ import mage.target.targetpointer.FixedTarget; public final class HorobiDeathsWail extends CardImpl { public HorobiDeathsWail(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{B}"); addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.SPIRIT); @@ -34,7 +33,7 @@ public final class HorobiDeathsWail extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); - + // Whenever a creature becomes the target of a spell or ability, destroy that creature. this.addAbility(new HorobiDeathsWailAbility(new DestroyTargetEffect())); } @@ -46,7 +45,7 @@ public final class HorobiDeathsWail extends CardImpl { @Override public HorobiDeathsWail copy() { return new HorobiDeathsWail(this); - } + } } @@ -74,7 +73,7 @@ class HorobiDeathsWailAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { Permanent creature = game.getPermanent(event.getTargetId()); if (creature != null && creature.isCreature(game)) { - getEffects().get(0).setTargetPointer(new FixedTarget(event.getTargetId())); + getEffects().get(0).setTargetPointer(new FixedTarget(event.getTargetId(), game)); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/h/HorobisWhisper.java b/Mage.Sets/src/mage/cards/h/HorobisWhisper.java index d07fca1bd0f..854e92037bc 100644 --- a/Mage.Sets/src/mage/cards/h/HorobisWhisper.java +++ b/Mage.Sets/src/mage/cards/h/HorobisWhisper.java @@ -1,7 +1,5 @@ - package mage.cards.h; -import mage.ObjectColor; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.costs.common.ExileFromGraveCost; import mage.abilities.decorator.ConditionalOneShotEffect; @@ -12,10 +10,8 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.filter.FilterCard; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.filter.common.FilterLandPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ColorPredicate; import mage.target.common.TargetCardInYourGraveyard; import mage.target.common.TargetCreaturePermanent; @@ -27,11 +23,9 @@ import java.util.UUID; */ public final class HorobisWhisper extends CardImpl { - private static final FilterCreaturePermanent filterTarget = new FilterCreaturePermanent("nonblack creature"); private static final FilterLandPermanent filterCondition = new FilterLandPermanent("Swamp"); static { - filterTarget.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); filterCondition.add(SubType.SWAMP.getPredicate()); } @@ -39,16 +33,13 @@ public final class HorobisWhisper extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{B}{B}"); this.subtype.add(SubType.ARCANE); - - // If you control a Swamp, destroy target nonblack creature. this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new DestroyTargetEffect(), new PermanentsOnTheBattlefieldCondition(filterCondition),"If you control a Swamp, destroy target nonblack creature")); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filterTarget) ); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); // Splice onto Arcane-Exile four cards from your graveyard. this.addAbility(new SpliceOntoArcaneAbility(new ExileFromGraveCost(new TargetCardInYourGraveyard(4,4, new FilterCard("cards"))))); - } diff --git a/Mage.Sets/src/mage/cards/h/HostileHostel.java b/Mage.Sets/src/mage/cards/h/HostileHostel.java index 9156b6faebf..b0ceff1be5a 100644 --- a/Mage.Sets/src/mage/cards/h/HostileHostel.java +++ b/Mage.Sets/src/mage/cards/h/HostileHostel.java @@ -6,7 +6,6 @@ import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.TransformSourceEffect; import mage.abilities.keyword.TransformAbility; import mage.abilities.mana.ColorlessManaAbility; import mage.cards.CardImpl; @@ -31,7 +30,6 @@ public final class HostileHostel extends CardImpl { public HostileHostel(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); - this.transformable = true; this.secondSideCardClazz = mage.cards.c.CreepingInn.class; // {T}: Add {C}. @@ -39,11 +37,10 @@ public final class HostileHostel extends CardImpl { // {1}, {T}, Sacrifice a creature: Put a soul counter on Hostile Hostel. Then if there are three or more soul counters on it, remove those counters, transform it, then untap it. Activate only as a sorcery. this.addAbility(new TransformAbility()); - Ability ability = new ActivateAsSorceryActivatedAbility(Zone.BATTLEFIELD, new HostileHostelEffect(), new ManaCostsImpl("{1}")); + Ability ability = new ActivateAsSorceryActivatedAbility(Zone.BATTLEFIELD, new HostileHostelEffect(), new ManaCostsImpl<>("{1}")); ability.addCost(new TapSourceCost()); ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(FILTER_CONTROLLED_CREATURE_SHORT_TEXT))); this.addAbility(ability); - } private HostileHostel(final HostileHostel card) { @@ -82,7 +79,7 @@ class HostileHostelEffect extends OneShotEffect { int counters = permanent.getCounters(game).getCount(CounterType.SOUL); if (counters > 2) { permanent.removeCounters(CounterType.SOUL.getName(), counters, source, game); - new TransformSourceEffect(true).apply(game, source); + permanent.transform(source, game); permanent.untap(game); } return true; diff --git a/Mage.Sets/src/mage/cards/h/HotheadedGiant.java b/Mage.Sets/src/mage/cards/h/HotheadedGiant.java index db25c00639f..3bdedbe92ab 100644 --- a/Mage.Sets/src/mage/cards/h/HotheadedGiant.java +++ b/Mage.Sets/src/mage/cards/h/HotheadedGiant.java @@ -1,14 +1,9 @@ - package mage.cards.h; -import java.util.UUID; import mage.MageInt; -import mage.ObjectColor; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAbility; -import mage.abilities.condition.Condition; -import mage.abilities.condition.InvertCondition; -import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; @@ -17,22 +12,21 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.WatcherScope; import mage.counters.CounterType; -import mage.filter.FilterSpell; -import mage.filter.predicate.mageobject.ColorPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; import mage.game.stack.Spell; import mage.watchers.Watcher; +import java.util.*; + /** - * - * @author jeffwadsworth + * @author TheElk801 */ public final class HotheadedGiant extends CardImpl { public HotheadedGiant(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); this.subtype.add(SubType.GIANT); this.subtype.add(SubType.WARRIOR); @@ -43,9 +37,11 @@ public final class HotheadedGiant extends CardImpl { this.addAbility(HasteAbility.getInstance()); // Hotheaded Giant enters the battlefield with two -1/-1 counters on it unless you've cast another red spell this turn. - Condition condition = new CastRedSpellThisTurnCondition(); - this.addAbility(new EntersBattlefieldAbility(new ConditionalOneShotEffect(new AddCountersSourceEffect(CounterType.M1M1.createInstance(2)), new InvertCondition(condition), ""), "with two -1/-1 counters on it unless you've cast another red spell this turn"), new HotHeadedGiantWatcher(this.getId())); - + this.addAbility(new EntersBattlefieldAbility( + new AddCountersSourceEffect(CounterType.M1M1.createInstance(2)), + HotheadedGiantWatcher::checkSpell, null, + "\"with two -1/-1 counters on it unless you've cast another red spell this turn\"" + ), new HotheadedGiantWatcher()); } private HotheadedGiant(final HotheadedGiant card) { @@ -58,50 +54,39 @@ public final class HotheadedGiant extends CardImpl { } } -class CastRedSpellThisTurnCondition implements Condition { +class HotheadedGiantWatcher extends Watcher { - @Override - public boolean apply(Game game, Ability source) { - HotHeadedGiantWatcher watcher = game.getState().getWatcher(HotHeadedGiantWatcher.class, source.getControllerId()); - if (watcher != null) { - return watcher.conditionMet(); - } - return false; - } -} + private final Map> spellMap = new HashMap<>(); + private static final List emptyList = new ArrayList<>(); -class HotHeadedGiantWatcher extends Watcher { - - private static final FilterSpell filter = new FilterSpell(); - - static { - filter.add(new ColorPredicate(ObjectColor.RED)); - } - - private UUID cardId; - - public HotHeadedGiantWatcher(UUID cardId) { - super(WatcherScope.PLAYER); - this.cardId = cardId; + HotheadedGiantWatcher() { + super(WatcherScope.GAME); } @Override public void watch(GameEvent event, Game game) { - if (condition == true) { //no need to check - condition has already occured + if (event.getType() != EventType.SPELL_CAST) { return; } - if (event.getType() == GameEvent.EventType.SPELL_CAST - && controllerId.equals(event.getPlayerId())) { - Spell spell = game.getStack().getSpell(event.getTargetId()); - if (!spell.getSourceId().equals(cardId) && filter.match(spell, game)) { - condition = true; - } + Spell spell = game.getSpell(event.getSourceId()); + if (spell != null && spell.getColor(game).isRed()) { + spellMap.computeIfAbsent(event.getPlayerId(), x -> new ArrayList<>()) + .add(new MageObjectReference(event.getSourceId(), game)); } } @Override public void reset() { super.reset(); - condition = false; + spellMap.clear(); + } + + static boolean checkSpell(Game game, Ability source) { + return game.getState() + .getWatcher(HotheadedGiantWatcher.class) + .spellMap + .getOrDefault(source.getControllerId(), emptyList) + .stream() + .noneMatch(mor -> !mor.refersTo(source, game)); } } diff --git a/Mage.Sets/src/mage/cards/h/HotshotMechanic.java b/Mage.Sets/src/mage/cards/h/HotshotMechanic.java new file mode 100644 index 00000000000..fed15e6b99a --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HotshotMechanic.java @@ -0,0 +1,37 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.abilities.common.CrewIncreasedPowerAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HotshotMechanic extends CardImpl { + + public HotshotMechanic(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{W}"); + + this.subtype.add(SubType.FOX); + this.subtype.add(SubType.PILOT); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Hotshot Mechanic crews Vehicles as though its power were 2 greater. + this.addAbility(new CrewIncreasedPowerAbility()); + } + + private HotshotMechanic(final HotshotMechanic card) { + super(card); + } + + @Override + public HotshotMechanic copy() { + return new HotshotMechanic(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HoundOfTheFarbogs.java b/Mage.Sets/src/mage/cards/h/HoundOfTheFarbogs.java index 346d4c7c03c..fe24f795529 100644 --- a/Mage.Sets/src/mage/cards/h/HoundOfTheFarbogs.java +++ b/Mage.Sets/src/mage/cards/h/HoundOfTheFarbogs.java @@ -23,8 +23,6 @@ import mage.constants.Zone; */ public final class HoundOfTheFarbogs extends CardImpl { - static final private String RULE = "{this} has menace as long as there are four or more card types among cards in your graveyard"; - public HoundOfTheFarbogs(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}"); this.subtype.add(SubType.ZOMBIE); @@ -33,8 +31,11 @@ public final class HoundOfTheFarbogs extends CardImpl { this.toughness = new MageInt(3); // Delirium — Hound of the Farborgs has menace as long as there are four or more card types among cards in your graveyard. - Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, - new ConditionalContinuousEffect(new GainAbilitySourceEffect(new MenaceAbility(), Duration.WhileOnBattlefield), DeliriumCondition.instance, RULE)); + Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect( + new GainAbilitySourceEffect(new MenaceAbility(), Duration.WhileOnBattlefield), + DeliriumCondition.instance, + "{this} has menace as long as there are four or more card types among cards in your graveyard. " + + "(A creature with menace can't be blocked except by two or more creatures.)")); ability.setAbilityWord(AbilityWord.DELIRIUM); ability.addHint(CardTypesInGraveyardHint.YOU); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/h/HoundTamer.java b/Mage.Sets/src/mage/cards/h/HoundTamer.java index d45833ecbd5..925be3abf19 100644 --- a/Mage.Sets/src/mage/cards/h/HoundTamer.java +++ b/Mage.Sets/src/mage/cards/h/HoundTamer.java @@ -7,7 +7,6 @@ import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.abilities.keyword.DayboundAbility; import mage.abilities.keyword.TrampleAbility; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -29,7 +28,6 @@ public final class HoundTamer extends CardImpl { this.subtype.add(SubType.WEREWOLF); this.power = new MageInt(3); this.toughness = new MageInt(3); - this.transformable = true; this.secondSideCardClazz = mage.cards.u.UntamedPup.class; // Trample @@ -43,7 +41,6 @@ public final class HoundTamer extends CardImpl { this.addAbility(ability); // Daybound - this.addAbility(new TransformAbility()); this.addAbility(new DayboundAbility()); } diff --git a/Mage.Sets/src/mage/cards/h/HowlingMoon.java b/Mage.Sets/src/mage/cards/h/HowlingMoon.java new file mode 100644 index 00000000000..c272a645863 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HowlingMoon.java @@ -0,0 +1,91 @@ +package mage.cards.h; + +import java.util.UUID; + +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BeginningOfCombatTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.token.WolfToken; +import mage.target.common.TargetControlledPermanent; +import mage.watchers.common.CastSpellLastTurnWatcher; + +/** + * + * @author weirddan455 + */ +public final class HowlingMoon extends CardImpl { + + private static final FilterControlledPermanent filter = new FilterControlledPermanent("Wolf or Werewolf you control"); + + static { + filter.add(Predicates.or(SubType.WOLF.getPredicate(), SubType.WEREWOLF.getPredicate())); + } + + public HowlingMoon(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}"); + + // At the beginning of combat on your turn, target Wolf or Werewolf you control gets +2/+2 until end of turn. + Ability ability = new BeginningOfCombatTriggeredAbility( + new BoostTargetEffect(2, 2, Duration.EndOfTurn), + TargetController.YOU, false + ); + ability.addTarget(new TargetControlledPermanent(filter)); + this.addAbility(ability); + + // Whenever an opponent casts their second spell each turn, create a 2/2 green Wolf creature token. + this.addAbility(new HowlingMoonTriggeredAbility(), new CastSpellLastTurnWatcher()); + } + + private HowlingMoon(final HowlingMoon card) { + super(card); + } + + @Override + public HowlingMoon copy() { + return new HowlingMoon(this); + } +} + +class HowlingMoonTriggeredAbility extends TriggeredAbilityImpl { + + public HowlingMoonTriggeredAbility() { + super(Zone.BATTLEFIELD, new CreateTokenEffect(new WolfToken())); + } + + private HowlingMoonTriggeredAbility(final HowlingMoonTriggeredAbility ability) { + super(ability); + } + + @Override + public HowlingMoonTriggeredAbility copy() { + return new HowlingMoonTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.SPELL_CAST; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (game.getOpponents(controllerId).contains(event.getPlayerId())) { + CastSpellLastTurnWatcher watcher = game.getState().getWatcher(CastSpellLastTurnWatcher.class); + return watcher != null && watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(event.getPlayerId()) == 2; + } + return false; + } + + @Override + public String getTriggerPhrase() { + return "Whenever an opponent casts their second spell each turn, "; + } +} diff --git a/Mage.Sets/src/mage/cards/h/HowlpackAlpha.java b/Mage.Sets/src/mage/cards/h/HowlpackAlpha.java index b877500f252..8ac3ceb528c 100644 --- a/Mage.Sets/src/mage/cards/h/HowlpackAlpha.java +++ b/Mage.Sets/src/mage/cards/h/HowlpackAlpha.java @@ -40,7 +40,6 @@ public final class HowlpackAlpha extends CardImpl { // this card is the second face of double-faced card this.nightCard = true; - this.transformable = true; this.color.setGreen(true); this.power = new MageInt(3); diff --git a/Mage.Sets/src/mage/cards/h/HowlpackAvenger.java b/Mage.Sets/src/mage/cards/h/HowlpackAvenger.java new file mode 100644 index 00000000000..047490e4b73 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HowlpackAvenger.java @@ -0,0 +1,101 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.SavedDamageValue; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.keyword.NightboundAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.DamagedPermanentBatchEvent; +import mage.game.events.GameEvent; +import mage.target.common.TargetAnyTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HowlpackAvenger extends CardImpl { + + public HowlpackAvenger(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); + + this.subtype.add(SubType.WEREWOLF); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + this.color.setRed(true); + this.nightCard = true; + + // Whenever a permanent you control is dealt damage, Howlpack Avenger deals that much damage to any target. + this.addAbility(new HowlpackAvengerTriggeredAbility()); + + // {1}{R}: Howlpack Avenger gets +2/+0 until end of turn. + this.addAbility(new SimpleActivatedAbility(new BoostSourceEffect( + 2, 0, Duration.EndOfTurn + ), new ManaCostsImpl<>("{1}{R}"))); + + // Nightbound + this.addAbility(new NightboundAbility()); + } + + private HowlpackAvenger(final HowlpackAvenger card) { + super(card); + } + + @Override + public HowlpackAvenger copy() { + return new HowlpackAvenger(this); + } +} + +class HowlpackAvengerTriggeredAbility extends TriggeredAbilityImpl { + + HowlpackAvengerTriggeredAbility() { + super(Zone.BATTLEFIELD, new DamageTargetEffect(SavedDamageValue.instance)); + this.addTarget(new TargetAnyTarget()); + } + + private HowlpackAvengerTriggeredAbility(final HowlpackAvengerTriggeredAbility ability) { + super(ability); + } + + @Override + public HowlpackAvengerTriggeredAbility copy() { + return new HowlpackAvengerTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT_BATCH; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + DamagedPermanentBatchEvent dEvent = (DamagedPermanentBatchEvent) event; + int damage = dEvent + .getEvents() + .stream() + .filter(damagedEvent -> isControlledBy(game.getControllerId(damagedEvent.getTargetId()))) + .mapToInt(GameEvent::getAmount) + .sum(); + if (damage < 1) { + return false; + } + this.getEffects().setValue("damage", damage); + return false; + } + + @Override + public String getRule() { + return "Whenever a permanent you control is dealt damage, {this} deals that much damage to any target."; + } +} diff --git a/Mage.Sets/src/mage/cards/h/HowlpackOfEstwald.java b/Mage.Sets/src/mage/cards/h/HowlpackOfEstwald.java index ded182d3163..42f61342450 100644 --- a/Mage.Sets/src/mage/cards/h/HowlpackOfEstwald.java +++ b/Mage.Sets/src/mage/cards/h/HowlpackOfEstwald.java @@ -21,7 +21,6 @@ public final class HowlpackOfEstwald extends CardImpl { // this card is the second face of double-faced card this.nightCard = true; - this.transformable = true; this.power = new MageInt(4); this.toughness = new MageInt(6); diff --git a/Mage.Sets/src/mage/cards/h/HowlpackPiper.java b/Mage.Sets/src/mage/cards/h/HowlpackPiper.java new file mode 100644 index 00000000000..e70c753569c --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HowlpackPiper.java @@ -0,0 +1,105 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.common.CantBeCounteredSourceAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.DayboundAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInHand; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HowlpackPiper extends CardImpl { + + public HowlpackPiper(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WEREWOLF); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + this.secondSideCardClazz = mage.cards.w.WildsongHowler.class; + + // This spell can't be countered. + this.addAbility(new CantBeCounteredSourceAbility()); + + // {1}{G}, {T}: You may put a creature card from your hand onto the battlefield. If it's a Wolf or Werewolf, untap Howlpack Piper. Activate only as a sorcery. + Ability ability = new ActivateAsSorceryActivatedAbility( + new HowlpackPiperEffect(), new ManaCostsImpl<>("{1}{G}") + ); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + + // Daybound + this.addAbility(new DayboundAbility()); + } + + private HowlpackPiper(final HowlpackPiper card) { + super(card); + } + + @Override + public HowlpackPiper copy() { + return new HowlpackPiper(this); + } +} + +class HowlpackPiperEffect extends OneShotEffect { + + HowlpackPiperEffect() { + super(Outcome.PutCreatureInPlay); + staticText = "you may put a creature card from your hand onto the battlefield. " + + "If it's a Wolf or Werewolf, untap {this}"; + } + + private HowlpackPiperEffect(final HowlpackPiperEffect effect) { + super(effect); + } + + @Override + public HowlpackPiperEffect copy() { + return new HowlpackPiperEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null || player.getHand().count(StaticFilters.FILTER_CARD_CREATURE, game) < 1) { + return false; + } + TargetCard target = new TargetCardInHand(0, 1, StaticFilters.FILTER_CARD_CREATURE); + player.choose(outcome, player.getHand(), target, game); + Card card = game.getCard(target.getFirstTarget()); + if (card == null) { + return false; + } + player.moveCards(card, Zone.BATTLEFIELD, source, game); + Permanent permanent = game.getPermanent(card.getId()); + Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game); + if (permanent == null || sourcePermanent == null) { + return true; + } + if (permanent.hasSubtype(SubType.WOLF, game) || permanent.hasSubtype(SubType.WEREWOLF, game)) { + sourcePermanent.untap(game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/h/HuatliDinosaurKnight.java b/Mage.Sets/src/mage/cards/h/HuatliDinosaurKnight.java index 6129e2ed0a2..0969ef69d8d 100644 --- a/Mage.Sets/src/mage/cards/h/HuatliDinosaurKnight.java +++ b/Mage.Sets/src/mage/cards/h/HuatliDinosaurKnight.java @@ -2,7 +2,6 @@ package mage.cards.h; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.common.DamageWithPowerFromOneToAnotherTargetEffect; import mage.abilities.effects.common.continuous.BoostControlledEffect; import mage.abilities.effects.common.counter.AddCountersTargetEffect; @@ -36,7 +35,7 @@ public final class HuatliDinosaurKnight extends CardImpl { addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.HUATLI); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +2: Put two +1/+1 counters on up to one target Dinosaur you control. Ability ability = new LoyaltyAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance(2)) diff --git a/Mage.Sets/src/mage/cards/h/HuatliRadiantChampion.java b/Mage.Sets/src/mage/cards/h/HuatliRadiantChampion.java index 8879c3524f9..1e2730baed3 100644 --- a/Mage.Sets/src/mage/cards/h/HuatliRadiantChampion.java +++ b/Mage.Sets/src/mage/cards/h/HuatliRadiantChampion.java @@ -1,7 +1,6 @@ package mage.cards.h; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.common.GetEmblemEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; @@ -30,7 +29,7 @@ public final class HuatliRadiantChampion extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.HUATLI); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +1: Put a loyalty counter on Huatli, Radiant Champion for each creature you control. this.addAbility(new LoyaltyAbility(new AddCountersSourceEffect(CounterType.LOYALTY.createInstance(0), diff --git a/Mage.Sets/src/mage/cards/h/HuatliTheSunsHeart.java b/Mage.Sets/src/mage/cards/h/HuatliTheSunsHeart.java index f6e0b0a5a7f..ec58e07e7ea 100644 --- a/Mage.Sets/src/mage/cards/h/HuatliTheSunsHeart.java +++ b/Mage.Sets/src/mage/cards/h/HuatliTheSunsHeart.java @@ -1,7 +1,6 @@ package mage.cards.h; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.dynamicvalue.common.GreatestToughnessAmongControlledCreaturesValue; import mage.abilities.effects.common.GainLifeEffect; @@ -25,7 +24,7 @@ public final class HuatliTheSunsHeart extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.HUATLI); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(7)); + this.setStartingLoyalty(7); // Each creature you control assigns combat damage equal to its toughness rather than its power. this.addAbility(new SimpleStaticAbility(new CombatDamageByToughnessEffect( diff --git a/Mage.Sets/src/mage/cards/h/HuatliWarriorPoet.java b/Mage.Sets/src/mage/cards/h/HuatliWarriorPoet.java index 9f436f45bd7..3bc125fbce6 100644 --- a/Mage.Sets/src/mage/cards/h/HuatliWarriorPoet.java +++ b/Mage.Sets/src/mage/cards/h/HuatliWarriorPoet.java @@ -3,7 +3,6 @@ package mage.cards.h; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.Mode; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.dynamicvalue.common.GetXLoyaltyValue; import mage.abilities.dynamicvalue.common.GreatestPowerAmongControlledCreaturesValue; import mage.abilities.effects.ContinuousEffect; @@ -34,7 +33,7 @@ public final class HuatliWarriorPoet extends CardImpl { addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.HUATLI); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +2: You gain life equal to the greatest power among creatures you control. this.addAbility(new LoyaltyAbility(new GainLifeEffect( diff --git a/Mage.Sets/src/mage/cards/h/HullbreakerHorror.java b/Mage.Sets/src/mage/cards/h/HullbreakerHorror.java new file mode 100644 index 00000000000..76af62b233e --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HullbreakerHorror.java @@ -0,0 +1,68 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.CantBeCounteredSourceAbility; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.abilities.keyword.FlashAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.FilterSpell; +import mage.target.TargetSpell; +import mage.target.common.TargetNonlandPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HullbreakerHorror extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("spell you don't control"); + + static { + filter.add(TargetController.NOT_YOU.getControllerPredicate()); + } + + public HullbreakerHorror(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{U}{U}"); + + this.subtype.add(SubType.KRAKEN); + this.subtype.add(SubType.HORROR); + this.power = new MageInt(7); + this.toughness = new MageInt(8); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // This spell can't be countered. + this.addAbility(new CantBeCounteredSourceAbility()); + + // Whenever you cast a spell, choose up to one — + // • Return target spell you don't control to its owner's hand. + Ability ability = new SpellCastControllerTriggeredAbility(new ReturnToHandTargetEffect(), false); + ability.addTarget(new TargetSpell(filter)); + ability.getModes().setMinModes(0); + ability.getModes().setMaxModes(1); + + // • Return target nonland permanent to its owner's hand. + Mode mode = new Mode(new ReturnToHandTargetEffect()); + mode.addTarget(new TargetNonlandPermanent()); + ability.addMode(mode); + this.addAbility(ability); + } + + private HullbreakerHorror(final HullbreakerHorror card) { + super(card); + } + + @Override + public HullbreakerHorror copy() { + return new HullbreakerHorror(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HungeringHydra.java b/Mage.Sets/src/mage/cards/h/HungeringHydra.java index 146289b1765..f4b702c6300 100644 --- a/Mage.Sets/src/mage/cards/h/HungeringHydra.java +++ b/Mage.Sets/src/mage/cards/h/HungeringHydra.java @@ -46,7 +46,7 @@ public final class HungeringHydra extends CardImpl { // Whenever damage is dealt to Hungering Hydra, put that many +1/+1 counters on it. this.addAbility(new DealtDamageToSourceTriggeredAbility( new HungeringHydraEffect(), - false, false, true + false, false )); } diff --git a/Mage.Sets/src/mage/cards/h/HungryForMore.java b/Mage.Sets/src/mage/cards/h/HungryForMore.java index 2c91750e367..629847927c4 100644 --- a/Mage.Sets/src/mage/cards/h/HungryForMore.java +++ b/Mage.Sets/src/mage/cards/h/HungryForMore.java @@ -10,9 +10,8 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.TimingRule; import mage.game.Game; -import mage.game.permanent.token.HungryForMoreToken; +import mage.game.permanent.token.HungryForMoreVampireToken; import mage.game.permanent.token.Token; import mage.target.targetpointer.FixedTargets; @@ -63,7 +62,7 @@ class HungryForMoreEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Token token = new HungryForMoreToken(); + Token token = new HungryForMoreVampireToken(); token.putOntoBattlefield(1, game, source, source.getControllerId()); game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility( new SacrificeTargetEffect().setTargetPointer(new FixedTargets( diff --git a/Mage.Sets/src/mage/cards/h/HungryRidgewolf.java b/Mage.Sets/src/mage/cards/h/HungryRidgewolf.java new file mode 100644 index 00000000000..624cef1d25e --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HungryRidgewolf.java @@ -0,0 +1,72 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.AnotherPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HungryRidgewolf extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent(); + + static { + filter.add(AnotherPredicate.instance); + filter.add(Predicates.or( + SubType.WOLF.getPredicate(), + SubType.WEREWOLF.getPredicate() + )); + } + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter); + private static final Hint hint = new ConditionHint(condition, "You control another Wolf or Werewolf"); + + public HungryRidgewolf(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); + + this.subtype.add(SubType.WOLF); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // As long as you control another Wolf or Werewolf, Hungry Ridgewolf gets +1/+0 and has trample. + Ability ability = new SimpleStaticAbility(new ConditionalContinuousEffect( + new BoostSourceEffect(1, 0, Duration.WhileOnBattlefield), + condition, "as long as you control another Wolf or Werewolf, {this} gets +1/+0" + )); + ability.addEffect(new ConditionalContinuousEffect( + new GainAbilitySourceEffect( + TrampleAbility.getInstance(), Duration.WhileOnBattlefield + ), condition, "and has trample" + )); + this.addAbility(ability.addHint(hint)); + } + + private HungryRidgewolf(final HungryRidgewolf card) { + super(card); + } + + @Override + public HungryRidgewolf copy() { + return new HungryRidgewolf(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HuntTheWeak.java b/Mage.Sets/src/mage/cards/h/HuntTheWeak.java index bde6c3afb0b..7243ab70a31 100644 --- a/Mage.Sets/src/mage/cards/h/HuntTheWeak.java +++ b/Mage.Sets/src/mage/cards/h/HuntTheWeak.java @@ -23,7 +23,8 @@ public final class HuntTheWeak extends CardImpl { // Put a +1/+1 counter on target creature you control. Then that creature fights target creature you don't control. this.getSpellAbility().addEffect(new AddCountersTargetEffect(CounterType.P1P1.createInstance())); this.getSpellAbility().addEffect(new FightTargetsEffect().setText( - "Then that creature fights up to one target creature you don't control" + "Then that creature fights target creature you don't control. " + + "(Each deals damage equal to its power to the other.)" )); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); diff --git a/Mage.Sets/src/mage/cards/h/HuntedNightmare.java b/Mage.Sets/src/mage/cards/h/HuntedNightmare.java index 01ffe1cb33a..ab2e8d9a12f 100644 --- a/Mage.Sets/src/mage/cards/h/HuntedNightmare.java +++ b/Mage.Sets/src/mage/cards/h/HuntedNightmare.java @@ -34,7 +34,7 @@ public final class HuntedNightmare extends CardImpl { this.toughness = new MageInt(5); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // When Hunted Nightmare enters the battlefield, target opponent puts a deathtouch counter on a creature they control. Ability ability = new EntersBattlefieldTriggeredAbility(new HuntedNightmareEffect()); diff --git a/Mage.Sets/src/mage/cards/h/HunterSliver.java b/Mage.Sets/src/mage/cards/h/HunterSliver.java index e271ba88afe..34243ca8a0b 100644 --- a/Mage.Sets/src/mage/cards/h/HunterSliver.java +++ b/Mage.Sets/src/mage/cards/h/HunterSliver.java @@ -1,7 +1,5 @@ - package mage.cards.h; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.continuous.GainAbilityAllEffect; @@ -9,13 +7,13 @@ import mage.abilities.keyword.ProvokeAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.SubType; import mage.filter.StaticFilters; +import java.util.UUID; + /** - * * @author cbt33 */ public final class HunterSliver extends CardImpl { @@ -28,8 +26,10 @@ public final class HunterSliver extends CardImpl { this.toughness = new MageInt(1); // All Sliver creatures have provoke. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(new ProvokeAbility(), - Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS))); + this.addAbility(new SimpleStaticAbility(new GainAbilityAllEffect( + new ProvokeAbility(), Duration.WhileOnBattlefield, + StaticFilters.FILTER_PERMANENT_ALL_SLIVERS + ).setText("all Sliver creatures have provoke"))); } private HunterSliver(final HunterSliver card) { diff --git a/Mage.Sets/src/mage/cards/h/HuntmasterLiger.java b/Mage.Sets/src/mage/cards/h/HuntmasterLiger.java index 3524529dd90..14dd6f51a18 100644 --- a/Mage.Sets/src/mage/cards/h/HuntmasterLiger.java +++ b/Mage.Sets/src/mage/cards/h/HuntmasterLiger.java @@ -32,7 +32,7 @@ public final class HuntmasterLiger extends CardImpl { // Whenever this creature mutates, other creatures you control get +X/+X until end of turn, where X is the number of times this creature has mutated. this.addAbility(new MutatesSourceTriggeredAbility(new BoostControlledEffect( SourceMutatedCount.instance, SourceMutatedCount.instance, Duration.EndOfTurn, - StaticFilters.FILTER_PERMANENT_CREATURE, true, true + StaticFilters.FILTER_PERMANENT_CREATURES, true, true ))); } diff --git a/Mage.Sets/src/mage/cards/h/HuntmasterOfTheFells.java b/Mage.Sets/src/mage/cards/h/HuntmasterOfTheFells.java index a79558e8ae4..5994fd49068 100644 --- a/Mage.Sets/src/mage/cards/h/HuntmasterOfTheFells.java +++ b/Mage.Sets/src/mage/cards/h/HuntmasterOfTheFells.java @@ -1,7 +1,8 @@ package mage.cards.h; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.Ability; +import mage.abilities.common.TransformsOrEntersTriggeredAbility; import mage.abilities.common.WerewolfFrontTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.GainLifeEffect; @@ -10,10 +11,6 @@ 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.GameEvent; -import mage.game.permanent.Permanent; import mage.game.permanent.token.WolfToken; import java.util.UUID; @@ -28,14 +25,15 @@ public final class HuntmasterOfTheFells extends CardImpl { this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.WEREWOLF); - this.transformable = true; this.secondSideCardClazz = mage.cards.r.RavagerOfTheFells.class; this.power = new MageInt(2); this.toughness = new MageInt(2); // Whenever this creature enters the battlefield or transforms into Huntmaster of the Fells, create a 2/2 green Wolf creature token and you gain 2 life. - this.addAbility(new HuntmasterOfTheFellsAbility()); + Ability ability = new TransformsOrEntersTriggeredAbility(new CreateTokenEffect(new WolfToken()), false); + ability.addEffect(new GainLifeEffect(2).concatBy("and")); + this.addAbility(ability); // At the beginning of each upkeep, if no spells were cast last turn, transform Huntmaster of the Fells. this.addAbility(new TransformAbility()); @@ -51,44 +49,3 @@ public final class HuntmasterOfTheFells extends CardImpl { return new HuntmasterOfTheFells(this); } } - -class HuntmasterOfTheFellsAbility extends TriggeredAbilityImpl { - - public HuntmasterOfTheFellsAbility() { - super(Zone.BATTLEFIELD, new CreateTokenEffect(new WolfToken()), false); - this.addEffect(new GainLifeEffect(2)); - } - - public HuntmasterOfTheFellsAbility(final HuntmasterOfTheFellsAbility ability) { - super(ability); - } - - @Override - public HuntmasterOfTheFellsAbility copy() { - return new HuntmasterOfTheFellsAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TRANSFORMED || event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.TRANSFORMED && event.getTargetId().equals(this.getSourceId())) { - Permanent permanent = game.getPermanent(sourceId); - if (permanent != null && !permanent.isTransformed()) { - return true; - } - } - if (event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD && event.getTargetId().equals(this.getSourceId())) { - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever this creature enters the battlefield or transforms into {this}, create a 2/2 green Wolf creature token and you gain 2 life."; - } -} diff --git a/Mage.Sets/src/mage/cards/h/Hydrolash.java b/Mage.Sets/src/mage/cards/h/Hydrolash.java index 7c68a62405e..22c02f31081 100644 --- a/Mage.Sets/src/mage/cards/h/Hydrolash.java +++ b/Mage.Sets/src/mage/cards/h/Hydrolash.java @@ -8,7 +8,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.common.FilterAttackingCreature; +import mage.filter.StaticFilters; /** * @@ -20,9 +20,9 @@ public final class Hydrolash extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{U}"); // Attacking creatures get -2/-0 until end of turn. - this.getSpellAbility().addEffect(new BoostAllEffect(-2, 0, Duration.EndOfTurn, new FilterAttackingCreature("Attacking creatures"), false)); + this.getSpellAbility().addEffect(new BoostAllEffect(-2, 0, Duration.EndOfTurn, StaticFilters.FILTER_ATTACKING_CREATURES, false)); // Draw a card. - this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("
")); } private Hydrolash(final Hydrolash card) { diff --git a/Mage.Sets/src/mage/cards/h/Hypnox.java b/Mage.Sets/src/mage/cards/h/Hypnox.java index 70d7067d384..62c1d3c64c8 100644 --- a/Mage.Sets/src/mage/cards/h/Hypnox.java +++ b/Mage.Sets/src/mage/cards/h/Hypnox.java @@ -16,7 +16,6 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.game.ExileZone; import mage.game.Game; -import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetOpponent; import mage.util.CardUtil; @@ -77,13 +76,12 @@ class HypnoxExileEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); Player player = game.getPlayer(source.getFirstTarget()); - Permanent permanent = source.getSourcePermanentOrLKI(game); - if (controller == null || player == null || permanent == null) { + if (controller == null || player == null) { return false; } return controller.moveCardsToExile( player.getHand().getCards(game), source, game, true, - CardUtil.getExileZoneId(game, source), permanent.getIdName() + CardUtil.getExileZoneId(game, source), CardUtil.getSourceName(game, source) ); } diff --git a/Mage.Sets/src/mage/cards/i/IcatianSkirmishers.java b/Mage.Sets/src/mage/cards/i/IcatianSkirmishers.java index 7c665521d29..6c55c5151b6 100644 --- a/Mage.Sets/src/mage/cards/i/IcatianSkirmishers.java +++ b/Mage.Sets/src/mage/cards/i/IcatianSkirmishers.java @@ -1,5 +1,3 @@ - - package mage.cards.i; import java.util.UUID; @@ -26,8 +24,8 @@ import mage.target.targetpointer.FixedTarget; */ public final class IcatianSkirmishers extends CardImpl { - public IcatianSkirmishers (UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{W}"); + public IcatianSkirmishers(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.SOLDIER); this.power = new MageInt(1); @@ -38,12 +36,12 @@ public final class IcatianSkirmishers extends CardImpl { // Banding this.addAbility(BandingAbility.getInstance()); - + // Whenever Icatian Skirmishers attacks, all creatures banded with it gain first strike until end of turn. this.addAbility(new AttacksTriggeredAbility(new IcatianSkirmishersEffect(), false)); } - public IcatianSkirmishers (final IcatianSkirmishers card) { + public IcatianSkirmishers(final IcatianSkirmishers card) { super(card); } @@ -76,9 +74,11 @@ class IcatianSkirmishersEffect extends OneShotEffect { if (sourcePermanent != null) { for (UUID bandedId : sourcePermanent.getBandedCards()) { Permanent banded = game.getPermanent(bandedId); - if (banded != null && banded.getBandedCards() != null && banded.getBandedCards().contains(sourcePermanent.getId())) { + if (banded != null + && banded.getBandedCards() != null + && banded.getBandedCards().contains(sourcePermanent.getId())) { GainAbilityTargetEffect effect = new GainAbilityTargetEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(bandedId)); + effect.setTargetPointer(new FixedTarget(bandedId, game)); game.addEffect(effect, source); } } diff --git a/Mage.Sets/src/mage/cards/i/IcatianStore.java b/Mage.Sets/src/mage/cards/i/IcatianStore.java index 0343f85dff6..2c162d5f97d 100644 --- a/Mage.Sets/src/mage/cards/i/IcatianStore.java +++ b/Mage.Sets/src/mage/cards/i/IcatianStore.java @@ -39,7 +39,7 @@ public final class IcatianStore extends CardImpl { this.addAbility(new SkipUntapOptionalAbility()); // At the beginning of your upkeep, if Icatian Store is tapped, put a storage counter on it. OneShotEffect addStorageCounter = new AddCountersSourceEffect(CounterType.STORAGE.createInstance()); - Effect effect = new ConditionalOneShotEffect(addStorageCounter, SourceTappedCondition.instance, "if {this} is tapped, put a storage counter on it"); + Effect effect = new ConditionalOneShotEffect(addStorageCounter, SourceTappedCondition.TAPPED, "if {this} is tapped, put a storage counter on it"); this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, effect, TargetController.YOU, false)); // {tap}, Remove any number of storage counters from Icatian Store: Add {W} for each storage counter removed this way. Ability ability = new DynamicManaAbility( diff --git a/Mage.Sets/src/mage/cards/i/IceCage.java b/Mage.Sets/src/mage/cards/i/IceCage.java index 99f5ca6d7d8..2de7423b090 100644 --- a/Mage.Sets/src/mage/cards/i/IceCage.java +++ b/Mage.Sets/src/mage/cards/i/IceCage.java @@ -1,9 +1,6 @@ - - package mage.cards.i; import java.util.UUID; -import mage.abilities.Ability; import mage.abilities.common.BecomesTargetAttachedTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.AttachEffect; @@ -27,15 +24,13 @@ public final class IceCage extends CardImpl { public IceCage(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{U}"); - this.subtype.add(SubType.AURA); // Enchant creature TargetPermanent auraTarget = new TargetCreaturePermanent(); this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.Detriment)); - Ability ability = new EnchantAbility(auraTarget.getTargetName()); - this.addAbility(ability); + this.addAbility(new EnchantAbility(auraTarget.getTargetName())); // Enchanted creature can't attack or block, and its activated abilities can't be activated. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantBlockAttackActivateAttachedEffect())); diff --git a/Mage.Sets/src/mage/cards/i/IceCauldron.java b/Mage.Sets/src/mage/cards/i/IceCauldron.java index 5e9458f44fc..30f5a15f066 100644 --- a/Mage.Sets/src/mage/cards/i/IceCauldron.java +++ b/Mage.Sets/src/mage/cards/i/IceCauldron.java @@ -104,7 +104,7 @@ class IceCauldronExileEffect extends OneShotEffect { if (chosenCard != null) { controller.moveCardToExileWithInfo(chosenCard, source.getSourceId(), sourcePermanent.getIdName(), source, game, Zone.HAND, true); AsThoughEffect effect = new IceCauldronCastFromExileEffect(); - effect.setTargetPointer(new FixedTarget(chosenCard.getId())); + effect.setTargetPointer(new FixedTarget(chosenCard.getId(), game)); game.addEffect(effect, source); game.getState().setValue("IceCauldronCard" + source.getSourceId().toString(), new MageObjectReference(chosenCard.getId(), game)); //store the exiled card return true; diff --git a/Mage.Sets/src/mage/cards/i/IcefallRegent.java b/Mage.Sets/src/mage/cards/i/IcefallRegent.java index d91ba430650..4454729a6f6 100644 --- a/Mage.Sets/src/mage/cards/i/IcefallRegent.java +++ b/Mage.Sets/src/mage/cards/i/IcefallRegent.java @@ -4,22 +4,19 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; +import mage.abilities.effects.common.DontUntapInControllersUntapStepTargetEffect; import mage.abilities.effects.common.TapTargetEffect; import mage.abilities.effects.common.cost.SpellsCostModificationThatTargetSourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.TargetController; import mage.filter.FilterCard; -import mage.filter.common.FilterCreaturePermanent; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.ZoneChangeEvent; -import mage.game.permanent.Permanent; -import mage.target.Target; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; -import mage.watchers.Watcher; import java.util.UUID; @@ -28,12 +25,6 @@ import java.util.UUID; */ public final class IcefallRegent extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public IcefallRegent(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{U}"); this.subtype.add(SubType.DRAGON); @@ -43,17 +34,17 @@ public final class IcefallRegent extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); - // When Icefall Regent enters the battlefield, tap target creature an opponent controls. That creature doesn't untap during its controller's untap step for as long as you control Icefall Regent. + // When Icefall Regent enters the battlefield, tap target creature an opponent controls. + // That creature doesn't untap during its controller's untap step for as long as you control Icefall Regent. Ability ability = new EntersBattlefieldTriggeredAbility(new TapTargetEffect(), false); - ability.addEffect(new IcefallRegentEffect()); - Target target = new TargetCreaturePermanent(filter); - ability.addTarget(target); - this.addAbility(ability, new IcefallRegentWatcher()); + ability.addEffect(new DontUntapInControllersUntapStepTargetEffect(Duration.WhileControlled)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); + this.addAbility(ability); // Spells your opponents cast that target Icefall Regent cost {2} more to cast. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, - new SpellsCostModificationThatTargetSourceEffect(2, new FilterCard("Spells"), TargetController.OPPONENT)) - ); + this.addAbility(new SimpleStaticAbility(new SpellsCostModificationThatTargetSourceEffect( + 2, new FilterCard("Spells"), TargetController.OPPONENT + ))); } private IcefallRegent(final IcefallRegent card) { @@ -65,96 +56,3 @@ public final class IcefallRegent extends CardImpl { return new IcefallRegent(this); } } - -class IcefallRegentEffect extends ContinuousRuleModifyingEffectImpl { - - public IcefallRegentEffect() { - super(Duration.Custom, Outcome.Detriment, false, false); - this.staticText = "That creature doesn't untap during its controller's untap step for as long as you control {this}"; - } - - public IcefallRegentEffect(final IcefallRegentEffect effect) { - super(effect); - } - - @Override - public IcefallRegentEffect copy() { - return new IcefallRegentEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - return false; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.LOST_CONTROL - || event.getType() == GameEvent.EventType.ZONE_CHANGE - || event.getType() == GameEvent.EventType.UNTAP; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - // Source must be on the battlefield (it's neccessary to check here because if as response to the enter - // the battlefield triggered ability the source dies (or will be exiled), then the ZONE_CHANGE or LOST_CONTROL - // event will happen before this effect is applied ever) - Permanent sourcePermanent = game.getPermanent(source.getSourceId()); - if (sourcePermanent == null || !sourcePermanent.isControlledBy(source.getControllerId())) { - discard(); - return false; - } - if (event.getType() == GameEvent.EventType.LOST_CONTROL) { - if (event.getTargetId().equals(source.getSourceId())) { - discard(); - return false; - } - } - if (event.getType() == GameEvent.EventType.ZONE_CHANGE && event.getTargetId().equals(source.getSourceId())) { - ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - if (zEvent.getFromZone() == Zone.BATTLEFIELD) { - discard(); - return false; - } - } - - if (game.getTurn().getStepType() == PhaseStep.UNTAP && event.getType() == GameEvent.EventType.UNTAP) { - if (event.getTargetId().equals(targetPointer.getFirst(game, source))) { - Permanent targetCreature = game.getPermanent(targetPointer.getFirst(game, source)); - return targetCreature != null && game.isActivePlayer(targetCreature.getControllerId()); - } - } - - return false; - } -} - -class IcefallRegentWatcher extends Watcher { - - IcefallRegentWatcher() { - super(WatcherScope.CARD); - } - - @Override - public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.LOST_CONTROL - && event.getPlayerId().equals(controllerId) - && event.getTargetId().equals(sourceId)) { - condition = true; - game.replaceEvent(event); - return; - } - if (event.getType() == GameEvent.EventType.ZONE_CHANGE && event.getTargetId().equals(sourceId)) { - ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - if (zEvent.getFromZone() == Zone.BATTLEFIELD) { - condition = true; - game.replaceEvent(event); - } - } - } - - @Override - public void reset() { - //don't reset condition each turn - only when this leaves the battlefield - } -} diff --git a/Mage.Sets/src/mage/cards/i/IchorExplosion.java b/Mage.Sets/src/mage/cards/i/IchorExplosion.java index df561f4c2b3..d55feef1339 100644 --- a/Mage.Sets/src/mage/cards/i/IchorExplosion.java +++ b/Mage.Sets/src/mage/cards/i/IchorExplosion.java @@ -15,10 +15,9 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Zone; import mage.filter.StaticFilters; -import static mage.filter.StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetControlledPermanent; /** * @@ -30,10 +29,10 @@ public final class IchorExplosion extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{5}{B}{B}"); // As an additional cost to cast Ichor Explosion, sacrifice a creature. - this.getSpellAbility().addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(FILTER_CONTROLLED_CREATURE_SHORT_TEXT))); + this.getSpellAbility().addCost(new SacrificeTargetCost(new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT))); // All creatures get -X/-X until end of turn, where X is the sacrificed creature's power. DynamicValue xValue = new IchorExplosionDynamicValue(); - this.getSpellAbility().addEffect(new BoostAllEffect(xValue, xValue, Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURE, false, null, true)); + this.getSpellAbility().addEffect(new BoostAllEffect(xValue, xValue, Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_ALL_CREATURES, false, null, true)); } diff --git a/Mage.Sets/src/mage/cards/i/Ichorid.java b/Mage.Sets/src/mage/cards/i/Ichorid.java index f05c7e7f4b3..da61c6ff2f7 100644 --- a/Mage.Sets/src/mage/cards/i/Ichorid.java +++ b/Mage.Sets/src/mage/cards/i/Ichorid.java @@ -1,10 +1,8 @@ - package mage.cards.i; -import java.util.UUID; import mage.MageInt; +import mage.MageObject; import mage.ObjectColor; -import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.BeginningOfYourEndStepTriggeredAbility; import mage.abilities.costs.common.ExileFromGraveCost; @@ -20,21 +18,20 @@ import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardIdPredicate; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.mageobject.ColorPredicate; import mage.game.Game; -import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; +import java.util.UUID; + /** - * * @author Plopman */ public final class Ichorid extends CardImpl { public Ichorid(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); this.subtype.add(SubType.HORROR); this.power = new MageInt(3); @@ -42,15 +39,12 @@ public final class Ichorid extends CardImpl { // Haste this.addAbility(HasteAbility.getInstance()); + // At the beginning of the end step, sacrifice Ichorid. this.addAbility(new BeginningOfYourEndStepTriggeredAbility(new SacrificeSourceEffect(), false)); + // At the beginning of your upkeep, if Ichorid is in your graveyard, you may exile a black creature card other than Ichorid from your graveyard. If you do, return Ichorid to the battlefield. - FilterCard filter = new FilterCreatureCard("a black creature card other than Ichorid from your graveyard"); - filter.add(Predicates.not(new CardIdPredicate(this.getId()))); - filter.add(new ColorPredicate(ObjectColor.BLACK)); - Ability ability = new IchoridTriggerdAbility(filter); - this.addAbility(ability); - + this.addAbility(new IchoridTriggerdAbility()); } private Ichorid(final Ichorid card) { @@ -63,16 +57,20 @@ public final class Ichorid extends CardImpl { } } -class IchoridTriggerdAbility extends BeginningOfUpkeepTriggeredAbility{ +class IchoridTriggerdAbility extends BeginningOfUpkeepTriggeredAbility { - public IchoridTriggerdAbility(FilterCard filter){ - super(Zone.GRAVEYARD, - new DoIfCostPaid(new ReturnSourceFromGraveyardToBattlefieldEffect(), - new ExileFromGraveCost( - new TargetCardInYourGraveyard(1, 1, filter, true) - ) - ), - TargetController.YOU, false); + private static final FilterCard filter = new FilterCreatureCard("another black creature card"); + + static { + filter.add(AnotherPredicate.instance); + filter.add(new ColorPredicate(ObjectColor.BLACK)); + } + + public IchoridTriggerdAbility() { + super(Zone.GRAVEYARD, new DoIfCostPaid( + new ReturnSourceFromGraveyardToBattlefieldEffect(), + new ExileFromGraveCost(new TargetCardInYourGraveyard(filter)) + ), TargetController.YOU, false); } public IchoridTriggerdAbility(IchoridTriggerdAbility ability) { @@ -83,19 +81,17 @@ class IchoridTriggerdAbility extends BeginningOfUpkeepTriggeredAbility{ public BeginningOfUpkeepTriggeredAbility copy() { return new IchoridTriggerdAbility(this); } - + @Override public boolean checkInterveningIfClause(Game game) { - Player controller = game.getPlayer(controllerId); - if(controller != null && controller.getGraveyard().contains(sourceId)){ - return super.checkInterveningIfClause(game); - } - return false; + MageObject sourceObject = getSourceObjectIfItStillExists(game); + return sourceObject != null && game.getState().getZone(getSourceId()) == Zone.GRAVEYARD; } @Override public String getRule() { - return "At the beginning of your upkeep, if {this} is in your graveyard, you may exile a black creature card other than {this} from your graveyard. If you do, return {this} to the battlefield."; + return "At the beginning of your upkeep, if {this} is in your graveyard, " + + "you may exile a black creature card other than {this} from your graveyard. " + + "If you do, return {this} to the battlefield."; } - } diff --git a/Mage.Sets/src/mage/cards/i/Ichthyomorphosis.java b/Mage.Sets/src/mage/cards/i/Ichthyomorphosis.java index d0411d87da8..37493941782 100644 --- a/Mage.Sets/src/mage/cards/i/Ichthyomorphosis.java +++ b/Mage.Sets/src/mage/cards/i/Ichthyomorphosis.java @@ -38,7 +38,7 @@ public final class Ichthyomorphosis extends CardImpl { // Enchanted creature loses all abilities and is a blue Fish with base power and toughness 0/1. Effect effect = new BecomesCreatureAttachedEffect( new CreatureToken(0, 1, "", SubType.FISH).withColor("U"), - "Enchanted creature loses all abilities and is a blue Fish creature with base power and toughness 0/1", + "Enchanted creature loses all abilities and is a blue Fish with base power and toughness 0/1", Duration.WhileOnBattlefield, BecomesCreatureAttachedEffect.LoseType.ALL ); effect.setOutcome(Outcome.Detriment); diff --git a/Mage.Sets/src/mage/cards/i/IdentityThief.java b/Mage.Sets/src/mage/cards/i/IdentityThief.java index 54064b16fe4..088dd54da05 100644 --- a/Mage.Sets/src/mage/cards/i/IdentityThief.java +++ b/Mage.Sets/src/mage/cards/i/IdentityThief.java @@ -82,7 +82,7 @@ class IdentityThiefAbility extends TriggeredAbilityImpl { @Override public String getTriggerPhrase() { - return "Whenever {this} attacks, " ; + return "Whenever {this} attacks, "; } @Override @@ -112,7 +112,7 @@ class IdentityThiefEffect extends OneShotEffect { && targetPermanent != null && sourcePermanent != null) { ContinuousEffect copyEffect = new CopyEffect(Duration.EndOfTurn, targetPermanent, source.getSourceId()); - copyEffect.setTargetPointer(new FixedTarget(sourcePermanent.getId())); + copyEffect.setTargetPointer(new FixedTarget(sourcePermanent.getId(), game)); game.addEffect(copyEffect, source); UUID exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); if (controller.moveCardsToExile(targetPermanent, source, game, true, exileZoneId, sourcePermanent.getName())) { diff --git a/Mage.Sets/src/mage/cards/i/IdolOfEndurance.java b/Mage.Sets/src/mage/cards/i/IdolOfEndurance.java index e23c609a88a..6a38e31fe36 100644 --- a/Mage.Sets/src/mage/cards/i/IdolOfEndurance.java +++ b/Mage.Sets/src/mage/cards/i/IdolOfEndurance.java @@ -14,15 +14,18 @@ import mage.constants.*; import mage.filter.FilterCard; import mage.filter.common.FilterCreatureCard; import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.game.ExileZone; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; import mage.players.Player; +import mage.util.CardUtil; import mage.watchers.Watcher; -import java.util.*; -import java.util.stream.Collectors; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; /** * @author TheElk801 @@ -83,28 +86,16 @@ class IdolOfEnduranceExileEffect extends OneShotEffect { return false; } Cards cards = new CardsImpl(player.getGraveyard().getCards(filter, game)); - MageObjectReference mor = new MageObjectReference(permanent, game); - player.moveCards(cards, Zone.EXILED, source, game); - Set morSet = cards - .getCards(game) - .stream() - .filter(Objects::nonNull) - .map(card -> new MageObjectReference(card, game)) - .collect(Collectors.toSet()); - String exileId = "idolOfEndurance_" + mor.getSourceId() + mor.getZoneChangeCounter(); - if (game.getState().getValue(exileId) == null) { - game.getState().setValue(exileId, new HashSet()); - } - ((Set) game.getState().getValue(exileId)).addAll(morSet); - game.addDelayedTriggeredAbility(new IdolOfEnduranceDelayedTrigger(exileId), source); + player.moveCardsToExile(cards.getCards(game), source, game, true, CardUtil.getExileZoneId(game, source), CardUtil.getSourceName(game, source)); + game.addDelayedTriggeredAbility(new IdolOfEnduranceDelayedTrigger(), source); return true; } } class IdolOfEnduranceDelayedTrigger extends DelayedTriggeredAbility { - IdolOfEnduranceDelayedTrigger(String exileId) { - super(new IdolOfEnduranceLeaveEffect(exileId), Duration.Custom, true, false); + IdolOfEnduranceDelayedTrigger() { + super(new IdolOfEnduranceLeaveEffect(), Duration.Custom, true, false); this.usesStack = false; this.setRuleVisible(false); } @@ -137,16 +128,12 @@ class IdolOfEnduranceDelayedTrigger extends DelayedTriggeredAbility { class IdolOfEnduranceLeaveEffect extends OneShotEffect { - private final String exileId; - - IdolOfEnduranceLeaveEffect(String exileId) { + IdolOfEnduranceLeaveEffect() { super(Outcome.Benefit); - this.exileId = exileId; } private IdolOfEnduranceLeaveEffect(final IdolOfEnduranceLeaveEffect effect) { super(effect); - this.exileId = effect.exileId; } @Override @@ -160,17 +147,11 @@ class IdolOfEnduranceLeaveEffect extends OneShotEffect { if (player == null) { return false; } - Object object = game.getState().getValue(exileId); - if (!(object instanceof Set)) { - return false; - } - Set morSet = (Set) object; - return player != null && player.moveCards( - morSet.stream() - .map(mor -> mor.getCard(game)) - .filter(Objects::nonNull) - .collect(Collectors.toSet()), - Zone.GRAVEYARD, source, game + ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source)); + return exileZone != null + && !exileZone.isEmpty() + && player.moveCards( + exileZone, Zone.GRAVEYARD, source, game ); } } @@ -199,28 +180,16 @@ class IdolOfEnduranceCastFromExileEffect extends AsThoughEffectImpl { @Override public void init(Ability source, Game game) { super.init(source, game); - IdolOfEnduranceWatcher watcher = game.getState().getWatcher(IdolOfEnduranceWatcher.class); - if (watcher != null) { - watcher.addPlayable(source, game); - } + IdolOfEnduranceWatcher.addPlayable(source, game); } @Override public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { - IdolOfEnduranceWatcher watcher = game.getState().getWatcher(IdolOfEnduranceWatcher.class); - if (watcher == null || !watcher.checkPermission(affectedControllerId, source, game)) { + if (!IdolOfEnduranceWatcher.checkPermission(affectedControllerId, source, game)) { return false; } - Object value = game.getState().getValue( - "idolOfEndurance_" + source.getSourceId() + source.getSourceObjectZoneChangeCounter() - ); - if (!(value instanceof Set)) { - discard(); - return false; - } - Set morSet = (Set) value; - if (game.getState().getZone(sourceId) != Zone.EXILED - || morSet.stream().noneMatch(mor -> mor.refersTo(sourceId, game))) { + ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source)); + if (exileZone == null || !exileZone.contains(sourceId)) { return false; } Card card = game.getCard(sourceId); @@ -247,7 +216,6 @@ class IdolOfEnduranceWatcher extends Watcher { } morMap.computeIfAbsent(event.getAdditionalReference().getApprovingMageObjectReference(), m -> new HashMap<>()) .compute(event.getPlayerId(), (u, i) -> i == null ? 0 : Integer.sum(i, -1)); - return; } } @@ -257,24 +225,27 @@ class IdolOfEnduranceWatcher extends Watcher { super.reset(); } - boolean checkPermission(UUID playerId, Ability source, Game game) { + static boolean checkPermission(UUID playerId, Ability source, Game game) { if (!playerId.equals(source.getControllerId())) { return false; } - MageObjectReference mor = new MageObjectReference( - source.getSourceId(), source.getSourceObjectZoneChangeCounter(), game - ); - if (!morMap.containsKey(mor)) { - return false; - } - return morMap.get(mor).getOrDefault(playerId, 0) > 0; + IdolOfEnduranceWatcher watcher = game.getState().getWatcher(IdolOfEnduranceWatcher.class); + MageObjectReference mor = new MageObjectReference(source); + return watcher + .morMap + .containsKey(mor) + && watcher + .morMap + .get(mor) + .getOrDefault(playerId, 0) > 0; } - void addPlayable(Ability source, Game game) { - MageObjectReference mor = new MageObjectReference( - source.getSourceId(), source.getSourceObjectZoneChangeCounter(), game - ); - morMap.computeIfAbsent(mor, m -> new HashMap<>()) - .compute(source.getControllerId(), (u, i) -> i == null ? 1 : Integer.sum(i, 1)); + static void addPlayable(Ability source, Game game) { + MageObjectReference mor = new MageObjectReference(source); + game.getState() + .getWatcher(IdolOfEnduranceWatcher.class) + .morMap + .computeIfAbsent(mor, m -> new HashMap<>()) + .compute(source.getControllerId(), CardUtil::setOrIncrementValue); } } diff --git a/Mage.Sets/src/mage/cards/i/IgniteDisorder.java b/Mage.Sets/src/mage/cards/i/IgniteDisorder.java index 7a15ec028a3..f28100eb6f9 100644 --- a/Mage.Sets/src/mage/cards/i/IgniteDisorder.java +++ b/Mage.Sets/src/mage/cards/i/IgniteDisorder.java @@ -1,4 +1,3 @@ - package mage.cards.i; import java.util.UUID; @@ -29,9 +28,8 @@ public final class IgniteDisorder extends CardImpl { public IgniteDisorder(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{R}"); - // Ignite Disorder deals 3 damage divided as you choose among one, two, or three target white and/or blue creatures. - this.getSpellAbility().addEffect(new DamageMultiEffect(3).setText("{this} deals 3 damage divided as you choose among one, two, or three target white and/or blue creatures")); + this.getSpellAbility().addEffect(new DamageMultiEffect(3)); this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount(3, filter)); } diff --git a/Mage.Sets/src/mage/cards/i/IgnorantBliss.java b/Mage.Sets/src/mage/cards/i/IgnorantBliss.java index 6f22f86c864..97a503a56e4 100644 --- a/Mage.Sets/src/mage/cards/i/IgnorantBliss.java +++ b/Mage.Sets/src/mage/cards/i/IgnorantBliss.java @@ -1,6 +1,5 @@ package mage.cards.i; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; @@ -63,12 +62,11 @@ class IgnorantBlissEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = source.getSourceObject(game); - if (controller == null || sourceObject == null) { + if (controller == null) { return false; } Cards hand = new CardsImpl(controller.getHand()); - controller.moveCardsToExile(hand.getCards(game), source, game, false, CardUtil.getExileZoneId(game, source), sourceObject.getIdName()); + controller.moveCardsToExile(hand.getCards(game), source, game, false, CardUtil.getExileZoneId(game, source), CardUtil.getSourceName(game, source)); hand.getCards(game) .stream() .filter(Objects::nonNull) diff --git a/Mage.Sets/src/mage/cards/i/IkraShidiqiTheUsurper.java b/Mage.Sets/src/mage/cards/i/IkraShidiqiTheUsurper.java index c81b3b2cd29..c6fdbc11692 100644 --- a/Mage.Sets/src/mage/cards/i/IkraShidiqiTheUsurper.java +++ b/Mage.Sets/src/mage/cards/i/IkraShidiqiTheUsurper.java @@ -34,7 +34,7 @@ public final class IkraShidiqiTheUsurper extends CardImpl { this.toughness = new MageInt(7); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // Whenever a creature you control deals combat damage to a player, you gain life equal to that creature's toughness. this.addAbility(new IkraShidiqiTheUsurperTriggeredAbility()); diff --git a/Mage.Sets/src/mage/cards/i/IllTemperedLoner.java b/Mage.Sets/src/mage/cards/i/IllTemperedLoner.java new file mode 100644 index 00000000000..0381d939033 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/IllTemperedLoner.java @@ -0,0 +1,58 @@ +package mage.cards.i; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DealtDamageToSourceTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.SavedDamageValue; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.keyword.DayboundAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.target.common.TargetAnyTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class IllTemperedLoner extends CardImpl { + + public IllTemperedLoner(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{R}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WEREWOLF); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + this.secondSideCardClazz = mage.cards.h.HowlpackAvenger.class; + + // Whenever Ill-Tempered Loner is dealt damage, it deals that much damage to any target. + Ability ability = new DealtDamageToSourceTriggeredAbility(new DamageTargetEffect(SavedDamageValue.instance) + .setText("it deals that much damage to any target"), false, false); + ability.addTarget(new TargetAnyTarget()); + this.addAbility(ability); + + // {1}{R}: Ill-Tempered Loner gets +2/+0 until end of turn. + this.addAbility(new SimpleActivatedAbility(new BoostSourceEffect( + 2, 0, Duration.EndOfTurn + ), new ManaCostsImpl<>("{1}{R}"))); + + // Daybound + this.addAbility(new DayboundAbility()); + } + + private IllTemperedLoner(final IllTemperedLoner card) { + super(card); + } + + @Override + public IllTemperedLoner copy() { + return new IllTemperedLoner(this); + } +} diff --git a/Mage.Sets/src/mage/cards/i/IlluminatedFolio.java b/Mage.Sets/src/mage/cards/i/IlluminatedFolio.java index f3d371bf95b..8a1073e8ad8 100644 --- a/Mage.Sets/src/mage/cards/i/IlluminatedFolio.java +++ b/Mage.Sets/src/mage/cards/i/IlluminatedFolio.java @@ -1,44 +1,44 @@ - package mage.cards.i; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.UUID; +import mage.MageItem; +import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.RevealTargetFromHandCost; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.cards.Cards; -import mage.cards.CardsImpl; import mage.constants.CardType; -import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.ColorPredicate; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.ColorlessPredicate; import mage.game.Game; -import mage.players.Player; import mage.target.common.TargetCardInHand; +import java.util.List; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + /** - * - * @author jeffwadsworth + * @author TheElk801 */ public final class IlluminatedFolio extends CardImpl { public IlluminatedFolio(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{5}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{5}"); // {1}, {tap}, Reveal two cards from your hand that share a color: Draw a card. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), new ManaCostsImpl("{1}")); + Ability ability = new SimpleActivatedAbility( + new DrawCardSourceControllerEffect(1), new GenericManaCost(1) + ); ability.addCost(new TapSourceCost()); - ability.addCost(new RevealTwoCardsSharedColorFromHandCost()); + ability.addCost(new RevealTargetFromHandCost(new IlluminatedFolioTarget())); this.addAbility(ability); - } private IlluminatedFolio(final IlluminatedFolio card) { @@ -51,111 +51,84 @@ public final class IlluminatedFolio extends CardImpl { } } -class RevealTwoCardsSharedColorFromHandCost extends RevealTargetFromHandCost { +class IlluminatedFolioTarget extends TargetCardInHand { - public RevealTwoCardsSharedColorFromHandCost() { - super(new TargetTwoCardsWithTheSameColorInHand()); + private static final FilterCard filter = new FilterCard("two cards from your hand that share a color"); + + static { + filter.add(Predicates.not(ColorlessPredicate.instance)); } - public RevealTwoCardsSharedColorFromHandCost(RevealTwoCardsSharedColorFromHandCost cost) { - super(cost); + public IlluminatedFolioTarget() { + super(2, 2, filter); } - @Override - public RevealTwoCardsSharedColorFromHandCost copy() { - return new RevealTwoCardsSharedColorFromHandCost(this); - } - -} - -class TargetTwoCardsWithTheSameColorInHand extends TargetCardInHand { - - public TargetTwoCardsWithTheSameColorInHand() { - super(2, 2, new FilterCard("two cards from your hand that share a color")); - } - - public TargetTwoCardsWithTheSameColorInHand(final TargetTwoCardsWithTheSameColorInHand target) { + private IlluminatedFolioTarget(final IlluminatedFolioTarget target) { super(target); } @Override - public Set possibleTargets(UUID sourceControllerId, Game game) { - Set newPossibleTargets = new HashSet<>(); - Set possibleTargets = new HashSet<>(); - Player player = game.getPlayer(sourceControllerId); - for (Card card : player.getHand().getCards(filter, game)) { - possibleTargets.add(card.getId()); - } - - Cards cardsToCheck = new CardsImpl(); - cardsToCheck.addAll(possibleTargets); - if (targets.size() == 1) { - // first target is already choosen, now only targets with the shared color are selectable - for (Map.Entry entry : targets.entrySet()) { - Card chosenCard = cardsToCheck.get(entry.getKey(), game); - if (chosenCard != null) { - for (UUID cardToCheck : cardsToCheck) { - if (!cardToCheck.equals(chosenCard.getId()) && chosenCard.getColor(game).equals(game.getCard(cardToCheck).getColor(game))) { - newPossibleTargets.add(cardToCheck); - } - } - } - } - } else { - for (UUID cardToCheck : cardsToCheck) { - FilterCard colorFilter = new FilterCard(); - colorFilter.add(new ColorPredicate(game.getCard(cardToCheck).getColor(game))); - if (cardsToCheck.count(colorFilter, game) > 1) { - newPossibleTargets.add(cardToCheck); - } - } - } - return newPossibleTargets; + public boolean canChoose(UUID sourceId, UUID sourceControllerId, Game game) { + return super.canChoose(sourceId, sourceControllerId, game) + && !possibleTargets(sourceId,sourceControllerId, game).isEmpty(); } @Override - public boolean canChoose(UUID sourceControllerId, Game game) { - Cards cardsToCheck = new CardsImpl(); - Player player = game.getPlayer(sourceControllerId); - for (Card card : player.getHand().getCards(filter, game)) { - cardsToCheck.add(card.getId()); + public Set possibleTargets(UUID sourceId, UUID sourceControllerId, Game game) { + Set possibleTargets = super.possibleTargets(sourceId,sourceControllerId, game); + if (this.getTargets().size() == 1) { + Card card = game.getCard(this.getTargets().get(0)); + possibleTargets.removeIf( + uuid -> !game + .getCard(uuid) + .getColor(game) + .shares(card.getColor(game)) + ); + return possibleTargets; } - int possibleCards = 0; - for (UUID cardToCheck : cardsToCheck) { - FilterCard colorFilter = new FilterCard(); - colorFilter.add(new ColorPredicate(game.getCard(cardToCheck).getColor(game))); - if (cardsToCheck.count(colorFilter, game) > 1) { - ++possibleCards; + if (possibleTargets.size() < 2) { + possibleTargets.clear(); + return possibleTargets; + } + Set allTargets = possibleTargets + .stream() + .map(game::getCard) + .collect(Collectors.toSet()); + possibleTargets.clear(); + for (ObjectColor color : ObjectColor.getAllColors()) { + Set inColor = allTargets + .stream() + .filter(card -> card.getColor(game).shares(color)) + .collect(Collectors.toSet()); + if (inColor.size() > 1) { + inColor.stream().map(MageItem::getId).forEach(possibleTargets::add); + } + if (possibleTargets.size() == allTargets.size()) { + break; } } - return possibleCards > 0; + return possibleTargets; } @Override public boolean canTarget(UUID id, Game game) { - if (super.canTarget(id, game)) { - Card card = game.getCard(id); - if (card != null) { - if (targets.size() == 1) { - Card card2 = game.getCard(targets.entrySet().iterator().next().getKey()); - if (card2 != null && card2.getColor(game).equals(card.getColor(game))) { - return true; - } - } else { - FilterCard colorFilter = new FilterCard(); - colorFilter.add(new ColorPredicate(card.getColor(game))); - Player player = game.getPlayer(card.getOwnerId()); - if (player.getHand().getCards(colorFilter, game).size() > 1) { - return true; - } - } - } + if (!super.canTarget(id, game)) { + return false; } - return false; + List targetList = this.getTargets(); + if (targetList.isEmpty()) { + return true; + } + Card card = game.getCard(id); + return card != null + && targetList + .stream() + .map(game::getCard) + .anyMatch(c -> c.getColor(game).shares(card.getColor(game))); } @Override - public TargetTwoCardsWithTheSameColorInHand copy() { - return new TargetTwoCardsWithTheSameColorInHand(this); + public IlluminatedFolioTarget copy() { + return new IlluminatedFolioTarget(this); } } diff --git a/Mage.Sets/src/mage/cards/i/IllusionistsStratagem.java b/Mage.Sets/src/mage/cards/i/IllusionistsStratagem.java index d1c0c9ab699..179ced6e00a 100644 --- a/Mage.Sets/src/mage/cards/i/IllusionistsStratagem.java +++ b/Mage.Sets/src/mage/cards/i/IllusionistsStratagem.java @@ -6,7 +6,7 @@ import mage.abilities.effects.common.ReturnToBattlefieldUnderYourControlTargetEf import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetControlledCreaturePermanent; import java.util.UUID; @@ -24,10 +24,10 @@ public final class IllusionistsStratagem extends CardImpl { this.getSpellAbility().addEffect(new ReturnToBattlefieldUnderYourControlTargetEffect(false) .withReturnNames("those cards", "their owner's").concatBy(", then")); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent(0, 2, - new FilterControlledCreaturePermanent("creatures you control"), false)); + StaticFilters.FILTER_CONTROLLED_CREATURES, false)); // Draw a card. - this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("
")); } private IllusionistsStratagem(final IllusionistsStratagem card) { diff --git a/Mage.Sets/src/mage/cards/i/IllusoryAmbusher.java b/Mage.Sets/src/mage/cards/i/IllusoryAmbusher.java index 51532e9d9b4..b6142aaab42 100644 --- a/Mage.Sets/src/mage/cards/i/IllusoryAmbusher.java +++ b/Mage.Sets/src/mage/cards/i/IllusoryAmbusher.java @@ -32,7 +32,7 @@ public final class IllusoryAmbusher extends CardImpl { this.addAbility(FlashAbility.getInstance()); // Whenever Illusory Ambusher is dealt damage, draw that many cards. - this.addAbility(new DealtDamageToSourceTriggeredAbility(new IllusoryAmbusherDealtDamageEffect(), false, false, true)); + this.addAbility(new DealtDamageToSourceTriggeredAbility(new IllusoryAmbusherDealtDamageEffect(), false, false)); } private IllusoryAmbusher(final IllusoryAmbusher card) { diff --git a/Mage.Sets/src/mage/cards/i/ImagesOfThePast.java b/Mage.Sets/src/mage/cards/i/ImagesOfThePast.java index 52445298b8a..fad38ad15ba 100644 --- a/Mage.Sets/src/mage/cards/i/ImagesOfThePast.java +++ b/Mage.Sets/src/mage/cards/i/ImagesOfThePast.java @@ -11,7 +11,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; -import mage.filter.common.FilterCreatureCard; +import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; @@ -27,7 +27,7 @@ public final class ImagesOfThePast extends CardImpl { // Return up to two target creature cards from your graveyard to the battlefield, then exile those creatures. this.getSpellAbility().addEffect(new ImagesOfThePastEffect()); - this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, 2, new FilterCreatureCard("creature cards from your graveyard"))); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, 2, StaticFilters.FILTER_CARD_CREATURES_YOUR_GRAVEYARD)); } diff --git a/Mage.Sets/src/mage/cards/i/ImmolatingGlare.java b/Mage.Sets/src/mage/cards/i/ImmolatingGlare.java index 71792545d16..93d375a7f00 100644 --- a/Mage.Sets/src/mage/cards/i/ImmolatingGlare.java +++ b/Mage.Sets/src/mage/cards/i/ImmolatingGlare.java @@ -1,4 +1,3 @@ - package mage.cards.i; import java.util.UUID; @@ -6,8 +5,7 @@ import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterAttackingCreature; -import mage.target.common.TargetCreaturePermanent; +import mage.target.common.TargetAttackingCreature; /** * @@ -19,7 +17,7 @@ public final class ImmolatingGlare extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{W}"); // Destroy target attacking creature. - this.getSpellAbility().addTarget(new TargetCreaturePermanent(new FilterAttackingCreature())); + this.getSpellAbility().addTarget(new TargetAttackingCreature()); this.getSpellAbility().addEffect(new DestroyTargetEffect()); } diff --git a/Mage.Sets/src/mage/cards/i/ImmolatingSouleater.java b/Mage.Sets/src/mage/cards/i/ImmolatingSouleater.java index c795d8e54ec..465997d5d68 100644 --- a/Mage.Sets/src/mage/cards/i/ImmolatingSouleater.java +++ b/Mage.Sets/src/mage/cards/i/ImmolatingSouleater.java @@ -1,36 +1,33 @@ - package mage.cards.i; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.mana.PhyrexianManaCost; +import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.ColoredManaSymbol; import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.SubType; + +import java.util.UUID; /** - * * @author North */ public final class ImmolatingSouleater extends CardImpl { public ImmolatingSouleater(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{2}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{2}"); this.subtype.add(SubType.PHYREXIAN); this.subtype.add(SubType.DOG); this.power = new MageInt(1); this.toughness = new MageInt(1); - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, - new BoostSourceEffect(1, 0, Duration.EndOfTurn), - new PhyrexianManaCost(ColoredManaSymbol.R))); + this.addAbility(new SimpleActivatedAbility(new BoostSourceEffect( + 1, 0, Duration.EndOfTurn + ), new ManaCostsImpl<>("{R/P}"))); } private ImmolatingSouleater(final ImmolatingSouleater card) { diff --git a/Mage.Sets/src/mage/cards/i/ImpactTremors.java b/Mage.Sets/src/mage/cards/i/ImpactTremors.java index bae02f0c378..67307e5b5fa 100644 --- a/Mage.Sets/src/mage/cards/i/ImpactTremors.java +++ b/Mage.Sets/src/mage/cards/i/ImpactTremors.java @@ -11,7 +11,7 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.TargetController; import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; /** * @@ -25,7 +25,7 @@ public final class ImpactTremors extends CardImpl { // Whenever a creature enters the battlefield under your control, Impact Tremors deals 1 damage to each opponent. this.addAbility(new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD, new DamagePlayersEffect(Outcome.Damage, StaticValue.get(1), TargetController.OPPONENT), - new FilterCreaturePermanent("a creature"), + StaticFilters.FILTER_PERMANENT_A_CREATURE, false)); } diff --git a/Mage.Sets/src/mage/cards/i/ImperialMoth.java b/Mage.Sets/src/mage/cards/i/ImperialMoth.java new file mode 100644 index 00000000000..c3d40b92497 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/ImperialMoth.java @@ -0,0 +1,38 @@ +package mage.cards.i; + +import mage.MageInt; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ImperialMoth extends CardImpl { + + public ImperialMoth(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, ""); + + this.subtype.add(SubType.INSECT); + this.power = new MageInt(2); + this.toughness = new MageInt(4); + this.color.setWhite(true); + this.nightCard = true; + + // Flying + this.addAbility(FlyingAbility.getInstance()); + } + + private ImperialMoth(final ImperialMoth card) { + super(card); + } + + @Override + public ImperialMoth copy() { + return new ImperialMoth(this); + } +} diff --git a/Mage.Sets/src/mage/cards/i/ImperialOath.java b/Mage.Sets/src/mage/cards/i/ImperialOath.java new file mode 100644 index 00000000000..94944cd722d --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/ImperialOath.java @@ -0,0 +1,33 @@ +package mage.cards.i; + +import java.util.UUID; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.keyword.ScryEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.game.permanent.token.SamuraiToken; + +/** + * + * @author Addictiveme + */ +public final class ImperialOath extends CardImpl { + + public ImperialOath(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{5}{W}"); + + // Create three 2/2 white Samurai creature tokens with vigilance. Scry 3. + this.getSpellAbility().addEffect(new CreateTokenEffect(new SamuraiToken(), 3)); + this.getSpellAbility().addEffect(new ScryEffect(3)); + } + + private ImperialOath(final ImperialOath card) { + super(card); + } + + @Override + public ImperialOath copy() { + return new ImperialOath(this); + } +} diff --git a/Mage.Sets/src/mage/cards/i/ImperialRecoveryUnit.java b/Mage.Sets/src/mage/cards/i/ImperialRecoveryUnit.java new file mode 100644 index 00000000000..e6025c4471a --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/ImperialRecoveryUnit.java @@ -0,0 +1,60 @@ +package mage.cards.i; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.abilities.keyword.CrewAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ImperialRecoveryUnit extends CardImpl { + + private static final FilterCard filter + = new FilterCard("creature or Vehicle card with mana value 2 or less from your graveyard"); + + static { + filter.add(Predicates.or( + CardType.CREATURE.getPredicate(), + SubType.VEHICLE.getPredicate() + )); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 3)); + } + + public ImperialRecoveryUnit(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); + + // Whenever Imperial Recovery Unit attacks, return target creature or Vehicle card with mana value 2 or less from your graveyard to your hand. + Ability ability = new AttacksTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect()); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + this.addAbility(ability); + + // Crew 2 + this.addAbility(new CrewAbility(2)); + } + + private ImperialRecoveryUnit(final ImperialRecoveryUnit card) { + super(card); + } + + @Override + public ImperialRecoveryUnit copy() { + return new ImperialRecoveryUnit(this); + } +} diff --git a/Mage.Sets/src/mage/cards/i/ImperialSubduer.java b/Mage.Sets/src/mage/cards/i/ImperialSubduer.java new file mode 100644 index 00000000000..4e3861885a1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/ImperialSubduer.java @@ -0,0 +1,47 @@ +package mage.cards.i; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksAloneControlledTriggeredAbility; +import mage.abilities.effects.common.TapTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ImperialSubduer extends CardImpl { + + public ImperialSubduer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SAMURAI); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // Whenever a Samurai or Warrior you control attacks alone, tap target creature you don't control. + Ability ability = new AttacksAloneControlledTriggeredAbility( + new TapTargetEffect(), + StaticFilters.FILTER_CONTROLLED_SAMURAI_OR_WARRIOR, + false, false + ); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); + this.addAbility(ability); + } + + private ImperialSubduer(final ImperialSubduer card) { + super(card); + } + + @Override + public ImperialSubduer copy() { + return new ImperialSubduer(this); + } +} diff --git a/Mage.Sets/src/mage/cards/i/ImperiousMindbreaker.java b/Mage.Sets/src/mage/cards/i/ImperiousMindbreaker.java new file mode 100644 index 00000000000..b7413c01308 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/ImperiousMindbreaker.java @@ -0,0 +1,52 @@ +package mage.cards.i; + +import mage.MageInt; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.common.SourcePermanentToughnessValue; +import mage.abilities.effects.common.MillCardsEachPlayerEffect; +import mage.abilities.effects.common.continuous.GainAbilityPairedEffect; +import mage.abilities.keyword.SoulbondAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ImperiousMindbreaker extends CardImpl { + + public ImperiousMindbreaker(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{U}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(1); + this.toughness = new MageInt(4); + + // Soulbond + this.addAbility(new SoulbondAbility()); + + // As long as Imperious Mindbreaker is paired with another creature, each of those creatures has "Whenever this creature attacks, each opponent mills cards equal to its toughness." + this.addAbility(new SimpleStaticAbility(new GainAbilityPairedEffect( + new AttacksTriggeredAbility(new MillCardsEachPlayerEffect( + SourcePermanentToughnessValue.getInstance(), TargetController.OPPONENT + ), false, "Whenever this creature attacks, each opponent mills cards equal to its toughness."), + "As long as {this} is paired with another creature, each of those creatures has " + + "\"Whenever this creature attacks, each opponent mills cards equal to its toughness.\"" + ))); + } + + private ImperiousMindbreaker(final ImperiousMindbreaker card) { + super(card); + } + + @Override + public ImperiousMindbreaker copy() { + return new ImperiousMindbreaker(this); + } +} diff --git a/Mage.Sets/src/mage/cards/i/ImposingGrandeur.java b/Mage.Sets/src/mage/cards/i/ImposingGrandeur.java new file mode 100644 index 00000000000..d46bcf7e76e --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/ImposingGrandeur.java @@ -0,0 +1,90 @@ +package mage.cards.i; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.CommanderCardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ImposingGrandeur extends CardImpl { + + public ImposingGrandeur(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{R}"); + + // Each player may discard their hand and draw cards equal to the greatest mana value of a commander they own on the battlefield or in the command zone. + this.getSpellAbility().addEffect(new ImposingGrandeurEffect()); + } + + private ImposingGrandeur(final ImposingGrandeur card) { + super(card); + } + + @Override + public ImposingGrandeur copy() { + return new ImposingGrandeur(this); + } +} + +class ImposingGrandeurEffect extends OneShotEffect { + + ImposingGrandeurEffect() { + super(Outcome.DrawCard); + staticText = "each player may discard their hand and draw cards equal to " + + "the greatest mana value of a commander they own on the battlefield or in the command zone"; + } + + private ImposingGrandeurEffect(final ImposingGrandeurEffect effect) { + super(effect); + } + + @Override + public ImposingGrandeurEffect copy() { + return new ImposingGrandeurEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Map map = new HashMap<>(); + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + if (player == null) { + continue; + } + int maxValue = game + .getCommanderCardsFromAnyZones( + player, CommanderCardType.ANY, + Zone.BATTLEFIELD, Zone.COMMAND + ) + .stream() + .mapToInt(MageObject::getManaValue) + .max() + .orElse(0); + map.put(playerId, maxValue); + } + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + int amount = map.getOrDefault(playerId, 0); + if (player != null && player.chooseUse( + outcome, "Discard your hand and draw " + + amount + " cards?", source, game + )) { + player.discard(player.getHand(), false, source, game); + player.drawCards(amount, source, game); + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/i/ImposingSovereign.java b/Mage.Sets/src/mage/cards/i/ImposingSovereign.java index 0d3b5b03c12..34ca08ba114 100644 --- a/Mage.Sets/src/mage/cards/i/ImposingSovereign.java +++ b/Mage.Sets/src/mage/cards/i/ImposingSovereign.java @@ -1,16 +1,13 @@ package mage.cards.i; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.PermanentsEnterBattlefieldTappedEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.game.Game; -import mage.game.events.EntersTheBattlefieldEvent; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.StaticFilters; import java.util.UUID; @@ -27,8 +24,9 @@ public final class ImposingSovereign extends CardImpl { this.toughness = new MageInt(1); // Creatures your opponents control enter the battlefield tapped. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ImposingSovereignEffect())); - + this.addAbility(new SimpleStaticAbility(new PermanentsEnterBattlefieldTappedEffect( + StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE + ).setText("creatures your opponents control enter the battlefield tapped"))); } private ImposingSovereign(final ImposingSovereign card) { @@ -40,43 +38,3 @@ public final class ImposingSovereign extends CardImpl { return new ImposingSovereign(this); } } - -class ImposingSovereignEffect extends ReplacementEffectImpl { - - ImposingSovereignEffect() { - super(Duration.WhileOnBattlefield, Outcome.Tap); - staticText = "Creatures your opponents control enter the battlefield tapped"; - } - - private ImposingSovereignEffect(final ImposingSovereignEffect effect) { - super(effect); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Permanent target = ((EntersTheBattlefieldEvent) event).getTarget(); - if (target != null) { - target.tap(source, game); - } - return false; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (!game.getOpponents(source.getControllerId()).contains(event.getPlayerId())) { - return false; - } - Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget(); - return permanent != null && permanent.isCreature(game); - } - - @Override - public ImposingSovereignEffect copy() { - return new ImposingSovereignEffect(this); - } -} diff --git a/Mage.Sets/src/mage/cards/i/ImposterMech.java b/Mage.Sets/src/mage/cards/i/ImposterMech.java new file mode 100644 index 00000000000..f48be1ad829 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/ImposterMech.java @@ -0,0 +1,61 @@ +package mage.cards.i; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.effects.common.CopyPermanentEffect; +import mage.abilities.keyword.CrewAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.util.functions.CopyApplier; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ImposterMech extends CardImpl { + + private static final CopyApplier applier = new CopyApplier() { + @Override + public boolean apply(Game game, MageObject blueprint, Ability source, UUID targetObjectId) { + blueprint.removeAllCardTypes(game); + blueprint.addCardType(game, CardType.ARTIFACT); + blueprint.addSubType(game, SubType.VEHICLE); + blueprint.getAbilities().add(new CrewAbility(3)); + return true; + } + }; + + public ImposterMech(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}{U}"); + + this.subtype.add(SubType.VEHICLE); + this.power = new MageInt(3); + this.toughness = new MageInt(1); + + // You may have Imposter Mech enter the battlefield as a copy of a creature an opponent controls, except its a Vehicle artifact with crew 3 and it loses all other card types. + this.addAbility(new EntersBattlefieldAbility( + new CopyPermanentEffect(StaticFilters.FILTER_OPPONENTS_PERMANENT_A_CREATURE, applier), + null, "You may have {this} enter the battlefield as a copy of a creature " + + "an opponent controls, except its a Vehicle artifact with crew 3 and it loses all other card types.", null + )); + + // Crew 3 + this.addAbility(new CrewAbility(3)); + } + + private ImposterMech(final ImposterMech card) { + super(card); + } + + @Override + public ImposterMech copy() { + return new ImposterMech(this); + } +} diff --git a/Mage.Sets/src/mage/cards/i/InTheWebOfWar.java b/Mage.Sets/src/mage/cards/i/InTheWebOfWar.java index 8b5409f3ec2..6a22ece41d2 100644 --- a/Mage.Sets/src/mage/cards/i/InTheWebOfWar.java +++ b/Mage.Sets/src/mage/cards/i/InTheWebOfWar.java @@ -14,6 +14,7 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SetTargetPointer; import mage.constants.Zone; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; /** @@ -22,16 +23,13 @@ import mage.filter.common.FilterCreaturePermanent; */ public final class InTheWebOfWar extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature"); - public InTheWebOfWar(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{3}{R}{R}"); - // Whenever a creature enters the battlefield under your control, it gets +2/+0 and gains haste until end of turn. Effect effect = new BoostTargetEffect(2,0, Duration.EndOfTurn); effect.setText("it gets +2/+0"); - Ability ability = new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD, effect, filter, false, SetTargetPointer.PERMANENT, null); + Ability ability = new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD, effect, StaticFilters.FILTER_PERMANENT_A_CREATURE, false, SetTargetPointer.PERMANENT, null); effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn); effect.setText("and gains haste until end of turn"); ability.addEffect(effect); diff --git a/Mage.Sets/src/mage/cards/i/InactionInjunction.java b/Mage.Sets/src/mage/cards/i/InactionInjunction.java index 4fa42b05f7b..a3cbab7feff 100644 --- a/Mage.Sets/src/mage/cards/i/InactionInjunction.java +++ b/Mage.Sets/src/mage/cards/i/InactionInjunction.java @@ -7,8 +7,7 @@ import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; /** @@ -17,26 +16,17 @@ import mage.target.common.TargetCreaturePermanent; */ public final class InactionInjunction extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public InactionInjunction(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{U}"); - - // Detain target creature an opponent controls. // (Until your next turn, that creature can't attack or block and its activated abilities can't be activated.) this.getSpellAbility().addEffect(new DetainTargetEffect()); - TargetCreaturePermanent target = new TargetCreaturePermanent(filter); + TargetCreaturePermanent target = new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE); this.getSpellAbility().addTarget(target); // Draw a card. this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); - } private InactionInjunction(final InactionInjunction card) { diff --git a/Mage.Sets/src/mage/cards/i/InallaArchmageRitualist.java b/Mage.Sets/src/mage/cards/i/InallaArchmageRitualist.java index 05aca9f23ea..b162446354b 100644 --- a/Mage.Sets/src/mage/cards/i/InallaArchmageRitualist.java +++ b/Mage.Sets/src/mage/cards/i/InallaArchmageRitualist.java @@ -114,7 +114,7 @@ class InallaArchmageRitualistEffect extends OneShotEffect { CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(null, null, true); effect.setTargetPointer(getTargetPointer()); if (effect.apply(game, source)) { - for (Permanent tokenPermanent : effect.getAddedPermanent()) { + for (Permanent tokenPermanent : effect.getAddedPermanents()) { ExileTargetEffect exileEffect = new ExileTargetEffect(); exileEffect.setTargetPointer(new FixedTarget(tokenPermanent, game)); DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); diff --git a/Mage.Sets/src/mage/cards/i/IndathaTriome.java b/Mage.Sets/src/mage/cards/i/IndathaTriome.java index 22d2d1572fe..b6f2baad954 100644 --- a/Mage.Sets/src/mage/cards/i/IndathaTriome.java +++ b/Mage.Sets/src/mage/cards/i/IndathaTriome.java @@ -1,7 +1,7 @@ package mage.cards.i; import mage.abilities.common.EntersBattlefieldTappedAbility; -import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.keyword.CyclingAbility; import mage.abilities.mana.BlackManaAbility; import mage.abilities.mana.GreenManaAbility; @@ -34,7 +34,7 @@ public final class IndathaTriome extends CardImpl { this.addAbility(new EntersBattlefieldTappedAbility()); // Cycling {3} - this.addAbility(new CyclingAbility(new ManaCostsImpl("{3}"))); + this.addAbility(new CyclingAbility(new GenericManaCost(3))); } private IndathaTriome(final IndathaTriome card) { diff --git a/Mage.Sets/src/mage/cards/i/IndomitableCreativity.java b/Mage.Sets/src/mage/cards/i/IndomitableCreativity.java index 679e186ad3d..b0c9866c237 100644 --- a/Mage.Sets/src/mage/cards/i/IndomitableCreativity.java +++ b/Mage.Sets/src/mage/cards/i/IndomitableCreativity.java @@ -71,7 +71,7 @@ class IndomitableCreativityEffect extends OneShotEffect { "For each permanent destroyed this way, " + "its controller reveals cards from the top of their library" + " until an artifact or creature card is revealed and exiles that card. " + - "Those players put the exiled card onto the battlefield, then shuffle"; + "Those players put the exiled cards onto the battlefield, then shuffle"; } public IndomitableCreativityEffect(final IndomitableCreativityEffect effect) { diff --git a/Mage.Sets/src/mage/cards/i/InducedAmnesia.java b/Mage.Sets/src/mage/cards/i/InducedAmnesia.java index 7d857e2fcb0..a5513edd8cf 100644 --- a/Mage.Sets/src/mage/cards/i/InducedAmnesia.java +++ b/Mage.Sets/src/mage/cards/i/InducedAmnesia.java @@ -1,6 +1,5 @@ package mage.cards.i; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.PutIntoGraveFromBattlefieldSourceTriggeredAbility; @@ -66,8 +65,7 @@ class InducedAmnesiaExileEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player targetPlayer = game.getPlayer(getTargetPointer().getFirst(game, source)); - MageObject sourceObject = source.getSourceObject(game); - if (targetPlayer == null || sourceObject == null) { + if (targetPlayer == null) { return false; } int numberOfCards = targetPlayer.getHand().size(); @@ -77,7 +75,7 @@ class InducedAmnesiaExileEffect extends OneShotEffect { Cards cards = new CardsImpl(targetPlayer.getHand()); targetPlayer.moveCardsToExile( cards.getCards(game), source, game, false, - CardUtil.getExileZoneId(game, source), sourceObject.getIdName() + CardUtil.getExileZoneId(game, source), CardUtil.getSourceName(game, source) ); cards.getCards(game) .stream() diff --git a/Mage.Sets/src/mage/cards/i/InfectiousCurse.java b/Mage.Sets/src/mage/cards/i/InfectiousCurse.java index 4d5aec75422..1ed1ff50d5e 100644 --- a/Mage.Sets/src/mage/cards/i/InfectiousCurse.java +++ b/Mage.Sets/src/mage/cards/i/InfectiousCurse.java @@ -33,7 +33,6 @@ public final class InfectiousCurse extends CardImpl { this.color.setBlack(true); this.nightCard = true; - this.transformable = true; // Enchant player TargetPlayer auraTarget = new TargetPlayer(); diff --git a/Mage.Sets/src/mage/cards/i/InfernalDenizen.java b/Mage.Sets/src/mage/cards/i/InfernalDenizen.java index 62ba7769cf4..c5d0fd92572 100644 --- a/Mage.Sets/src/mage/cards/i/InfernalDenizen.java +++ b/Mage.Sets/src/mage/cards/i/InfernalDenizen.java @@ -1,4 +1,3 @@ - package mage.cards.i; import java.util.UUID; @@ -124,7 +123,7 @@ class InfernalDenizenEffect extends OneShotEffect { new GainControlTargetEffect(Duration.Custom, true, opponent.getId()), SourceOnBattlefieldCondition.instance, ""); - giveEffect.setTargetPointer(new FixedTarget(targetCreature.getFirstTarget())); + giveEffect.setTargetPointer(new FixedTarget(targetCreature.getFirstTarget(), game)); game.addEffect(giveEffect, source); return true; } diff --git a/Mage.Sets/src/mage/cards/i/InfernalKirin.java b/Mage.Sets/src/mage/cards/i/InfernalKirin.java index 307eb2a50f6..7af8c205383 100644 --- a/Mage.Sets/src/mage/cards/i/InfernalKirin.java +++ b/Mage.Sets/src/mage/cards/i/InfernalKirin.java @@ -36,7 +36,7 @@ public final class InfernalKirin extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever you cast a Spirit or Arcane spell, target player reveals their hand and discards all cards with that spell's converted mana cost. - Ability ability = new SpellCastControllerTriggeredAbility(Zone.BATTLEFIELD, new InfernalKirinEffect(), StaticFilters.SPIRIT_OR_ARCANE_CARD, false, true); + Ability ability = new SpellCastControllerTriggeredAbility(Zone.BATTLEFIELD, new InfernalKirinEffect(), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false, true); ability.addTarget(new TargetPlayer()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/i/InfernoTitan.java b/Mage.Sets/src/mage/cards/i/InfernoTitan.java index 1d176b6d1c3..34f7103a944 100644 --- a/Mage.Sets/src/mage/cards/i/InfernoTitan.java +++ b/Mage.Sets/src/mage/cards/i/InfernoTitan.java @@ -1,4 +1,3 @@ - package mage.cards.i; import java.util.UUID; @@ -6,15 +5,12 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldOrAttacksSourceTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.costs.mana.ColoredManaCost; import mage.abilities.effects.common.DamageMultiEffect; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.*; import mage.target.common.TargetAnyTargetAmount; /** @@ -31,9 +27,9 @@ public final class InfernoTitan extends CardImpl { this.toughness = new MageInt(6); // {R}: Inferno Titan gets +1/+0 until end of turn. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1, 0, Duration.EndOfTurn), new ManaCostsImpl("{R}"))); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1, 0, Duration.EndOfTurn), new ColoredManaCost(ColoredManaSymbol.R))); - // Whenever Inferno Titan enters the battlefield or attacks, it deals 3 damage divided as you choose among one, two, or three target creatures and/or players. + // Whenever Inferno Titan enters the battlefield or attacks, it deals 3 damage divided as you choose among one, two, or three targets. Ability ability = new EntersBattlefieldOrAttacksSourceTriggeredAbility(new DamageMultiEffect(3, "it")); ability.addTarget(new TargetAnyTargetAmount(3)); this.addAbility(ability); @@ -47,5 +43,4 @@ public final class InfernoTitan extends CardImpl { public InfernoTitan copy() { return new InfernoTitan(this); } - } diff --git a/Mage.Sets/src/mage/cards/i/Infest.java b/Mage.Sets/src/mage/cards/i/Infest.java index 271bf6f8ca7..453533588ad 100644 --- a/Mage.Sets/src/mage/cards/i/Infest.java +++ b/Mage.Sets/src/mage/cards/i/Infest.java @@ -1,4 +1,3 @@ - package mage.cards.i; import java.util.UUID; @@ -7,7 +6,6 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.common.FilterCreaturePermanent; /** * @@ -20,7 +18,7 @@ public final class Infest extends CardImpl { // All creatures get -2/-2 until end of turn. - this.getSpellAbility().addEffect(new BoostAllEffect(-2, -2, Duration.EndOfTurn, new FilterCreaturePermanent("All creatures"), false)); + this.getSpellAbility().addEffect(new BoostAllEffect(-2, -2, Duration.EndOfTurn)); } private Infest(final Infest card) { diff --git a/Mage.Sets/src/mage/cards/i/InfestationExpert.java b/Mage.Sets/src/mage/cards/i/InfestationExpert.java new file mode 100644 index 00000000000..b5bd768744e --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/InfestationExpert.java @@ -0,0 +1,46 @@ +package mage.cards.i; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldOrAttacksSourceTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.DayboundAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.InsectToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class InfestationExpert extends CardImpl { + + public InfestationExpert(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WEREWOLF); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + this.secondSideCardClazz = mage.cards.i.InfestedWerewolf.class; + + // Whenever Infestation Expert enters the battlefield or attacks, create a 1/1 green Insect creature token. + this.addAbility(new EntersBattlefieldOrAttacksSourceTriggeredAbility( + new CreateTokenEffect(new InsectToken()) + )); + + // Daybound + this.addAbility(new DayboundAbility()); + } + + private InfestationExpert(final InfestationExpert card) { + super(card); + } + + @Override + public InfestationExpert copy() { + return new InfestationExpert(this); + } +} diff --git a/Mage.Sets/src/mage/cards/i/InfestedWerewolf.java b/Mage.Sets/src/mage/cards/i/InfestedWerewolf.java new file mode 100644 index 00000000000..96f815e0c32 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/InfestedWerewolf.java @@ -0,0 +1,46 @@ +package mage.cards.i; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldOrAttacksSourceTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.NightboundAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.InsectToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class InfestedWerewolf extends CardImpl { + + public InfestedWerewolf(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); + + this.subtype.add(SubType.WEREWOLF); + this.power = new MageInt(4); + this.toughness = new MageInt(5); + this.color.setGreen(true); + this.nightCard = true; + + // Whenever Infested Werewolf enters the battlefield or attacks, create two 1/1 green Insect creature token. + this.addAbility(new EntersBattlefieldOrAttacksSourceTriggeredAbility( + new CreateTokenEffect(new InsectToken(), 2) + )); + + // Nightbound + this.addAbility(new NightboundAbility()); + } + + private InfestedWerewolf(final InfestedWerewolf card) { + super(card); + } + + @Override + public InfestedWerewolf copy() { + return new InfestedWerewolf(this); + } +} diff --git a/Mage.Sets/src/mage/cards/i/InfiniteAuthority.java b/Mage.Sets/src/mage/cards/i/InfiniteAuthority.java index 422da2a6e29..397faa7150f 100644 --- a/Mage.Sets/src/mage/cards/i/InfiniteAuthority.java +++ b/Mage.Sets/src/mage/cards/i/InfiniteAuthority.java @@ -1,4 +1,3 @@ - package mage.cards.i; import java.util.Objects; @@ -90,13 +89,13 @@ class InfiniteAuthorityTriggeredAbility extends TriggeredAbilityImpl { if (blocker != null && Objects.equals(blocked, enchantedCreature) && blocker.getToughness().getValue() <= 3) { - effect.setTargetPointer(new FixedTarget(blocker.getId())); + effect.setTargetPointer(new FixedTarget(blocker.getId(), game)); return true; } if (blocked != null && Objects.equals(blocker, enchantedCreature) && blocked.getToughness().getValue() <= 3) { - effect.setTargetPointer(new FixedTarget(blocked.getId())); + effect.setTargetPointer(new FixedTarget(blocked.getId(), game)); return true; } } @@ -106,7 +105,7 @@ class InfiniteAuthorityTriggeredAbility extends TriggeredAbilityImpl { @Override public String getTriggerPhrase() { - return "Whenever enchanted creature blocks or becomes blocked by a creature with toughness 3 or less, " ; + return "Whenever enchanted creature blocks or becomes blocked by a creature with toughness 3 or less, "; } } diff --git a/Mage.Sets/src/mage/cards/i/InfusedArrows.java b/Mage.Sets/src/mage/cards/i/InfusedArrows.java index 08f74eb8730..79998c242dc 100644 --- a/Mage.Sets/src/mage/cards/i/InfusedArrows.java +++ b/Mage.Sets/src/mage/cards/i/InfusedArrows.java @@ -1,4 +1,3 @@ - package mage.cards.i; import java.util.UUID; @@ -9,14 +8,12 @@ import mage.abilities.costs.common.TapSourceCost; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.RemovedCountersForCostValue; import mage.abilities.dynamicvalue.common.SignInversionDynamicValue; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.abilities.keyword.SunburstAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Zone; import mage.counters.CounterType; import mage.target.common.TargetCreaturePermanent; @@ -26,16 +23,15 @@ import mage.target.common.TargetCreaturePermanent; */ public final class InfusedArrows extends CardImpl { + private static final DynamicValue xValue = new SignInversionDynamicValue(RemovedCountersForCostValue.instance); + public InfusedArrows(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{4}"); // Sunburst this.addAbility(new SunburstAbility(this)); // {tap}, Remove X charge counters from Infused Arrows: Target creature gets -X/-X until end of turn. - DynamicValue value = new SignInversionDynamicValue(RemovedCountersForCostValue.instance); - Effect effect = new BoostTargetEffect(value, value, Duration.EndOfTurn); - effect.setText("Target creature gets -X/-X until end of turn"); - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new TapSourceCost()); + Ability ability = new SimpleActivatedAbility(new BoostTargetEffect(xValue, xValue, Duration.EndOfTurn), new TapSourceCost()); ability.addCost(new RemoveVariableCountersSourceCost(CounterType.CHARGE.createInstance())); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/i/IngeniousInfiltrator.java b/Mage.Sets/src/mage/cards/i/IngeniousInfiltrator.java index 593bf29f82c..fb850d1308f 100644 --- a/Mage.Sets/src/mage/cards/i/IngeniousInfiltrator.java +++ b/Mage.Sets/src/mage/cards/i/IngeniousInfiltrator.java @@ -32,7 +32,7 @@ public final class IngeniousInfiltrator extends CardImpl { this.toughness = new MageInt(3); // Ninjutsu {U}{B} - this.addAbility(new NinjutsuAbility(new ManaCostsImpl("{U}{B}"))); + this.addAbility(new NinjutsuAbility("{U}{B}")); // Whenever a Ninja you control deals combat damage to a player, draw a card. this.addAbility(new DealsDamageToAPlayerAllTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/i/InheritedFiend.java b/Mage.Sets/src/mage/cards/i/InheritedFiend.java index 82cb137fd31..52f35e6004c 100644 --- a/Mage.Sets/src/mage/cards/i/InheritedFiend.java +++ b/Mage.Sets/src/mage/cards/i/InheritedFiend.java @@ -12,6 +12,8 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.counters.CounterType; +import mage.filter.FilterCard; +import mage.filter.common.FilterCreatureCard; import mage.target.common.TargetCardInGraveyard; import java.util.UUID; @@ -21,6 +23,8 @@ import java.util.UUID; */ public final class InheritedFiend extends CardImpl { + private static final FilterCard filter = new FilterCreatureCard("creature card from a graveyard"); + public InheritedFiend(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); @@ -28,7 +32,6 @@ public final class InheritedFiend extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(4); this.color.setBlack(true); - this.transformable = true; this.nightCard = true; // Flying @@ -37,7 +40,7 @@ public final class InheritedFiend extends CardImpl { // {2}{B}: Exile target creature card from a graveyard. Put a +1/+1 counter on Inherited Fiend. Ability ability = new SimpleActivatedAbility(new ExileTargetEffect(), new ManaCostsImpl<>("{2}{B}")); ability.addEffect(new AddCountersSourceEffect(CounterType.P1P1.createInstance()).concatBy(".")); - ability.addTarget(new TargetCardInGraveyard()); + ability.addTarget(new TargetCardInGraveyard(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/i/InkEyesServantOfOni.java b/Mage.Sets/src/mage/cards/i/InkEyesServantOfOni.java index 2ad0b5c7ba0..975bdf7aad0 100644 --- a/Mage.Sets/src/mage/cards/i/InkEyesServantOfOni.java +++ b/Mage.Sets/src/mage/cards/i/InkEyesServantOfOni.java @@ -38,7 +38,7 @@ public final class InkEyesServantOfOni extends CardImpl { this.toughness = new MageInt(4); // Ninjutsu {3}{B}{B} ({3}{B}{B}, Return an unblocked attacker you control to hand: Put this card onto the battlefield from your hand tapped and attacking.) - this.addAbility(new NinjutsuAbility(new ManaCostsImpl("{3}{B}{B}"))); + this.addAbility(new NinjutsuAbility("{3}{B}{B}")); // Whenever Ink-Eyes, Servant of Oni deals combat damage to a player, you may put target creature card from that player's graveyard onto the battlefield under your control. this.addAbility(new InkEyesServantOfOniTriggeredAbility()); diff --git a/Mage.Sets/src/mage/cards/i/InkriseInfiltrator.java b/Mage.Sets/src/mage/cards/i/InkriseInfiltrator.java new file mode 100644 index 00000000000..f00ffa95454 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/InkriseInfiltrator.java @@ -0,0 +1,46 @@ +package mage.cards.i; + +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class InkriseInfiltrator extends CardImpl { + + public InkriseInfiltrator(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.NINJA); + this.power = new MageInt(1); + this.toughness = new MageInt(2); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // {3}{B}: Inkrise Infiltrator gets +2/+2 until end of turn. + this.addAbility(new SimpleActivatedAbility(new BoostSourceEffect( + 2, 2, Duration.EndOfTurn + ), new ManaCostsImpl<>("{3}{B}"))); + } + + private InkriseInfiltrator(final InkriseInfiltrator card) { + super(card); + } + + @Override + public InkriseInfiltrator copy() { + return new InkriseInfiltrator(this); + } +} diff --git a/Mage.Sets/src/mage/cards/i/InnocenceKami.java b/Mage.Sets/src/mage/cards/i/InnocenceKami.java index 8f9ed05156a..8ac260e9cee 100644 --- a/Mage.Sets/src/mage/cards/i/InnocenceKami.java +++ b/Mage.Sets/src/mage/cards/i/InnocenceKami.java @@ -35,7 +35,7 @@ public final class InnocenceKami extends CardImpl { ability.addCost(new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); - this.addAbility(new SpellCastControllerTriggeredAbility(new UntapSourceEffect(), StaticFilters.SPIRIT_OR_ARCANE_CARD, false)); + this.addAbility(new SpellCastControllerTriggeredAbility(new UntapSourceEffect(), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false)); } private InnocenceKami(final InnocenceKami card) { diff --git a/Mage.Sets/src/mage/cards/i/InnocentBlood.java b/Mage.Sets/src/mage/cards/i/InnocentBlood.java index c240985170e..c968a46028c 100644 --- a/Mage.Sets/src/mage/cards/i/InnocentBlood.java +++ b/Mage.Sets/src/mage/cards/i/InnocentBlood.java @@ -1,26 +1,25 @@ - package mage.cards.i; -import java.util.UUID; import mage.abilities.effects.common.SacrificeAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * * @author jonubuu */ public final class InnocentBlood extends CardImpl { - public InnocentBlood(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{B}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{B}"); // Each player sacrifices a creature. - this.getSpellAbility().addEffect(new SacrificeAllEffect(1, new FilterControlledCreaturePermanent("creature"))); + this.getSpellAbility().addEffect(new SacrificeAllEffect( + 1, StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT + )); } private InnocentBlood(final InnocentBlood card) { diff --git a/Mage.Sets/src/mage/cards/i/InnocentTraveler.java b/Mage.Sets/src/mage/cards/i/InnocentTraveler.java new file mode 100644 index 00000000000..8e6d2edf044 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/InnocentTraveler.java @@ -0,0 +1,102 @@ +package mage.cards.i; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.TransformAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class InnocentTraveler extends CardImpl { + + public InnocentTraveler(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{B}"); + + this.subtype.add(SubType.HUMAN); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + this.secondSideCardClazz = mage.cards.m.MaliciousInvader.class; + + // At the beginning of your upkeep, any opponent may sacrifice a creature. If no one does, transform Innocent Traveler. + this.addAbility(new TransformAbility()); + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new InnocentTravelerEffect(), TargetController.YOU, false + )); + } + + private InnocentTraveler(final InnocentTraveler card) { + super(card); + } + + @Override + public InnocentTraveler copy() { + return new InnocentTraveler(this); + } +} + +class InnocentTravelerEffect extends OneShotEffect { + + private static final FilterPermanent filter = new FilterControlledCreaturePermanent("creature to sacrifice"); + + InnocentTravelerEffect() { + super(Outcome.Benefit); + staticText = "any opponent may sacrifice a creature. If no one does, transform {this}"; + } + + private InnocentTravelerEffect(final InnocentTravelerEffect effect) { + super(effect); + } + + @Override + public InnocentTravelerEffect copy() { + return new InnocentTravelerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + boolean flag = false; + for (UUID opponentId : game.getOpponents(source.getControllerId())) { + Player opponent = game.getPlayer(opponentId); + if (opponent == null) { + continue; + } + + TargetPermanent target = new TargetPermanent(filter); + target.setNotTarget(true); + if (!target.canChoose(source.getSourceId(), opponent.getId(), game) + || !opponent.chooseUse(Outcome.AIDontUseIt, "Sacrifice a creature?", source, game) + || !opponent.choose(Outcome.Sacrifice, target, source.getSourceId(), game)) { + continue; + } + Permanent permanent = game.getPermanent(target.getFirstTarget()); + if (permanent == null || !permanent.sacrifice(source, game)) { + continue; + } + flag = true; + } + if (flag) { + return true; + } + Permanent sourcePerm = source.getSourcePermanentIfItStillExists(game); + if (sourcePerm != null) { + sourcePerm.transform(source, game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/i/InquisitivePuppet.java b/Mage.Sets/src/mage/cards/i/InquisitivePuppet.java index 9de9edf129a..7537629e91f 100644 --- a/Mage.Sets/src/mage/cards/i/InquisitivePuppet.java +++ b/Mage.Sets/src/mage/cards/i/InquisitivePuppet.java @@ -27,7 +27,7 @@ public final class InquisitivePuppet extends CardImpl { this.toughness = new MageInt(2); // When Inquisitive Puppet enters the battlefield, scry 1. - this.addAbility(new EntersBattlefieldTriggeredAbility(new ScryEffect(1))); + this.addAbility(new EntersBattlefieldTriggeredAbility(new ScryEffect(1, false))); // Exile Inquisitive Puppet: Create a 1/1 white Human creature token. this.addAbility(new SimpleActivatedAbility(new CreateTokenEffect(new HumanToken()), new ExileSourceCost())); diff --git a/Mage.Sets/src/mage/cards/i/InsatiableSouleater.java b/Mage.Sets/src/mage/cards/i/InsatiableSouleater.java index b6e2d3b19c9..31bdf5df35a 100644 --- a/Mage.Sets/src/mage/cards/i/InsatiableSouleater.java +++ b/Mage.Sets/src/mage/cards/i/InsatiableSouleater.java @@ -1,37 +1,34 @@ - package mage.cards.i; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.mana.PhyrexianManaCost; +import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.ColoredManaSymbol; import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.SubType; + +import java.util.UUID; /** - * * @author North */ public final class InsatiableSouleater extends CardImpl { public InsatiableSouleater(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{4}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{4}"); this.subtype.add(SubType.PHYREXIAN); this.subtype.add(SubType.BEAST); this.power = new MageInt(5); this.toughness = new MageInt(1); - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, - new GainAbilitySourceEffect(TrampleAbility.getInstance(), Duration.EndOfTurn), - new PhyrexianManaCost(ColoredManaSymbol.G))); + this.addAbility(new SimpleActivatedAbility(new GainAbilitySourceEffect( + TrampleAbility.getInstance(), Duration.EndOfTurn + ), new ManaCostsImpl<>("{G/P}"))); } private InsatiableSouleater(final InsatiableSouleater card) { diff --git a/Mage.Sets/src/mage/cards/i/InscriptionOfAbundance.java b/Mage.Sets/src/mage/cards/i/InscriptionOfAbundance.java index 0d7a0aeadfe..017823e60f2 100644 --- a/Mage.Sets/src/mage/cards/i/InscriptionOfAbundance.java +++ b/Mage.Sets/src/mage/cards/i/InscriptionOfAbundance.java @@ -36,7 +36,8 @@ public final class InscriptionOfAbundance extends CardImpl { // Choose one. If this spell was kicked, choose any number instead. // • Put two +1/+1 counters on target creature. - this.getSpellAbility().getModes().setChooseText("choose one. If this spell was kicked, choose any number instead."); + this.getSpellAbility().getModes().setChooseText("choose one. " + + "If this spell was kicked, choose any number instead."); this.getSpellAbility().addEffect(new AddCountersTargetEffect(CounterType.P1P1.createInstance(2))); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); @@ -46,7 +47,7 @@ public final class InscriptionOfAbundance extends CardImpl { this.getSpellAbility().addMode(mode); // • Target creature you control fights target creature you don't control. - mode = new Mode(new FightTargetsEffect()); + mode = new Mode(new FightTargetsEffect(false)); mode.addTarget(new TargetControlledCreaturePermanent()); mode.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); this.getSpellAbility().addMode(mode); diff --git a/Mage.Sets/src/mage/cards/i/InsidiousMist.java b/Mage.Sets/src/mage/cards/i/InsidiousMist.java index d429fc40bee..f5598c337da 100644 --- a/Mage.Sets/src/mage/cards/i/InsidiousMist.java +++ b/Mage.Sets/src/mage/cards/i/InsidiousMist.java @@ -36,7 +36,6 @@ public final class InsidiousMist extends CardImpl { this.color.setBlue(true); this.nightCard = true; - this.transformable = true; // Hexproof this.addAbility(HexproofAbility.getInstance()); @@ -53,7 +52,7 @@ public final class InsidiousMist extends CardImpl { // Whenever Insideous Mist attacks and isn't blocked, you may pay {2}{B}. If you do, transform it. this.addAbility(new TransformAbility()); - this.addAbility(new AttacksAndIsNotBlockedTriggeredAbility(new DoIfCostPaid(new TransformSourceEffect(false), new ManaCostsImpl("{2}{B}"), "Pay {2}{B} to transform?"))); + this.addAbility(new AttacksAndIsNotBlockedTriggeredAbility(new DoIfCostPaid(new TransformSourceEffect(), new ManaCostsImpl("{2}{B}"), "Pay {2}{B} to transform?"))); } private InsidiousMist(final InsidiousMist card) { diff --git a/Mage.Sets/src/mage/cards/i/InspireAwe.java b/Mage.Sets/src/mage/cards/i/InspireAwe.java index 483b60114aa..1887efa7d50 100644 --- a/Mage.Sets/src/mage/cards/i/InspireAwe.java +++ b/Mage.Sets/src/mage/cards/i/InspireAwe.java @@ -29,8 +29,8 @@ public final class InspireAwe extends CardImpl { // Prevent all combat damage that would be dealt this turn except by enchanted creatures and enchantment creatures. Scry 2. this.getSpellAbility().addEffect(new PreventAllDamageByAllPermanentsEffect( filter, Duration.EndOfTurn, true - ).setText("Prevent all combat damage that would be dealt this turn " + - "except by enchanted creatures and enchantment creatures.")); + ).setText("prevent all combat damage that would be dealt this turn except combat damage " + + "that would be dealt by enchanted creatures and enchantment creatures")); this.getSpellAbility().addEffect(new ScryEffect(2)); } diff --git a/Mage.Sets/src/mage/cards/i/InspiredIdea.java b/Mage.Sets/src/mage/cards/i/InspiredIdea.java new file mode 100644 index 00000000000..a28271eac79 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/InspiredIdea.java @@ -0,0 +1,39 @@ +package mage.cards.i; + +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.continuous.MaximumHandSizeControllerEffect; +import mage.abilities.keyword.CleaveAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class InspiredIdea extends CardImpl { + + public InspiredIdea(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{U}"); + + // Cleave {3}{U}{U} + this.addAbility(new CleaveAbility(this, new DrawCardSourceControllerEffect(3), "{3}{U}{U}")); + + // Draw three cards. [Your maximum hand size is reduced by three for the rest of the game.] + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(3)); + this.getSpellAbility().addEffect(new MaximumHandSizeControllerEffect( + 3, Duration.EndOfGame, MaximumHandSizeControllerEffect.HandSizeModification.REDUCE + ).setText("[Your maximum hand size is reduced by three for the rest of the game.]")); + } + + private InspiredIdea(final InspiredIdea card) { + super(card); + } + + @Override + public InspiredIdea copy() { + return new InspiredIdea(this); + } +} diff --git a/Mage.Sets/src/mage/cards/i/InspiredSprite.java b/Mage.Sets/src/mage/cards/i/InspiredSprite.java index be6fdb24fac..1363a2fd650 100644 --- a/Mage.Sets/src/mage/cards/i/InspiredSprite.java +++ b/Mage.Sets/src/mage/cards/i/InspiredSprite.java @@ -23,7 +23,7 @@ import mage.filter.FilterSpell; */ public final class InspiredSprite extends CardImpl { - private static final FilterSpell filter = new FilterSpell("Wizard"); + private static final FilterSpell filter = new FilterSpell("a Wizard spell"); static { filter.add(SubType.WIZARD.getPredicate()); diff --git a/Mage.Sets/src/mage/cards/i/InspiringCall.java b/Mage.Sets/src/mage/cards/i/InspiringCall.java index c94b09b5601..893d76ca21f 100644 --- a/Mage.Sets/src/mage/cards/i/InspiringCall.java +++ b/Mage.Sets/src/mage/cards/i/InspiringCall.java @@ -11,8 +11,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.counters.CounterType; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; /** * @@ -20,19 +19,18 @@ import mage.filter.common.FilterControlledCreaturePermanent; */ public final class InspiringCall extends CardImpl { - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("creature you control with a +1/+1 counter on it"); - - static { - filter.add(CounterType.P1P1.getPredicate()); - } - public InspiringCall(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{G}"); - // Draw a card for each creature you control with a +1/+1 counter on it. Those creatures gain indestructible until end of turn. (Damage and effects that say "destroy" don't destroy them.) - this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(new PermanentsOnBattlefieldCount(filter))); - Effect effect = new GainAbilityControlledEffect(IndestructibleAbility.getInstance(), Duration.EndOfTurn, filter); - effect.setText("Those creatures gain indestructible until end of turn. (Damage and effects that say \"destroy\" don't destroy them.)"); + // Draw a card for each creature you control with a +1/+1 counter on it. + // Those creatures gain indestructible until end of turn. + // (Damage and effects that say "destroy" don't destroy them.) + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect( + new PermanentsOnBattlefieldCount(StaticFilters.FILTER_CONTROLLED_CREATURE_P1P1))); + Effect effect = new GainAbilityControlledEffect( + IndestructibleAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_CONTROLLED_CREATURE_P1P1); + effect.setText("Those creatures gain indestructible until end of turn. " + + "(Damage and effects that say \"destroy\" don't destroy them.)"); this.getSpellAbility().addEffect(effect); } diff --git a/Mage.Sets/src/mage/cards/i/InspiringStatuary.java b/Mage.Sets/src/mage/cards/i/InspiringStatuary.java index 12e8bcda085..33270464a97 100644 --- a/Mage.Sets/src/mage/cards/i/InspiringStatuary.java +++ b/Mage.Sets/src/mage/cards/i/InspiringStatuary.java @@ -17,7 +17,7 @@ import java.util.UUID; */ public final class InspiringStatuary extends CardImpl { - private static final FilterCard filter = new FilterCard("non-artifact spells you cast"); + private static final FilterCard filter = new FilterCard("nonartifact spells"); static { filter.add(Predicates.not(CardType.ARTIFACT.getPredicate())); diff --git a/Mage.Sets/src/mage/cards/i/InstigatorGang.java b/Mage.Sets/src/mage/cards/i/InstigatorGang.java index adbbb4ac482..a88e96462f2 100644 --- a/Mage.Sets/src/mage/cards/i/InstigatorGang.java +++ b/Mage.Sets/src/mage/cards/i/InstigatorGang.java @@ -24,7 +24,6 @@ public final class InstigatorGang extends CardImpl { this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.WEREWOLF); - this.transformable = true; this.secondSideCardClazz = mage.cards.w.WildbloodPack.class; this.power = new MageInt(2); diff --git a/Mage.Sets/src/mage/cards/i/Insurrection.java b/Mage.Sets/src/mage/cards/i/Insurrection.java index f1ce3e979f4..295eb5fac19 100644 --- a/Mage.Sets/src/mage/cards/i/Insurrection.java +++ b/Mage.Sets/src/mage/cards/i/Insurrection.java @@ -1,4 +1,3 @@ - package mage.cards.i; import java.util.UUID; @@ -24,8 +23,7 @@ import mage.target.targetpointer.FixedTarget; public final class Insurrection extends CardImpl { public Insurrection(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{5}{R}{R}{R}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{5}{R}{R}{R}"); // Untap all creatures and gain control of them until end of turn. They gain haste until end of turn. this.getSpellAbility().addEffect(new InsurrectionEffect()); @@ -64,8 +62,8 @@ class InsurrectionEffect extends OneShotEffect { ContinuousEffect gainHaste = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn); for (Permanent creature : game.getBattlefield().getAllActivePermanents(CardType.CREATURE, game)) { creature.untap(game); - gainControl.setTargetPointer(new FixedTarget(creature.getId())); - gainHaste.setTargetPointer(new FixedTarget(creature.getId())); + gainControl.setTargetPointer(new FixedTarget(creature.getId(), game)); + gainHaste.setTargetPointer(new FixedTarget(creature.getId(), game)); game.addEffect(gainControl, source); game.addEffect(gainHaste, source); result = true; diff --git a/Mage.Sets/src/mage/cards/i/IntercessorsArrest.java b/Mage.Sets/src/mage/cards/i/IntercessorsArrest.java new file mode 100644 index 00000000000..f3c78031a52 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/IntercessorsArrest.java @@ -0,0 +1,97 @@ +package mage.cards.i; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.combat.CantAttackBlockAttachedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; + +import java.util.Optional; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class IntercessorsArrest extends CardImpl { + + public IntercessorsArrest(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); + + this.subtype.add(SubType.AURA); + + // Enchant permanent + TargetPermanent auraTarget = new TargetPermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + this.addAbility(new EnchantAbility(auraTarget.getTargetName())); + + // Enchanted permanent can't attack, block, or crew Vehicles. Its activated abilities can't be activated unless they're mana abilities. + Ability ability = new SimpleStaticAbility(new CantAttackBlockAttachedEffect(AttachmentType.AURA) + .setText("Enchanted permanent can't attack, block")); + ability.addEffect(new IntercessorsArrestEffect()); + this.addAbility(ability); + } + + private IntercessorsArrest(final IntercessorsArrest card) { + super(card); + } + + @Override + public IntercessorsArrest copy() { + return new IntercessorsArrest(this); + } +} + +class IntercessorsArrestEffect extends ContinuousRuleModifyingEffectImpl { + + public IntercessorsArrestEffect() { + super(Duration.WhileOnBattlefield, Outcome.Detriment); + staticText = ", or crew Vehicles. Its activated abilities can't be activated unless they're mana abilities"; + } + + private IntercessorsArrestEffect(final IntercessorsArrestEffect effect) { + super(effect); + } + + @Override + public IntercessorsArrestEffect copy() { + return new IntercessorsArrestEffect(this); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + switch (event.getType()) { + case CREW_VEHICLE: + case ACTIVATE_ABILITY: + return true; + default: + return false; + } + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + Permanent enchantment = game.getPermanent(source.getSourceId()); + if (enchantment == null) { + return false; + } + switch (event.getType()) { + case CREW_VEHICLE: + return enchantment.isAttachedTo(event.getTargetId()); + case ACTIVATE_ABILITY: + if (enchantment.isAttachedTo(event.getSourceId())) { + Optional ability = game.getAbility(event.getTargetId(), event.getSourceId()); + return ability.isPresent() && ability.get().getAbilityType() != AbilityType.MANA; + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/i/InterventionPact.java b/Mage.Sets/src/mage/cards/i/InterventionPact.java index 70fd9908468..e8acc4964bc 100644 --- a/Mage.Sets/src/mage/cards/i/InterventionPact.java +++ b/Mage.Sets/src/mage/cards/i/InterventionPact.java @@ -1,4 +1,3 @@ - package mage.cards.i; import java.util.UUID; @@ -29,10 +28,10 @@ import mage.target.targetpointer.FixedTarget; public final class InterventionPact extends CardImpl { public InterventionPact(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{0}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{0}"); this.color.setWhite(true); - + // The next time a source of your choice would deal damage to you this turn, prevent that damage. You gain life equal to the damage prevented this way. this.getSpellAbility().addEffect(new InterventionPactEffect()); // At the beginning of your next upkeep, pay {1}{W}{W}. If you don't, you lose the game. @@ -74,7 +73,7 @@ class InterventionPactEffect extends OneShotEffect { target.setNotTarget(true); if (controller.chooseTarget(outcome, target, source, game)) { ContinuousEffect continuousEffect = new InterventionPactPreventDamageEffect(); - continuousEffect.setTargetPointer(new FixedTarget(target.getFirstTarget())); + continuousEffect.setTargetPointer(new FixedTarget(target.getFirstTarget(), game)); game.addEffect(continuousEffect, source); } return true; @@ -109,8 +108,8 @@ class InterventionPactPreventDamageEffect extends PreventionEffectImpl { PreventionEffectData preventEffectData = preventDamageAction(event, source, game); if (preventEffectData.getPreventedDamage() > 0) { used = true; - Player player = game .getPlayer(source.getControllerId()); - if(player != null){ + Player player = game.getPlayer(source.getControllerId()); + if (player != null) { player.gainLife(preventEffectData.getPreventedDamage(), game, source); } } diff --git a/Mage.Sets/src/mage/cards/i/IntoTheNight.java b/Mage.Sets/src/mage/cards/i/IntoTheNight.java new file mode 100644 index 00000000000..e12fc074898 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/IntoTheNight.java @@ -0,0 +1,64 @@ +package mage.cards.i; + +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.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class IntoTheNight extends CardImpl { + + public IntoTheNight(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{R}"); + + // It becomes night. Discard any number of cards, then draw that many cards plus one. + this.getSpellAbility().addEffect(new IntoTheNightEffect()); + } + + private IntoTheNight(final IntoTheNight card) { + super(card); + } + + @Override + public IntoTheNight copy() { + return new IntoTheNight(this); + } +} + +class IntoTheNightEffect extends OneShotEffect { + + IntoTheNightEffect() { + super(Outcome.Benefit); + staticText = "it becomes night. Discard any number of cards, then draw that many cards plus one"; + } + + private IntoTheNightEffect(final IntoTheNightEffect effect) { + super(effect); + } + + @Override + public IntoTheNightEffect copy() { + return new IntoTheNightEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + game.setDaytime(false); + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + player.drawCards(player.discard( + 0, Integer.MAX_VALUE, false, source, game + ).size() + 1, source, game); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/i/IntruderAlarm.java b/Mage.Sets/src/mage/cards/i/IntruderAlarm.java index 4197c14366d..c2243e5d4b6 100644 --- a/Mage.Sets/src/mage/cards/i/IntruderAlarm.java +++ b/Mage.Sets/src/mage/cards/i/IntruderAlarm.java @@ -13,7 +13,7 @@ import mage.constants.Duration; import mage.constants.TargetController; import mage.constants.Zone; import static mage.filter.StaticFilters.FILTER_PERMANENT_CREATURES; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; /** * @@ -27,7 +27,7 @@ public final class IntruderAlarm extends CardImpl { // Creatures don't untap during their controllers' untap steps. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DontUntapInControllersUntapStepAllEffect(Duration.WhileOnBattlefield, TargetController.ANY, FILTER_PERMANENT_CREATURES))); // Whenever a creature enters the battlefield, untap all creatures. - this.addAbility(new EntersBattlefieldAllTriggeredAbility(new UntapAllEffect(FILTER_PERMANENT_CREATURES), new FilterCreaturePermanent("a creature"))); + this.addAbility(new EntersBattlefieldAllTriggeredAbility(new UntapAllEffect(FILTER_PERMANENT_CREATURES), StaticFilters.FILTER_PERMANENT_A_CREATURE)); } private IntruderAlarm(final IntruderAlarm card) { diff --git a/Mage.Sets/src/mage/cards/i/IntrusivePackbeast.java b/Mage.Sets/src/mage/cards/i/IntrusivePackbeast.java index 72d5cb9d373..9221a1d3050 100644 --- a/Mage.Sets/src/mage/cards/i/IntrusivePackbeast.java +++ b/Mage.Sets/src/mage/cards/i/IntrusivePackbeast.java @@ -29,7 +29,7 @@ public final class IntrusivePackbeast extends CardImpl { this.addAbility(VigilanceAbility.getInstance()); // When Intrusive Packbeast enters the battlefield, tap up to two target creatures your opponents control. - Ability ability = new EntersBattlefieldTriggeredAbility(new TapTargetEffect()); + Ability ability = new EntersBattlefieldTriggeredAbility(new TapTargetEffect().setText("tap up to two target creatures your opponents control")); ability.addTarget(new TargetOpponentsCreaturePermanent(0, 2)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/i/InvasionOfTheGiants.java b/Mage.Sets/src/mage/cards/i/InvasionOfTheGiants.java index dbbe8f22a4a..e9b999f23df 100644 --- a/Mage.Sets/src/mage/cards/i/InvasionOfTheGiants.java +++ b/Mage.Sets/src/mage/cards/i/InvasionOfTheGiants.java @@ -44,7 +44,7 @@ public final class InvasionOfTheGiants extends CardImpl { this.subtype.add(SubType.SAGA); // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) - SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); + SagaAbility sagaAbility = new SagaAbility(this); // I — Scry 2. sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, new ScryEffect(2)); @@ -149,7 +149,7 @@ class InvasionOfTheGiantsWatcher extends Watcher { } Spell spell = game.getSpell(event.getSourceId()); if (spell != null && spell.hasSubtype(SubType.GIANT, game)) { - playerMap.compute(event.getPlayerId(), (u, i) -> i == null ? 1 : Integer.sum(i, 1)); + playerMap.compute(event.getPlayerId(), CardUtil::setOrIncrementValue); } } diff --git a/Mage.Sets/src/mage/cards/i/InventiveIteration.java b/Mage.Sets/src/mage/cards/i/InventiveIteration.java new file mode 100644 index 00000000000..dcbcf504cd1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/InventiveIteration.java @@ -0,0 +1,104 @@ +package mage.cards.i; + +import mage.abilities.Ability; +import mage.abilities.common.SagaAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ExileSagaAndReturnTransformedEffect; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.abilities.keyword.TransformAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInYourGraveyard; +import mage.target.common.TargetCreatureOrPlaneswalker; +import mage.util.RandomUtil; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class InventiveIteration extends CardImpl { + + public InventiveIteration(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{U}"); + + this.subtype.add(SubType.SAGA); + this.secondSideCardClazz = mage.cards.l.LivingBreakthrough.class; + + // (As this Saga enters and after your draw step, add a lore counter.) + SagaAbility sagaAbility = new SagaAbility(this); + + // I — Return up to one target creature or planeswalker to its owner's hand. + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_I, + new ReturnToHandTargetEffect(), new TargetCreatureOrPlaneswalker(0, 1) + ); + + // II — Return an artifact card from your graveyard to your hand. If you can't, draw a card. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_II, new InventiveIterationEffect()); + + // III — Exile this Saga, then return it to the battlefield transformed under your control. + this.addAbility(new TransformAbility()); + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, new ExileSagaAndReturnTransformedEffect()); + + this.addAbility(sagaAbility); + } + + private InventiveIteration(final InventiveIteration card) { + super(card); + } + + @Override + public InventiveIteration copy() { + return new InventiveIteration(this); + } +} + +class InventiveIterationEffect extends OneShotEffect { + + InventiveIterationEffect() { + super(Outcome.Benefit); + staticText = "return an artifact card from your graveyard to your hand. If you can't, draw a card"; + } + + private InventiveIterationEffect(final InventiveIterationEffect effect) { + super(effect); + } + + @Override + public InventiveIterationEffect copy() { + return new InventiveIterationEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Card card; + switch (player.getGraveyard().count(StaticFilters.FILTER_CARD_ARTIFACT, game)) { + case 0: + player.drawCards(1, source, game); + return true; + case 1: + card = RandomUtil.randomFromCollection( + player.getGraveyard().getCards(StaticFilters.FILTER_CARD_ARTIFACT, game) + ); + break; + default: + TargetCard target = new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_ARTIFACT); + target.setNotTarget(true); + player.choose(outcome, player.getGraveyard(), target, game); + card = game.getCard(target.getFirstTarget()); + } + player.moveCards(card, Zone.HAND, source, game); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/i/InventorsGoggles.java b/Mage.Sets/src/mage/cards/i/InventorsGoggles.java index 0f0b6fb10df..345b171287c 100644 --- a/Mage.Sets/src/mage/cards/i/InventorsGoggles.java +++ b/Mage.Sets/src/mage/cards/i/InventorsGoggles.java @@ -1,7 +1,5 @@ - package mage.cards.i; -import java.util.UUID; import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.GenericManaCost; @@ -13,27 +11,27 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.FilterPermanent; +import java.util.UUID; + /** - * * @author emerald000 */ public final class InventorsGoggles extends CardImpl { + private static final FilterPermanent filter = new FilterPermanent(SubType.ARTIFICER, "an Artificer"); + public InventorsGoggles(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{1}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}"); this.subtype.add(SubType.EQUIPMENT); // Equipped creature gets +1/+2. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(1, 2, Duration.WhileOnBattlefield))); + this.addAbility(new SimpleStaticAbility(new BoostEquippedEffect(1, 2, Duration.WhileOnBattlefield))); // Whenever an Artificer enters the battlefield under your control, you may attach Inventor's Goggles to it. this.addAbility(new EntersBattlefieldControlledTriggeredAbility( - Zone.BATTLEFIELD, - new AttachEffect(Outcome.BoostCreature, "attach {this} to it"), - new FilterPermanent(SubType.ARTIFICER, "Artificer"), - true, - SetTargetPointer.PERMANENT, - null)); + Zone.BATTLEFIELD, new AttachEffect(Outcome.BoostCreature, "attach {this} to it"), + filter, true, SetTargetPointer.PERMANENT, null + )); // Equip {2} this.addAbility(new EquipAbility(Outcome.BoostCreature, new GenericManaCost(2))); diff --git a/Mage.Sets/src/mage/cards/i/InvertInvent.java b/Mage.Sets/src/mage/cards/i/InvertInvent.java index ae03c672ceb..7b6c644dd38 100644 --- a/Mage.Sets/src/mage/cards/i/InvertInvent.java +++ b/Mage.Sets/src/mage/cards/i/InvertInvent.java @@ -35,7 +35,7 @@ public final class InvertInvent extends SplitCard { // Invent // Search your library for an instant card and/or a sorcery card, reveal them, put them into your hand, then shuffle your library. - this.getRightHalfCard().getSpellAbility().addEffect(new SearchLibraryPutInHandEffect(new InventTarget(), true)); + this.getRightHalfCard().getSpellAbility().addEffect(new SearchLibraryPutInHandEffect(new InventTarget(), true).setText("search your library for an instant card and/or a sorcery card, reveal them, put them into your hand, then shuffle")); } private InvertInvent(final InvertInvent card) { diff --git a/Mage.Sets/src/mage/cards/i/InvestigatorsJournal.java b/Mage.Sets/src/mage/cards/i/InvestigatorsJournal.java new file mode 100644 index 00000000000..3f1f730870e --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/InvestigatorsJournal.java @@ -0,0 +1,105 @@ +package mage.cards.i; + +import java.util.HashMap; +import java.util.UUID; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.RemoveCountersSourceCost; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * + * @author weirddan455 + */ +public final class InvestigatorsJournal extends CardImpl { + + public InvestigatorsJournal(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); + + this.subtype.add(SubType.CLUE); + + // Investigator's Journal enters the battlefield with a number of suspect counters on it equal to the greatest number of creatures a player controls. + this.addAbility(new EntersBattlefieldAbility( + new AddCountersSourceEffect(CounterType.SUSPECT.createInstance(), InvestigatorsJournalValue.instance, false), + "with a number of suspect counters on it equal to the greatest number of creatures a player controls" + )); + + // {2}, {T}, Remove a suspect counter from Investigator's Journal: Draw a card. + Ability ability = new SimpleActivatedAbility(new DrawCardSourceControllerEffect(1), new GenericManaCost(2)); + ability.addCost(new TapSourceCost()); + ability.addCost(new RemoveCountersSourceCost(CounterType.SUSPECT.createInstance())); + this.addAbility(ability); + + // {2}, Sacrifice Investigator's Journal: Draw a card. + ability = new SimpleActivatedAbility(new DrawCardSourceControllerEffect(1), new GenericManaCost(2)); + ability.addCost(new SacrificeSourceCost()); + this.addAbility(ability); + } + + private InvestigatorsJournal(final InvestigatorsJournal card) { + super(card); + } + + @Override + public InvestigatorsJournal copy() { + return new InvestigatorsJournal(this); + } +} + +enum InvestigatorsJournalValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + HashMap creatureCounts = new HashMap<>(); + for (UUID playerId : game.getState().getPlayersInRange(sourceAbility.getControllerId(), game)) { + creatureCounts.put(playerId, 0); + } + for (Permanent permanent : game.getBattlefield().getAllPermanents()) { + if (permanent.isPhasedIn() && permanent.isCreature(game)) { + UUID controllerId = permanent.getControllerId(); + Integer count = creatureCounts.get(controllerId); + if (count != null) { + creatureCounts.put(controllerId, count + 1); + } + } + } + int greatestCreatureCount = 0; + for (Integer count : creatureCounts.values()) { + if (count > greatestCreatureCount) { + greatestCreatureCount = count; + } + } + return greatestCreatureCount; + } + + @Override + public InvestigatorsJournalValue copy() { + return instance; + } + + @Override + public String toString() { + return "X"; + } + + @Override + public String getMessage() { + return "greatest number of creatures a player controls"; + } +} diff --git a/Mage.Sets/src/mage/cards/i/InvigoratingHotSpring.java b/Mage.Sets/src/mage/cards/i/InvigoratingHotSpring.java new file mode 100644 index 00000000000..25d44dca879 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/InvigoratingHotSpring.java @@ -0,0 +1,68 @@ +package mage.cards.i; + +import mage.abilities.ActivatedAbility; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.common.LimitedTimesPerTurnActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.RemoveCountersSourceCost; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.TimingRule; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.permanent.ModifiedPredicate; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class InvigoratingHotSpring extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledCreaturePermanent("modified creatures"); + + static { + filter.add(ModifiedPredicate.instance); + } + + public InvigoratingHotSpring(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{R}{G}"); + + // Invigorating Hot Spring enters the battlefield with four +1/+1 counters on it. + this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect( + CounterType.P1P1.createInstance(4) + ), "with four +1/+1 counters on it")); + + // Modified creatures you control have haste. + this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( + HasteAbility.getInstance(), Duration.WhileOnBattlefield, filter + ))); + + // Remove a +1/+1 counter from Invigoration Hot Springs: Put a +1/+1 counter on target creature you control. Activate only as a sorcery and only once each turn. + ActivatedAbility ability = new LimitedTimesPerTurnActivatedAbility( + Zone.BATTLEFIELD, new AddCountersTargetEffect(CounterType.P1P1.createInstance()), + new RemoveCountersSourceCost(CounterType.P1P1.createInstance()) + ); + ability.addTarget(new TargetControlledCreaturePermanent()); + ability.setTiming(TimingRule.SORCERY); + this.addAbility(ability); + } + + private InvigoratingHotSpring(final InvigoratingHotSpring card) { + super(card); + } + + @Override + public InvigoratingHotSpring copy() { + return new InvigoratingHotSpring(this); + } +} diff --git a/Mage.Sets/src/mage/cards/i/InvokeDespair.java b/Mage.Sets/src/mage/cards/i/InvokeDespair.java new file mode 100644 index 00000000000..edfc786b31c --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/InvokeDespair.java @@ -0,0 +1,108 @@ +package mage.cards.i; + +import java.util.UUID; + +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.filter.StaticFilters; +import mage.filter.common.FilterControlledEnchantmentPermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.Target; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetControlledPermanent; +import mage.target.common.TargetOpponent; + +/** + * + * @author weirddan455 + */ +public final class InvokeDespair extends CardImpl { + + public InvokeDespair(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}{B}{B}{B}"); + + // Target opponent sacrifices a creature. If they can't, they lose 2 life and you draw a card. Then repeat this process for an enchantment and a planeswalker. + this.getSpellAbility().addEffect(new InvokeDespairEffect()); + this.getSpellAbility().addTarget(new TargetOpponent()); + } + + private InvokeDespair(final InvokeDespair card) { + super(card); + } + + @Override + public InvokeDespair copy() { + return new InvokeDespair(this); + } +} + +class InvokeDespairEffect extends OneShotEffect { + + private static final FilterControlledEnchantmentPermanent enchantmentFilter = new FilterControlledEnchantmentPermanent(); + + public InvokeDespairEffect() { + super(Outcome.Sacrifice); + this.staticText = "Target opponent sacrifices a creature. If they can't, they lose 2 life and you draw a card. Then repeat this process for an enchantment and a planeswalker"; + } + + private InvokeDespairEffect(final InvokeDespairEffect effect) { + super(effect); + } + + @Override + public InvokeDespairEffect copy() { + return new InvokeDespairEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player opponent = game.getPlayer(source.getFirstTarget()); + Player controller = game.getPlayer(source.getControllerId()); + if (opponent == null || controller == null) { + return false; + } + Target target = new TargetControlledCreaturePermanent(); + target.setNotTarget(true); + opponent.choose(outcome, target, source.getSourceId(), game); + Permanent permanent = game.getPermanent(target.getFirstTarget()); + boolean sacrificed = false; + if (permanent != null) { + sacrificed = permanent.sacrifice(source, game); + } + if (!sacrificed) { + opponent.loseLife(2, game, source, false); + controller.drawCards(1, source, game); + } + target = new TargetControlledPermanent(enchantmentFilter); + target.setNotTarget(true); + opponent.choose(outcome, target, source.getSourceId(), game); + permanent = game.getPermanent(target.getFirstTarget()); + sacrificed = false; + if (permanent != null) { + sacrificed = permanent.sacrifice(source, game); + } + if (!sacrificed) { + opponent.loseLife(2, game, source, false); + controller.drawCards(1, source, game); + } + target = new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_PLANESWALKER); + target.setNotTarget(true); + opponent.choose(outcome, target, source.getSourceId(), game); + permanent = game.getPermanent(target.getFirstTarget()); + sacrificed = false; + if (permanent != null) { + sacrificed = permanent.sacrifice(source, game); + } + if (!sacrificed) { + opponent.loseLife(2, game, source, false); + controller.drawCards(1, source, game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/i/InvokeJustice.java b/Mage.Sets/src/mage/cards/i/InvokeJustice.java new file mode 100644 index 00000000000..de8a604f78e --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/InvokeJustice.java @@ -0,0 +1,96 @@ +package mage.cards.i; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.counters.CounterType; +import mage.filter.FilterCard; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterPermanentCard; +import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetAmount; +import mage.target.TargetPlayer; +import mage.target.common.TargetCardInYourGraveyard; +import mage.target.common.TargetPermanentAmount; +import mage.target.targetpointer.SecondTargetPointer; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class InvokeJustice extends CardImpl { + + private static final FilterCard filter = new FilterPermanentCard("permanent card from your graveyard"); + + public InvokeJustice(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{W}{W}{W}{W}"); + + // Return target permanent card from your graveyard to the battlefield, then distribute four +1/+1 counters among any number of creatures and/or Vehicles target player controls. + this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect()); + this.getSpellAbility().addEffect(new InvokeJusticeEffect()); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(filter)); + this.getSpellAbility().addTarget(new TargetPlayer()); + } + + private InvokeJustice(final InvokeJustice card) { + super(card); + } + + @Override + public InvokeJustice copy() { + return new InvokeJustice(this); + } +} + +class InvokeJusticeEffect extends OneShotEffect { + + InvokeJusticeEffect() { + super(Outcome.Benefit); + staticText = ", then distribute four +1/+1 counters among " + + "any number of creatures and/or Vehicles target player controls"; + this.setTargetPointer(new SecondTargetPointer()); + } + + private InvokeJusticeEffect(final InvokeJusticeEffect effect) { + super(effect); + } + + @Override + public InvokeJusticeEffect copy() { + return new InvokeJusticeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Player player = game.getPlayer(getTargetPointer().getFirst(game, source)); + if (controller == null || player == null) { + return false; + } + FilterPermanent filter = new FilterPermanent( + "creatures and/or Vehicles controlled by " + player.getName() + ); + filter.add(new ControllerIdPredicate(player.getId())); + if (!game.getBattlefield().contains(filter, source, game, 1)) { + return false; + } + TargetAmount target = new TargetPermanentAmount(4, filter); + target.setNotTarget(true); + controller.choose(outcome, target, source.getSourceId(), game); + for (UUID targetId : target.getTargets()) { + Permanent permanent = game.getPermanent(targetId); + if (permanent != null) { + permanent.addCounters(CounterType.P1P1.createInstance(target.getTargetAmount(targetId)), source, game); + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/i/InvokeTheAncients.java b/Mage.Sets/src/mage/cards/i/InvokeTheAncients.java new file mode 100644 index 00000000000..0ea1288588f --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/InvokeTheAncients.java @@ -0,0 +1,93 @@ +package mage.cards.i; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.choices.Choice; +import mage.choices.ChoiceImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.SpiritGreenToken; +import mage.game.permanent.token.Token; +import mage.players.Player; + +import java.util.Arrays; +import java.util.Locale; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class InvokeTheAncients extends CardImpl { + + public InvokeTheAncients(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{G}{G}{G}{G}"); + + // Create two 4/5 green Spirit creature tokens. For each of them, put your choice of a vigilance counter, a reach counter, or a trample counter on it. + this.getSpellAbility().addEffect(new InvokeTheAncientsEffect()); + } + + private InvokeTheAncients(final InvokeTheAncients card) { + super(card); + } + + @Override + public InvokeTheAncients copy() { + return new InvokeTheAncients(this); + } +} + +class InvokeTheAncientsEffect extends OneShotEffect { + + private static final Token token = new SpiritGreenToken(); + private static final Set choices = Arrays.asList( + "Vigilance", "Reach", "Trample" + ).stream().collect(Collectors.toSet()); + + InvokeTheAncientsEffect() { + super(Outcome.Benefit); + staticText = "create two 4/5 green Spirit creature tokens. For each of them, " + + "put your choice of a vigilance counter, a reach counter, or a trample counter on it"; + } + + private InvokeTheAncientsEffect(final InvokeTheAncientsEffect effect) { + super(effect); + } + + @Override + public InvokeTheAncientsEffect copy() { + return new InvokeTheAncientsEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + token.putOntoBattlefield(2, game, source, source.getControllerId()); + for (UUID tokenId : token.getLastAddedTokenIds()) { + Permanent permanent = game.getPermanent(tokenId); + if (permanent == null) { + continue; + } + Choice choice = new ChoiceImpl(true); + choice.setMessage("Choose vigilance, reach, or trample counter"); + choice.setChoices(choices); + player.choose(outcome, choice, game); + String chosen = choice.getChoice(); + if (chosen != null) { + permanent.addCounters(CounterType.findByName( + chosen.toLowerCase(Locale.ENGLISH) + ).createInstance(), source.getControllerId(), source, game); + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/i/InvokeTheWinds.java b/Mage.Sets/src/mage/cards/i/InvokeTheWinds.java new file mode 100644 index 00000000000..14da21550fb --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/InvokeTheWinds.java @@ -0,0 +1,36 @@ +package mage.cards.i; + +import mage.abilities.effects.common.UntapTargetEffect; +import mage.abilities.effects.common.continuous.GainControlTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class InvokeTheWinds extends CardImpl { + + public InvokeTheWinds(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{U}{U}{U}{U}"); + + // Gain control of target artifact or creature. Untap it. + this.getSpellAbility().addEffect(new GainControlTargetEffect(Duration.Custom)); + this.getSpellAbility().addEffect(new UntapTargetEffect().setText("Untap it")); + this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_CREATURE)); + } + + private InvokeTheWinds(final InvokeTheWinds card) { + super(card); + } + + @Override + public InvokeTheWinds copy() { + return new InvokeTheWinds(this); + } +} diff --git a/Mage.Sets/src/mage/cards/i/IreShaman.java b/Mage.Sets/src/mage/cards/i/IreShaman.java index ca3203f512b..eca51966108 100644 --- a/Mage.Sets/src/mage/cards/i/IreShaman.java +++ b/Mage.Sets/src/mage/cards/i/IreShaman.java @@ -3,26 +3,15 @@ package mage.cards.i; import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.TurnedFaceUpSourceTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.AsThoughEffectImpl; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ExileTopXMayPlayUntilEndOfTurnEffect; import mage.abilities.keyword.MenaceAbility; import mage.abilities.keyword.MorphAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AsThoughEffectType; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.targetpointer.FixedTarget; /** * @@ -44,7 +33,7 @@ public final class IreShaman extends CardImpl { this.addAbility(new MorphAbility(this, new ManaCostsImpl("{R}"), true)); // When Ire Shaman is turned face up, exile the top card of your library. Until end of turn, you may play that card. - this.addAbility(new TurnedFaceUpSourceTriggeredAbility(new IreShamanExileEffect(), false)); + this.addAbility(new TurnedFaceUpSourceTriggeredAbility(new ExileTopXMayPlayUntilEndOfTurnEffect(1), false)); } private IreShaman(final IreShaman card) { @@ -56,66 +45,3 @@ public final class IreShaman extends CardImpl { return new IreShaman(this); } } - -class IreShamanExileEffect extends OneShotEffect { - - public IreShamanExileEffect() { - super(Outcome.Detriment); - this.staticText = "exile the top card of your library. Until end of turn, you may play that card"; - } - - public IreShamanExileEffect(final IreShamanExileEffect effect) { - super(effect); - } - - @Override - public IreShamanExileEffect copy() { - return new IreShamanExileEffect(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()) { - Card card = controller.getLibrary().getFromTop(game); - if (card != null) { - String exileName = sourcePermanent.getIdName() + " "; - controller.moveCardsToExile(card, source, game, true, source.getSourceId(), exileName); - ContinuousEffect effect = new IreShamanCastFromExileEffect(); - effect.setTargetPointer(new FixedTarget(card.getId())); - game.addEffect(effect, source); - } - return true; - } - return false; - } -} - -class IreShamanCastFromExileEffect extends AsThoughEffectImpl { - - public IreShamanCastFromExileEffect() { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit); - staticText = "You may play the card from exile"; - } - - public IreShamanCastFromExileEffect(final IreShamanCastFromExileEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public IreShamanCastFromExileEffect copy() { - return new IreShamanCastFromExileEffect(this); - } - - @Override - public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - return source.isControlledBy(affectedControllerId) - && objectId.equals(getTargetPointer().getFirst(game, source)); - } -} diff --git a/Mage.Sets/src/mage/cards/i/IridescentHornbeetle.java b/Mage.Sets/src/mage/cards/i/IridescentHornbeetle.java index 7f5cfa5852c..bc0f62c5eee 100644 --- a/Mage.Sets/src/mage/cards/i/IridescentHornbeetle.java +++ b/Mage.Sets/src/mage/cards/i/IridescentHornbeetle.java @@ -16,6 +16,7 @@ import mage.counters.CounterType; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.token.InsectToken; +import mage.util.CardUtil; import mage.watchers.Watcher; import java.util.HashMap; @@ -92,7 +93,7 @@ class IridescentHornbeetleWatcher extends Watcher { || !event.getData().equals(CounterType.P1P1.getName())) { return; } - playerMap.compute(event.getPlayerId(), (u, i) -> i == null ? 1 : Integer.sum(i, 1)); + playerMap.compute(event.getPlayerId(), CardUtil::setOrIncrementValue); } @Override diff --git a/Mage.Sets/src/mage/cards/i/IroasGodOfVictory.java b/Mage.Sets/src/mage/cards/i/IroasGodOfVictory.java index 9c43c4721e3..dcd63420397 100644 --- a/Mage.Sets/src/mage/cards/i/IroasGodOfVictory.java +++ b/Mage.Sets/src/mage/cards/i/IroasGodOfVictory.java @@ -50,7 +50,7 @@ public final class IroasGodOfVictory extends CardImpl { // Creatures you control have menace. (They can't be blocked except by two or more creatures.) this.addAbility(new SimpleStaticAbility( - new GainAbilityAllEffect(new MenaceAbility(), Duration.WhileOnBattlefield, filter) + new GainAbilityAllEffect(new MenaceAbility(false), Duration.WhileOnBattlefield, filter) )); // Prevent all damage that would be dealt to attacking creatures you control. diff --git a/Mage.Sets/src/mage/cards/i/IronApprentice.java b/Mage.Sets/src/mage/cards/i/IronApprentice.java new file mode 100644 index 00000000000..fbc8996e263 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/IronApprentice.java @@ -0,0 +1,105 @@ +package mage.cards.i; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.counters.Counter; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class IronApprentice extends CardImpl { + + public IronApprentice(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{1}"); + + this.subtype.add(SubType.CONSTRUCT); + this.power = new MageInt(0); + this.toughness = new MageInt(0); + + // Iron Apprentice enters the battlefield with a +1/+1 counter on it. + this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect( + CounterType.P1P1.createInstance(1) + ), "with a +1/+1 counter on it")); + + // When Iron Apprentice dies, if it had counters on it, put those counters on target creature you control. + Ability ability = new ConditionalTriggeredAbility( + new DiesSourceTriggeredAbility(new IronApprenticeEffect()), IronApprenticeCondition.instance, + "When {this} dies, if it had counters on it, put those counters on target creature you control." + ); + ability.addTarget(new TargetControlledCreaturePermanent()); + this.addAbility(ability); + } + + private IronApprentice(final IronApprentice card) { + super(card); + } + + @Override + public IronApprentice copy() { + return new IronApprentice(this); + } +} + +enum IronApprenticeCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = source.getSourcePermanentOrLKI(game); + return permanent != null && permanent + .getCounters(game) + .values() + .stream() + .mapToInt(Counter::getCount) + .anyMatch(x -> x > 0); + } +} + +class IronApprenticeEffect extends OneShotEffect { + + IronApprenticeEffect() { + super(Outcome.Benefit); + } + + private IronApprenticeEffect(final IronApprenticeEffect effect) { + super(effect); + } + + @Override + public IronApprenticeEffect copy() { + return new IronApprenticeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = source.getSourcePermanentOrLKI(game); + Permanent creature = game.getPermanent(source.getFirstTarget()); + if (permanent == null || creature == null) { + return false; + } + permanent + .getCounters(game) + .copy() + .values() + .stream() + .forEach(counter -> creature.addCounters(counter, source, game)); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/i/Ironfang.java b/Mage.Sets/src/mage/cards/i/Ironfang.java index 2e287c13515..500ad43411f 100644 --- a/Mage.Sets/src/mage/cards/i/Ironfang.java +++ b/Mage.Sets/src/mage/cards/i/Ironfang.java @@ -22,7 +22,6 @@ public final class Ironfang extends CardImpl { // this card is the second face of double-faced card this.nightCard = true; - this.transformable = true; this.power = new MageInt(3); this.toughness = new MageInt(1); diff --git a/Mage.Sets/src/mage/cards/i/IronhoofBoar.java b/Mage.Sets/src/mage/cards/i/IronhoofBoar.java new file mode 100644 index 00000000000..378ace5d77f --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/IronhoofBoar.java @@ -0,0 +1,51 @@ +package mage.cards.i; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.ChannelAbility; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class IronhoofBoar extends CardImpl { + + public IronhoofBoar(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{5}{R}"); + + this.subtype.add(SubType.BOAR); + this.power = new MageInt(5); + this.toughness = new MageInt(4); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // Channel — {1}{R}, Discard Ironhoof Boar: Target creature gets +3/+1 and gains trample until end of turn. + Ability ability = new ChannelAbility("{1}{R}", new BoostTargetEffect(3, 1) + .setText("target creature gets +3/+1")); + ability.addEffect(new GainAbilityTargetEffect(TrampleAbility.getInstance()) + .setText("and gains trample until end of turn")); + this.addAbility(ability); + } + + private IronhoofBoar(final IronhoofBoar card) { + super(card); + } + + @Override + public IronhoofBoar copy() { + return new IronhoofBoar(this); + } +} diff --git a/Mage.Sets/src/mage/cards/i/IronsoulEnforcer.java b/Mage.Sets/src/mage/cards/i/IronsoulEnforcer.java new file mode 100644 index 00000000000..37a0a57815e --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/IronsoulEnforcer.java @@ -0,0 +1,72 @@ +package mage.cards.i; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksAloneControlledTriggeredAbility; +import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; +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.common.FilterArtifactCard; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.filter.predicate.mageobject.CommanderPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class IronsoulEnforcer extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledPermanent("{this} or a commander you control"); + private static final FilterCard filter2 + = new FilterArtifactCard("artifact card from your graveyard"); + + static { + filter.add(IronsoulEnforcerPredicate.instance); + } + + public IronsoulEnforcer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{4}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SAMURAI); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Whenever Ironsoul Enforcer or a commander you control attacks alone, return target artifact card from your graveyard to the battlefield. + Ability ability = new AttacksAloneControlledTriggeredAbility( + new ReturnFromGraveyardToBattlefieldTargetEffect(), filter, false, false + ); + ability.addTarget(new TargetCardInYourGraveyard(filter2)); + this.addAbility(ability); + } + + private IronsoulEnforcer(final IronsoulEnforcer card) { + super(card); + } + + @Override + public IronsoulEnforcer copy() { + return new IronsoulEnforcer(this); + } +} + +enum IronsoulEnforcerPredicate implements ObjectSourcePlayerPredicate { + instance; + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + return input.getObject().getId().equals(input.getSourceId()) + || CommanderPredicate.instance.apply(input.getObject(), game); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/i/IsarethTheAwakener.java b/Mage.Sets/src/mage/cards/i/IsarethTheAwakener.java index 86b4934d34d..ba436201bb9 100644 --- a/Mage.Sets/src/mage/cards/i/IsarethTheAwakener.java +++ b/Mage.Sets/src/mage/cards/i/IsarethTheAwakener.java @@ -137,7 +137,7 @@ class IsarethTheAwakenerEffect extends OneShotEffect { } Counters countersToAdd = new Counters(); countersToAdd.addCounter(CounterType.CORPSE.createInstance()); - game.setEnterWithCounters(source.getSourceId(), countersToAdd); + game.setEnterWithCounters(card.getId(), countersToAdd); return controller.moveCards(card, Zone.BATTLEFIELD, source, game); } } diff --git a/Mage.Sets/src/mage/cards/i/IshkanahBroodmother.java b/Mage.Sets/src/mage/cards/i/IshkanahBroodmother.java new file mode 100644 index 00000000000..22a9c2fdcf6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/IshkanahBroodmother.java @@ -0,0 +1,85 @@ +package mage.cards.i; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.ExileFromGraveCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DraftFromSpellbookEffect; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.abilities.keyword.ReachAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.StaticFilters; +import mage.filter.common.FilterCreaturePermanent; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class IshkanahBroodmother extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(SubType.SPIDER, "Spiders"); + private static final List spellbook = Collections.unmodifiableList(Arrays.asList( + "Arachnoform", + "Brood Weaver", + "Drider", + // "Glowstone Recluse", mutate card + "Gnottvold Recluse", + "Hatchery Spider", + "Mammoth Spider", + "Netcaster Spider", + "Prey Upon", + "Sentinel Spider", + "Snarespinner", + "Spider Spawning", + "Spidery Grasp", + "Sporecap Spider", + "Twin-Silk Spider" + )); + + public IshkanahBroodmother(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.SPIDER); + this.power = new MageInt(3); + this.toughness = new MageInt(5); + + // Reach + this.addAbility(ReachAbility.getInstance()); + + // Other Spiders you control get +1/+2. + this.addAbility(new SimpleStaticAbility(new BoostControlledEffect( + 1, 2, Duration.WhileOnBattlefield, filter, true + ))); + + // {1}{B/G}, Exile two cards from your graveyard: Draft a card from Ishkanah, Broodmother's spellbook. + Ability ability = new SimpleActivatedAbility( + new DraftFromSpellbookEffect(spellbook), new ManaCostsImpl<>("{1}{B/G}") + ); + ability.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard( + 2, StaticFilters.FILTER_CARD_CARDS + ))); + this.addAbility(ability); + } + + private IshkanahBroodmother(final IshkanahBroodmother card) { + super(card); + } + + @Override + public IshkanahBroodmother copy() { + return new IshkanahBroodmother(this); + } +} diff --git a/Mage.Sets/src/mage/cards/i/IsperiasSkywatch.java b/Mage.Sets/src/mage/cards/i/IsperiasSkywatch.java index ed6c7e86833..186570dd784 100644 --- a/Mage.Sets/src/mage/cards/i/IsperiasSkywatch.java +++ b/Mage.Sets/src/mage/cards/i/IsperiasSkywatch.java @@ -11,8 +11,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; /** @@ -20,12 +19,6 @@ import mage.target.common.TargetCreaturePermanent; * @author LevelX2 */ public final class IsperiasSkywatch extends CardImpl { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } public IsperiasSkywatch(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{5}{U}"); @@ -41,7 +34,7 @@ public final class IsperiasSkywatch extends CardImpl { // When Isperia's Skywatch enters the battlefield, detain target creature an opponent controls. // (Until your next turn, that creature can't attack or block and its activated abilities can't be activated.) Ability ability = new EntersBattlefieldTriggeredAbility(new DetainTargetEffect()); - TargetCreaturePermanent target = new TargetCreaturePermanent(filter); + TargetCreaturePermanent target = new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE); ability.addTarget(target); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/i/IsshinTwoHeavensAsOne.java b/Mage.Sets/src/mage/cards/i/IsshinTwoHeavensAsOne.java new file mode 100644 index 00000000000..1475e50fd39 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/IsshinTwoHeavensAsOne.java @@ -0,0 +1,89 @@ +package mage.cards.i; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.NumberOfTriggersEvent; +import mage.game.permanent.Permanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class IsshinTwoHeavensAsOne extends CardImpl { + + public IsshinTwoHeavensAsOne(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}{W}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SAMURAI); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // If a creature attacking causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time. + this.addAbility(new SimpleStaticAbility(new IsshinTwoHeavensAsOneEffect())); + } + + private IsshinTwoHeavensAsOne(final IsshinTwoHeavensAsOne card) { + super(card); + } + + @Override + public IsshinTwoHeavensAsOne copy() { + return new IsshinTwoHeavensAsOne(this); + } +} + +class IsshinTwoHeavensAsOneEffect extends ReplacementEffectImpl { + + IsshinTwoHeavensAsOneEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "if a creature attacking causes a triggered ability " + + "of a permanent you control to trigger, that ability triggers an additional time"; + } + + IsshinTwoHeavensAsOneEffect(final IsshinTwoHeavensAsOneEffect effect) { + super(effect); + } + + @Override + public IsshinTwoHeavensAsOneEffect copy() { + return new IsshinTwoHeavensAsOneEffect(this); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.NUMBER_OF_TRIGGERS; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + NumberOfTriggersEvent numberOfTriggersEvent = (NumberOfTriggersEvent) event; + Permanent sourcePermanent = game.getPermanent(numberOfTriggersEvent.getSourceId()); + if (sourcePermanent == null || !sourcePermanent.isControlledBy(source.getControllerId())) { + return false; + } + GameEvent sourceEvent = numberOfTriggersEvent.getSourceEvent(); + switch (sourceEvent.getType()) { + case ATTACKER_DECLARED: + case DECLARED_ATTACKERS: + case DEFENDER_ATTACKED: + return true; + } + return false; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + event.setAmount(event.getAmount() + 1); + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/i/IvorytuskFortress.java b/Mage.Sets/src/mage/cards/i/IvorytuskFortress.java index 73e30cbdf5d..070eaa78547 100644 --- a/Mage.Sets/src/mage/cards/i/IvorytuskFortress.java +++ b/Mage.Sets/src/mage/cards/i/IvorytuskFortress.java @@ -10,8 +10,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; -import mage.counters.CounterType; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; /** * @@ -19,12 +18,6 @@ import mage.filter.common.FilterCreaturePermanent; */ public final class IvorytuskFortress extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("each creature you control with a +1/+1 counter on it"); - - static { - filter.add(CounterType.P1P1.getPredicate()); - } - public IvorytuskFortress(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}{B}{G}"); this.subtype.add(SubType.ELEPHANT); @@ -33,7 +26,8 @@ public final class IvorytuskFortress extends CardImpl { this.toughness = new MageInt(7); // Untap each creature you control with a +1/+1 counter on it during each other player's untap step. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new UntapAllDuringEachOtherPlayersUntapStepEffect(filter))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, + new UntapAllDuringEachOtherPlayersUntapStepEffect(StaticFilters.FILTER_EACH_CONTROLLED_CREATURE_P1P1))); } private IvorytuskFortress(final IvorytuskFortress card) { diff --git a/Mage.Sets/src/mage/cards/i/IvyLaneDenizen.java b/Mage.Sets/src/mage/cards/i/IvyLaneDenizen.java index 4257e4b85fb..369ebb50b8b 100644 --- a/Mage.Sets/src/mage/cards/i/IvyLaneDenizen.java +++ b/Mage.Sets/src/mage/cards/i/IvyLaneDenizen.java @@ -1,4 +1,3 @@ - package mage.cards.i; import java.util.UUID; @@ -26,8 +25,8 @@ public final class IvyLaneDenizen extends CardImpl { private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("another green creature"); static { - filter.add(new ColorPredicate(ObjectColor.GREEN)); filter.add(AnotherPredicate.instance); + filter.add(new ColorPredicate(ObjectColor.GREEN)); } public IvyLaneDenizen(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/i/IymrithDesertDoom.java b/Mage.Sets/src/mage/cards/i/IymrithDesertDoom.java index 082c0b1a198..bcdaa792757 100644 --- a/Mage.Sets/src/mage/cards/i/IymrithDesertDoom.java +++ b/Mage.Sets/src/mage/cards/i/IymrithDesertDoom.java @@ -40,7 +40,7 @@ public final class IymrithDesertDoom extends CardImpl { // Iymrith, Desert Doom has ward {4} as long as it's untapped. this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( new GainAbilitySourceEffect(new WardAbility(new GenericManaCost(4)), Duration.WhileOnBattlefield), - new InvertCondition(SourceTappedCondition.instance), + SourceTappedCondition.UNTAPPED, "{this} has ward {4} as long as it's untapped" ))); diff --git a/Mage.Sets/src/mage/cards/i/IzzetCharm.java b/Mage.Sets/src/mage/cards/i/IzzetCharm.java index 91fe42f1319..3f65a58a442 100644 --- a/Mage.Sets/src/mage/cards/i/IzzetCharm.java +++ b/Mage.Sets/src/mage/cards/i/IzzetCharm.java @@ -10,8 +10,7 @@ import mage.abilities.effects.common.DrawDiscardControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.FilterSpell; -import mage.filter.predicate.Predicates; +import mage.filter.StaticFilters; import mage.target.TargetSpell; import mage.target.common.TargetCreaturePermanent; @@ -21,18 +20,12 @@ import mage.target.common.TargetCreaturePermanent; */ public final class IzzetCharm extends CardImpl { - static private final FilterSpell filter = new FilterSpell("noncreature spell"); - static { - filter.add(Predicates.not(CardType.CREATURE.getPredicate())); - } - public IzzetCharm(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{U}{R}"); - // Choose one — Counter target noncreature spell unless its controller pays {2}; this.getSpellAbility().addEffect(new CounterUnlessPaysEffect(new GenericManaCost(2))); - this.getSpellAbility().getTargets().add(new TargetSpell(filter)); + this.getSpellAbility().getTargets().add(new TargetSpell(StaticFilters.FILTER_SPELL_NON_CREATURE)); // or Izzet Charm deals 2 damage to target creature; Mode mode = new Mode(); diff --git a/Mage.Sets/src/mage/cards/j/JaceArcaneStrategist.java b/Mage.Sets/src/mage/cards/j/JaceArcaneStrategist.java index cec4fe502cd..3ed23ece1d1 100644 --- a/Mage.Sets/src/mage/cards/j/JaceArcaneStrategist.java +++ b/Mage.Sets/src/mage/cards/j/JaceArcaneStrategist.java @@ -3,7 +3,6 @@ package mage.cards.j; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.common.DrawSecondCardTriggeredAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.combat.CantBeBlockedAllEffect; import mage.abilities.effects.common.counter.AddCountersTargetEffect; @@ -29,7 +28,7 @@ public final class JaceArcaneStrategist extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.JACE); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // Whenever you draw your second card each turn, put a +1/+1 counter on target creature you control. Ability ability = new DrawSecondCardTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java b/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java index c6c1ba6f4f2..f30379f876d 100644 --- a/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java +++ b/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java @@ -3,7 +3,6 @@ package mage.cards.j; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.cards.*; @@ -16,7 +15,6 @@ import mage.filter.predicate.other.PlayerIdPredicate; import mage.game.ExileZone; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetCard; @@ -40,7 +38,7 @@ public final class JaceArchitectOfThought extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.JACE); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Until your next turn, whenever a creature an opponent controls attacks, it gets -1/-0 until end of turn. this.addAbility(new LoyaltyAbility(new JaceArchitectOfThoughtStartEffect1(), 1)); @@ -113,7 +111,7 @@ class JaceArchitectOfThoughtDelayedTriggeredAbility extends DelayedTriggeredAbil public boolean checkTrigger(GameEvent event, Game game) { if (game.getOpponents(getControllerId()).contains(event.getPlayerId())) { getEffects().forEach((effect) -> { - effect.setTargetPointer(new FixedTarget(event.getSourceId())); + effect.setTargetPointer(new FixedTarget(event.getSourceId(), game)); }); return true; } diff --git a/Mage.Sets/src/mage/cards/j/JaceBeleren.java b/Mage.Sets/src/mage/cards/j/JaceBeleren.java index 27a93f560b1..7ea444b8510 100644 --- a/Mage.Sets/src/mage/cards/j/JaceBeleren.java +++ b/Mage.Sets/src/mage/cards/j/JaceBeleren.java @@ -3,7 +3,6 @@ package mage.cards.j; import java.util.UUID; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.common.DrawCardAllEffect; import mage.abilities.effects.common.DrawCardTargetEffect; import mage.abilities.effects.common.PutLibraryIntoGraveTargetEffect; @@ -25,7 +24,7 @@ public final class JaceBeleren extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.JACE); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +2: Each player draws a card. this.addAbility(new LoyaltyAbility(new DrawCardAllEffect(1), 2)); diff --git a/Mage.Sets/src/mage/cards/j/JaceCunningCastaway.java b/Mage.Sets/src/mage/cards/j/JaceCunningCastaway.java index ec38cad3e7d..7b83bde8d7c 100644 --- a/Mage.Sets/src/mage/cards/j/JaceCunningCastaway.java +++ b/Mage.Sets/src/mage/cards/j/JaceCunningCastaway.java @@ -4,7 +4,6 @@ package mage.cards.j; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenCopyTargetEffect; import mage.abilities.effects.common.CreateTokenEffect; @@ -34,7 +33,7 @@ public final class JaceCunningCastaway extends CardImpl { addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.JACE); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +1: Whenever one or more creatures you control deal combat damage to a player this turn, draw a card, then discard a card. this.addAbility(new LoyaltyAbility(new JaceCunningCastawayEffect1(), 1)); diff --git a/Mage.Sets/src/mage/cards/j/JaceIngeniousMindMage.java b/Mage.Sets/src/mage/cards/j/JaceIngeniousMindMage.java index 686cdc5f704..dc7a2f301d7 100644 --- a/Mage.Sets/src/mage/cards/j/JaceIngeniousMindMage.java +++ b/Mage.Sets/src/mage/cards/j/JaceIngeniousMindMage.java @@ -4,7 +4,6 @@ package mage.cards.j; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.UntapAllControllerEffect; import mage.abilities.effects.common.continuous.GainControlTargetEffect; @@ -29,7 +28,7 @@ public final class JaceIngeniousMindMage extends CardImpl { addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.JACE); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +1: Draw a card. this.addAbility(new LoyaltyAbility(new DrawCardSourceControllerEffect(1), 1)); diff --git a/Mage.Sets/src/mage/cards/j/JaceMemoryAdept.java b/Mage.Sets/src/mage/cards/j/JaceMemoryAdept.java index 1d15ffea264..04ab1c419a2 100644 --- a/Mage.Sets/src/mage/cards/j/JaceMemoryAdept.java +++ b/Mage.Sets/src/mage/cards/j/JaceMemoryAdept.java @@ -5,7 +5,6 @@ import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.Mode; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.DrawCardTargetEffect; import mage.abilities.effects.common.PutLibraryIntoGraveTargetEffect; @@ -28,7 +27,7 @@ public final class JaceMemoryAdept extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.JACE); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Draw a card. Target player puts the top card of their library into their graveyard. LoyaltyAbility ability1 = new LoyaltyAbility(new DrawCardSourceControllerEffect(1), 1); diff --git a/Mage.Sets/src/mage/cards/j/JaceMirrorMage.java b/Mage.Sets/src/mage/cards/j/JaceMirrorMage.java index faabc94065e..f16da18f0b7 100644 --- a/Mage.Sets/src/mage/cards/j/JaceMirrorMage.java +++ b/Mage.Sets/src/mage/cards/j/JaceMirrorMage.java @@ -3,7 +3,6 @@ package mage.cards.j; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.condition.common.KickedCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.OneShotEffect; @@ -36,7 +35,7 @@ public final class JaceMirrorMage extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.JACE); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // Kicker {2} this.addAbility(new KickerAbility("{2}")); diff --git a/Mage.Sets/src/mage/cards/j/JaceTelepathUnbound.java b/Mage.Sets/src/mage/cards/j/JaceTelepathUnbound.java index 16aa5f01c56..bc67fa20ea3 100644 --- a/Mage.Sets/src/mage/cards/j/JaceTelepathUnbound.java +++ b/Mage.Sets/src/mage/cards/j/JaceTelepathUnbound.java @@ -3,7 +3,6 @@ package mage.cards.j; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.*; import mage.abilities.effects.common.GetEmblemEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; @@ -31,9 +30,8 @@ public final class JaceTelepathUnbound extends CardImpl { this.color.setBlue(true); this.nightCard = true; - this.transformable = true; - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +1: Up to one target creature gets -2/-0 until your next turn. Effect effect = new BoostTargetEffect(-2, 0, Duration.UntilYourNextTurn); diff --git a/Mage.Sets/src/mage/cards/j/JaceTheLivingGuildpact.java b/Mage.Sets/src/mage/cards/j/JaceTheLivingGuildpact.java index c905f3cacc5..3aa4233e08b 100644 --- a/Mage.Sets/src/mage/cards/j/JaceTheLivingGuildpact.java +++ b/Mage.Sets/src/mage/cards/j/JaceTheLivingGuildpact.java @@ -2,7 +2,6 @@ package mage.cards.j; import java.util.UUID; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.Effect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; @@ -38,7 +37,7 @@ public final class JaceTheLivingGuildpact extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.JACE); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +1: Look at the top two cards of your library. Put one of them into your graveyard. Effect effect = new LookLibraryAndPickControllerEffect( diff --git a/Mage.Sets/src/mage/cards/j/JaceTheMindSculptor.java b/Mage.Sets/src/mage/cards/j/JaceTheMindSculptor.java index b4ae3b8e6cf..15755998437 100644 --- a/Mage.Sets/src/mage/cards/j/JaceTheMindSculptor.java +++ b/Mage.Sets/src/mage/cards/j/JaceTheMindSculptor.java @@ -4,7 +4,6 @@ package mage.cards.j; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.BrainstormEffect; import mage.abilities.effects.common.ReturnToHandTargetEffect; @@ -33,7 +32,7 @@ public final class JaceTheMindSculptor extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.JACE); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +2: Look at the top card of target player's library. You may put that card on the bottom of that player's library. LoyaltyAbility ability1 = new LoyaltyAbility(new JaceTheMindSculptorEffect1(), 2); diff --git a/Mage.Sets/src/mage/cards/j/JaceUnravelerOfSecrets.java b/Mage.Sets/src/mage/cards/j/JaceUnravelerOfSecrets.java index 0dc11c8b01e..4d9c3b5ed9b 100644 --- a/Mage.Sets/src/mage/cards/j/JaceUnravelerOfSecrets.java +++ b/Mage.Sets/src/mage/cards/j/JaceUnravelerOfSecrets.java @@ -4,7 +4,6 @@ package mage.cards.j; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.GetEmblemEffect; @@ -31,10 +30,10 @@ public final class JaceUnravelerOfSecrets extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.JACE); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +1: Scry 1, then draw a card. - Ability ability = new LoyaltyAbility(new ScryEffect(1), 1); + Ability ability = new LoyaltyAbility(new ScryEffect(1, false), 1); Effect effect = new DrawCardSourceControllerEffect(1); effect.setText(", then draw a card"); ability.addEffect(effect); diff --git a/Mage.Sets/src/mage/cards/j/JaceVrynsProdigy.java b/Mage.Sets/src/mage/cards/j/JaceVrynsProdigy.java index e2af5275a3b..9b2e0358f7a 100644 --- a/Mage.Sets/src/mage/cards/j/JaceVrynsProdigy.java +++ b/Mage.Sets/src/mage/cards/j/JaceVrynsProdigy.java @@ -4,7 +4,7 @@ package mage.cards.j; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.Gender; +import mage.abilities.Pronoun; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.condition.common.CardsInControllerGraveyardCondition; import mage.abilities.costs.common.TapSourceCost; @@ -34,13 +34,12 @@ public final class JaceVrynsProdigy extends CardImpl { this.power = new MageInt(0); this.toughness = new MageInt(2); - this.transformable = true; this.secondSideCardClazz = JaceTelepathUnbound.class; // {T}: Draw a card, then discard a card. If there are five or more cards in your graveyard, exile Jace, Vryn's Prodigy, then return him to the battefield transformed under his owner's control. this.addAbility(new TransformAbility()); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawDiscardControllerEffect(1, 1), new TapSourceCost()); - Effect effect = new ConditionalOneShotEffect(new ExileAndReturnTransformedSourceEffect(Gender.MALE), new CardsInControllerGraveyardCondition(5)); + Effect effect = new ConditionalOneShotEffect(new ExileAndReturnTransformedSourceEffect(Pronoun.HE), new CardsInControllerGraveyardCondition(5)); ability.addEffect(effect); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/j/JaceWielderOfMysteries.java b/Mage.Sets/src/mage/cards/j/JaceWielderOfMysteries.java index dcb6ec7f37a..e08bf8945b0 100644 --- a/Mage.Sets/src/mage/cards/j/JaceWielderOfMysteries.java +++ b/Mage.Sets/src/mage/cards/j/JaceWielderOfMysteries.java @@ -2,7 +2,6 @@ package mage.cards.j; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.ReplacementEffectImpl; @@ -33,7 +32,7 @@ public final class JaceWielderOfMysteries extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.JACE); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // If you would draw a card while your library has no cards in it, you win the game instead. this.addAbility(new SimpleStaticAbility(new JaceWielderOfMysteriesContinuousEffect())); diff --git a/Mage.Sets/src/mage/cards/j/JackalPup.java b/Mage.Sets/src/mage/cards/j/JackalPup.java index 051a3b1bdd4..24c7fa97eec 100644 --- a/Mage.Sets/src/mage/cards/j/JackalPup.java +++ b/Mage.Sets/src/mage/cards/j/JackalPup.java @@ -27,7 +27,7 @@ public final class JackalPup extends CardImpl { this.toughness = new MageInt(1); // Whenever Jackal Pup is dealt damage, it deals that much damage to you. - this.addAbility(new DealtDamageToSourceTriggeredAbility(new JackalPupEffect(), false, false, true)); + this.addAbility(new DealtDamageToSourceTriggeredAbility(new JackalPupEffect(), false, false)); } diff --git a/Mage.Sets/src/mage/cards/j/JacobHaukenInspector.java b/Mage.Sets/src/mage/cards/j/JacobHaukenInspector.java new file mode 100644 index 00000000000..b69fb403aea --- /dev/null +++ b/Mage.Sets/src/mage/cards/j/JacobHaukenInspector.java @@ -0,0 +1,136 @@ +package mage.cards.j; + +import java.util.UUID; +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.keyword.TransformAbility; +import mage.cards.Card; +import mage.constants.*; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInHand; +import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; + +/** + * + * @author weirddan455 + */ +public final class JacobHaukenInspector extends CardImpl { + + public JacobHaukenInspector(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ADVISOR); + this.power = new MageInt(0); + this.toughness = new MageInt(2); + + this.secondSideCardClazz = mage.cards.h.HaukensInsight.class; + + // {T}: Draw a card, then exile a card from your hand face down. You may look at that card for as long as it remains exiled. You may pay {4}{U}{U}. If you do, transform Jacob Hauken, Inspector. + this.addAbility(new TransformAbility()); + Ability ability = new SimpleActivatedAbility(new JacobHaukenInspectorExileEffect(), new TapSourceCost()); + ability.addEffect(new DoIfCostPaid(new TransformSourceEffect(), new ManaCostsImpl<>("{4}{U}{U}"))); + this.addAbility(ability); + } + + private JacobHaukenInspector(final JacobHaukenInspector card) { + super(card); + } + + @Override + public JacobHaukenInspector copy() { + return new JacobHaukenInspector(this); + } +} + +class JacobHaukenInspectorExileEffect extends OneShotEffect { + + public JacobHaukenInspectorExileEffect() { + super(Outcome.Benefit); + staticText = "Draw a card, then exile a card from your hand face down. You may look at that card for as long as it remains exiled"; + } + + private JacobHaukenInspectorExileEffect(final JacobHaukenInspectorExileEffect effect) { + super(effect); + } + + @Override + public JacobHaukenInspectorExileEffect copy() { + return new JacobHaukenInspectorExileEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + controller.drawCards(1, source, game); + if (!controller.getHand().isEmpty()) { + TargetCardInHand target = new TargetCardInHand().withChooseHint("to exile"); + controller.chooseTarget(outcome, controller.getHand(), target, source, game); + Card card = game.getCard(target.getFirstTarget()); + if (card != null) { + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + MageObject sourceObject = source.getSourceObject(game); + String exileName = sourceObject == null ? null : sourceObject.getIdName(); + controller.moveCardsToExile(card, source, game, true, exileId, exileName); + if (game.getState().getZone(card.getId()) == Zone.EXILED) { + card.setFaceDown(true, game); + JacobHaukenInspectorLookEffect effect = new JacobHaukenInspectorLookEffect(controller.getId()); + effect.setTargetPointer(new FixedTarget(card, game)); + game.addEffect(effect, source); + } + } + } + return true; + } +} + +class JacobHaukenInspectorLookEffect extends AsThoughEffectImpl { + + private final UUID authorizedPlayerId; + + public JacobHaukenInspectorLookEffect(UUID authorizedPlayerId) { + super(AsThoughEffectType.LOOK_AT_FACE_DOWN, Duration.EndOfGame, Outcome.Benefit); + this.authorizedPlayerId = authorizedPlayerId; + } + + private JacobHaukenInspectorLookEffect(final JacobHaukenInspectorLookEffect effect) { + super(effect); + this.authorizedPlayerId = effect.authorizedPlayerId; + } + + @Override + public JacobHaukenInspectorLookEffect copy() { + return new JacobHaukenInspectorLookEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + UUID cardId = getTargetPointer().getFirst(game, source); + if (cardId == null) { + this.discard(); // card is no longer in the origin zone, effect can be discarded + } + return affectedControllerId.equals(authorizedPlayerId) + && objectId.equals(cardId); + } +} diff --git a/Mage.Sets/src/mage/cards/j/JadeIdol.java b/Mage.Sets/src/mage/cards/j/JadeIdol.java index 4ce0aee9031..0d707a38768 100644 --- a/Mage.Sets/src/mage/cards/j/JadeIdol.java +++ b/Mage.Sets/src/mage/cards/j/JadeIdol.java @@ -21,7 +21,7 @@ public final class JadeIdol extends CardImpl { public JadeIdol(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); - this.addAbility(new SpellCastControllerTriggeredAbility(new BecomesCreatureSourceEffect(new JadeIdolToken(), "", Duration.EndOfTurn), StaticFilters.SPIRIT_OR_ARCANE_CARD, false)); + this.addAbility(new SpellCastControllerTriggeredAbility(new BecomesCreatureSourceEffect(new JadeIdolToken(), "", Duration.EndOfTurn), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false)); } private JadeIdol(final JadeIdol card) { diff --git a/Mage.Sets/src/mage/cards/j/JaggedPoppet.java b/Mage.Sets/src/mage/cards/j/JaggedPoppet.java index 00cf3a3a594..1e98da1ec05 100644 --- a/Mage.Sets/src/mage/cards/j/JaggedPoppet.java +++ b/Mage.Sets/src/mage/cards/j/JaggedPoppet.java @@ -33,7 +33,7 @@ public final class JaggedPoppet extends CardImpl { this.toughness = new MageInt(4); // Whenever Jagged Poppet is dealt damage, discard that many cards. - this.addAbility(new DealtDamageToSourceTriggeredAbility(new JaggedPoppetDealtDamageEffect(), false, false, true)); + this.addAbility(new DealtDamageToSourceTriggeredAbility(new JaggedPoppetDealtDamageEffect(), false, false)); // Hellbent - Whenever Jagged Poppet deals combat damage to a player, if you have no cards in hand, that player discards cards equal to the damage. Ability hellbentAbility = new ConditionalInterveningIfTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/j/JaggedScarArchers.java b/Mage.Sets/src/mage/cards/j/JaggedScarArchers.java index 9c883b75b4c..8c71bdc4b70 100644 --- a/Mage.Sets/src/mage/cards/j/JaggedScarArchers.java +++ b/Mage.Sets/src/mage/cards/j/JaggedScarArchers.java @@ -47,7 +47,7 @@ public final class JaggedScarArchers extends CardImpl { // Jagged-Scar Archers's power and toughness are each equal to the number of Elves you control. this.addAbility(new SimpleStaticAbility(Zone.ALL, new SetPowerToughnessSourceEffect(new PermanentsOnBattlefieldCount(controlledElvesFilter), Duration.EndOfGame))); // {tap}: Jagged-Scar Archers deals damage equal to its power to target creature with flying. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(new SourcePermanentPowerCount()), new TapSourceCost()); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(new SourcePermanentPowerCount()).setText("{this} deals damage equal to its power to target creature with flying"), new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent(flyingCreatureFilter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/j/JawsOfStone.java b/Mage.Sets/src/mage/cards/j/JawsOfStone.java index 54e9eff7630..c286b9a1adc 100644 --- a/Mage.Sets/src/mage/cards/j/JawsOfStone.java +++ b/Mage.Sets/src/mage/cards/j/JawsOfStone.java @@ -1,4 +1,3 @@ - package mage.cards.j; import java.util.UUID; @@ -17,25 +16,24 @@ import mage.target.common.TargetAnyTargetAmount; * @author jeffwadsworth */ public final class JawsOfStone extends CardImpl { - + static final private FilterControlledLandPermanent filter = new FilterControlledLandPermanent("mountains you control"); - + static { filter.add(SubType.MOUNTAIN.getPredicate()); } - static final private String rule = "{this} deals X damage divided as you choose among any number of target creatures and/or players, where X is the number of Mountains you control as you cast {this}"; - + static final private String rule = "{this} deals X damage divided as you choose among any number of targets, where X is the number of Mountains you control as you cast this spell"; + public JawsOfStone(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{5}{R}"); - // Jaws of Stone deals X damage divided as you choose among any number of target creatures and/or players, where X is the number of Mountains you control as you cast Jaws of Stone. + // Jaws of Stone deals X damage divided as you choose among any number of targets, where X is the number of Mountains you control as you cast this spell. PermanentsOnBattlefieldCount mountains = new PermanentsOnBattlefieldCount(filter, null); Effect effect = new DamageMultiEffect(mountains); effect.setText(rule); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addTarget(new TargetAnyTargetAmount(mountains)); - } private JawsOfStone(final JawsOfStone card) { diff --git a/Mage.Sets/src/mage/cards/j/JayaBallard.java b/Mage.Sets/src/mage/cards/j/JayaBallard.java index 5a1e88a84a3..bf01d7cfe46 100644 --- a/Mage.Sets/src/mage/cards/j/JayaBallard.java +++ b/Mage.Sets/src/mage/cards/j/JayaBallard.java @@ -2,7 +2,6 @@ package mage.cards.j; import mage.Mana; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.common.GetEmblemEffect; import mage.abilities.effects.common.discard.DiscardAndDrawThatManyEffect; import mage.abilities.effects.mana.AddConditionalManaEffect; @@ -27,7 +26,7 @@ public final class JayaBallard extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.JAYA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +1: Add {R}{R}{R}. Spend this mana only to cast instant or sorcery spells. this.addAbility(new LoyaltyAbility(new AddConditionalManaEffect( diff --git a/Mage.Sets/src/mage/cards/j/JayaVeneratedFiremage.java b/Mage.Sets/src/mage/cards/j/JayaVeneratedFiremage.java index f124de4b778..1f451a78153 100644 --- a/Mage.Sets/src/mage/cards/j/JayaVeneratedFiremage.java +++ b/Mage.Sets/src/mage/cards/j/JayaVeneratedFiremage.java @@ -3,7 +3,6 @@ package mage.cards.j; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.common.DamageTargetEffect; @@ -28,7 +27,7 @@ public final class JayaVeneratedFiremage extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.JAYA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // If another red source you control would deal damage to a permanent or player, it deals that much damage plus 1 to that permanent or player instead. this.addAbility(new SimpleStaticAbility(new JayaVeneratedFiremageEffect())); diff --git a/Mage.Sets/src/mage/cards/j/JayasGreeting.java b/Mage.Sets/src/mage/cards/j/JayasGreeting.java index 12cf5687a80..7b2d940d2d5 100644 --- a/Mage.Sets/src/mage/cards/j/JayasGreeting.java +++ b/Mage.Sets/src/mage/cards/j/JayasGreeting.java @@ -19,7 +19,7 @@ public final class JayasGreeting extends CardImpl { // Jaya's Greeting deals 3 damage to target creature. Scry 1. this.getSpellAbility().addEffect(new DamageTargetEffect(3)); - this.getSpellAbility().addEffect(new ScryEffect(1)); + this.getSpellAbility().addEffect(new ScryEffect(1, false)); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } diff --git a/Mage.Sets/src/mage/cards/j/JazalGoldmane.java b/Mage.Sets/src/mage/cards/j/JazalGoldmane.java index a6a32614787..85cd8dac6fb 100644 --- a/Mage.Sets/src/mage/cards/j/JazalGoldmane.java +++ b/Mage.Sets/src/mage/cards/j/JazalGoldmane.java @@ -1,4 +1,3 @@ - package mage.cards.j; import java.util.UUID; @@ -12,11 +11,10 @@ import mage.abilities.keyword.FirstStrikeAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; +import mage.constants.SubType; import mage.constants.SuperType; -import mage.constants.Zone; -import mage.filter.common.FilterAttackingCreature; +import mage.filter.StaticFilters; /** * @@ -38,8 +36,7 @@ public final class JazalGoldmane extends CardImpl { // {3}{W}{W}: Attacking creatures you control get +X/+X until end of turn, where X is the number of attacking creatures. DynamicValue xValue = new AttackingCreatureCount("the number of attacking creatures"); this.addAbility(new SimpleActivatedAbility( - Zone.BATTLEFIELD, - new BoostControlledEffect(xValue, xValue, Duration.EndOfTurn, new FilterAttackingCreature("Attacking creatures"), false, true), + new BoostControlledEffect(xValue, xValue, Duration.EndOfTurn, StaticFilters.FILTER_ATTACKING_CREATURES, false, true), new ManaCostsImpl("{3}{W}{W}"))); } diff --git a/Mage.Sets/src/mage/cards/j/JerrenCorruptedBishop.java b/Mage.Sets/src/mage/cards/j/JerrenCorruptedBishop.java index aafbae17164..36eb28f00ad 100644 --- a/Mage.Sets/src/mage/cards/j/JerrenCorruptedBishop.java +++ b/Mage.Sets/src/mage/cards/j/JerrenCorruptedBishop.java @@ -45,7 +45,6 @@ public final class JerrenCorruptedBishop extends CardImpl { this.subtype.add(SubType.CLERIC); this.power = new MageInt(2); this.toughness = new MageInt(3); - this.transformable = true; this.secondSideCardClazz = mage.cards.o.OrmendahlTheCorrupter.class; // Whenever Jerren, Corrupted Bishop enters the battlefield or another nontoken Human you control dies, you lose 1 life and create a 1/1 white Human creature token. @@ -61,7 +60,7 @@ public final class JerrenCorruptedBishop extends CardImpl { // At the beginning of your end step, if you have exactly 13 life, you may pay {4}{B}{B}. If you do, transform Jerren. this.addAbility(new TransformAbility()); this.addAbility(new BeginningOfEndStepTriggeredAbility(Zone.BATTLEFIELD, new DoIfCostPaid( - new TransformSourceEffect(true), new ManaCostsImpl<>("{4}{B}{B}") + new TransformSourceEffect(), new ManaCostsImpl<>("{4}{B}{B}") ), TargetController.YOU, JerrenCorruptedBishopCondition.instance, false)); } diff --git a/Mage.Sets/src/mage/cards/j/JeskaThriceReborn.java b/Mage.Sets/src/mage/cards/j/JeskaThriceReborn.java index 34aba889bd8..bfe7a8e07de 100644 --- a/Mage.Sets/src/mage/cards/j/JeskaThriceReborn.java +++ b/Mage.Sets/src/mage/cards/j/JeskaThriceReborn.java @@ -38,6 +38,7 @@ public final class JeskaThriceReborn extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.JESKA); + this.setStartingLoyalty(0); // Jeska, Thrice Reborn enters the battlefield with a loyalty counter on it for each time you've cast a commander from the command zone this game. this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect( diff --git a/Mage.Sets/src/mage/cards/j/JetmirsGarden.java b/Mage.Sets/src/mage/cards/j/JetmirsGarden.java new file mode 100644 index 00000000000..e90f8878f97 --- /dev/null +++ b/Mage.Sets/src/mage/cards/j/JetmirsGarden.java @@ -0,0 +1,48 @@ +package mage.cards.j; + +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.keyword.CyclingAbility; +import mage.abilities.mana.GreenManaAbility; +import mage.abilities.mana.RedManaAbility; +import mage.abilities.mana.WhiteManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class JetmirsGarden extends CardImpl { + + public JetmirsGarden(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + this.subtype.add(SubType.MOUNTAIN); + this.subtype.add(SubType.FOREST); + this.subtype.add(SubType.PLAINS); + + // ({T}: Add {R}, {G}, or {W}.) + this.addAbility(new RedManaAbility()); + this.addAbility(new GreenManaAbility()); + this.addAbility(new WhiteManaAbility()); + + // Jetmir's Garden enters the battlefield tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // Cycling {3} + this.addAbility(new CyclingAbility(new GenericManaCost(3))); + } + + private JetmirsGarden(final JetmirsGarden card) { + super(card); + } + + @Override + public JetmirsGarden copy() { + return new JetmirsGarden(this); + } +} diff --git a/Mage.Sets/src/mage/cards/j/JiangYanggu.java b/Mage.Sets/src/mage/cards/j/JiangYanggu.java index 193e63b41e9..3a390631b1b 100644 --- a/Mage.Sets/src/mage/cards/j/JiangYanggu.java +++ b/Mage.Sets/src/mage/cards/j/JiangYanggu.java @@ -4,7 +4,6 @@ package mage.cards.j; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.condition.InvertCondition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.decorator.ConditionalOneShotEffect; @@ -43,7 +42,7 @@ public final class JiangYanggu extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.YANGGU); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Target creature gets +2/+2 until end of turn. Ability ability = new LoyaltyAbility(new BoostTargetEffect(2, 2, Duration.EndOfTurn), 1); diff --git a/Mage.Sets/src/mage/cards/j/JiangYangguWildcrafter.java b/Mage.Sets/src/mage/cards/j/JiangYangguWildcrafter.java index a16cc9a59a2..63333e9acd9 100644 --- a/Mage.Sets/src/mage/cards/j/JiangYangguWildcrafter.java +++ b/Mage.Sets/src/mage/cards/j/JiangYangguWildcrafter.java @@ -2,9 +2,8 @@ package mage.cards.j; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.effects.common.continuous.GainAbilityAllEffect; import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.abilities.mana.AnyColorManaAbility; import mage.cards.CardImpl; @@ -14,8 +13,7 @@ import mage.constants.Duration; import mage.constants.SubType; import mage.constants.SuperType; import mage.counters.CounterType; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; import java.util.UUID; @@ -25,23 +23,16 @@ import java.util.UUID; */ public final class JiangYangguWildcrafter extends CardImpl { - private static final FilterPermanent filter - = new FilterControlledCreaturePermanent("Each creature you control with a +1/+1 counter on it"); - - static { - filter.add(CounterType.P1P1.getPredicate()); - } - public JiangYangguWildcrafter(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{2}{G}"); this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.YANGGU); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // Each creature you control with a +1/+1 counter on it has "{T}: Add one mana of any color." - this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( - new AnyColorManaAbility(), Duration.WhileOnBattlefield, filter + this.addAbility(new SimpleStaticAbility(new GainAbilityAllEffect( + new AnyColorManaAbility(), Duration.WhileOnBattlefield, StaticFilters.FILTER_EACH_CONTROLLED_CREATURE_P1P1 ))); // -1: Put a +1/+1 counter on target creature. diff --git a/Mage.Sets/src/mage/cards/j/JinGitaxiasProgressTyrant.java b/Mage.Sets/src/mage/cards/j/JinGitaxiasProgressTyrant.java new file mode 100644 index 00000000000..650efcdcbd3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/j/JinGitaxiasProgressTyrant.java @@ -0,0 +1,92 @@ +package mage.cards.j; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.common.SpellCastOpponentTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CopySourceSpellEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterSpell; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.game.stack.Spell; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class JinGitaxiasProgressTyrant extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("an artifact, instant, or sorcery spell"); + + static { + filter.add(Predicates.or( + CardType.ARTIFACT.getPredicate(), + CardType.INSTANT.getPredicate(), + CardType.SORCERY.getPredicate() + )); + } + + public JinGitaxiasProgressTyrant(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{U}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.PHYREXIAN); + this.subtype.add(SubType.PRAETOR); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + + // Whenever you cast an artifact, instant, or sorcery spell, copy that spell. You may choose new targets for the copy. This ability triggers only once each turn. + this.addAbility(new SpellCastControllerTriggeredAbility( + new CopySourceSpellEffect().setText("copy that spell. You may choose new targets for the copy"), + filter, false, true + ).setTriggersOnce(true)); + + // Whenever an opponent casts an artifact, instant, or sorcery spell, counter that spell. This ability triggers only once each turn. + this.addAbility(new SpellCastOpponentTriggeredAbility( + new JinGitaxiasProgressTyrantEffect(), filter, false + ).setTriggersOnce(true)); + } + + private JinGitaxiasProgressTyrant(final JinGitaxiasProgressTyrant card) { + super(card); + } + + @Override + public JinGitaxiasProgressTyrant copy() { + return new JinGitaxiasProgressTyrant(this); + } +} + +class JinGitaxiasProgressTyrantEffect extends OneShotEffect { + + JinGitaxiasProgressTyrantEffect() { + super(Outcome.Benefit); + staticText = "counter that spell"; + } + + private JinGitaxiasProgressTyrantEffect(final JinGitaxiasProgressTyrantEffect effect) { + super(effect); + } + + @Override + public JinGitaxiasProgressTyrantEffect copy() { + return new JinGitaxiasProgressTyrantEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Spell spell = (Spell) getValue("spellCast"); + if (spell != null) { + spell.counter(source, game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/j/Johan.java b/Mage.Sets/src/mage/cards/j/Johan.java index 2cb8a88d337..b79c0955e32 100644 --- a/Mage.Sets/src/mage/cards/j/Johan.java +++ b/Mage.Sets/src/mage/cards/j/Johan.java @@ -39,7 +39,7 @@ public final class Johan extends CardImpl { // At the beginning of combat on your turn, you may have Johan gain "Johan can't attack" until end of combat. If you do, attacking doesn't cause creatures you control to tap this combat if Johan is untapped. Condition condition = new CompoundCondition("if {this} is untapped", - new InvertCondition(SourceTappedCondition.instance), + SourceTappedCondition.UNTAPPED, SourceOnBattlefieldCondition.instance); Ability ability = new BeginningOfCombatTriggeredAbility(new CantAttackSourceEffect(Duration.EndOfCombat).setText("you may have {this} gain \"{this} can't attack\" until end of combat"), TargetController.YOU, true); ability.addEffect(new ConditionalContinuousEffect( diff --git a/Mage.Sets/src/mage/cards/j/JosuVessLichKnight.java b/Mage.Sets/src/mage/cards/j/JosuVessLichKnight.java index b9f4e55732a..6831b35d9e4 100644 --- a/Mage.Sets/src/mage/cards/j/JosuVessLichKnight.java +++ b/Mage.Sets/src/mage/cards/j/JosuVessLichKnight.java @@ -31,7 +31,7 @@ public final class JosuVessLichKnight extends CardImpl { this.addAbility(new KickerAbility("{5}{B}")); //Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); //When Josu Vess, Lich Knight enters the battlefield, if it was kicked, create eight 2/2 black Zombie Knight creature tokens with menace. EntersBattlefieldTriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new ZombieKnightToken(), 8)); diff --git a/Mage.Sets/src/mage/cards/j/JourneyToEternity.java b/Mage.Sets/src/mage/cards/j/JourneyToEternity.java index 71595f95f74..cb54bb7b777 100644 --- a/Mage.Sets/src/mage/cards/j/JourneyToEternity.java +++ b/Mage.Sets/src/mage/cards/j/JourneyToEternity.java @@ -34,7 +34,6 @@ public final class JourneyToEternity extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.AURA); - this.transformable = true; this.secondSideCardClazz = mage.cards.a.AtzalCaveOfEternity.class; // Enchant creature you control diff --git a/Mage.Sets/src/mage/cards/j/Joust.java b/Mage.Sets/src/mage/cards/j/Joust.java index 77b8d33df29..7fa44d822bf 100644 --- a/Mage.Sets/src/mage/cards/j/Joust.java +++ b/Mage.Sets/src/mage/cards/j/Joust.java @@ -49,7 +49,7 @@ class JoustEffect extends OneShotEffect { super(Outcome.Benefit); staticText = "Choose target creature you control and target creature you don't control. " + "The creature you control gets +2/+1 until end of turn if it's a Knight. " + - "Then those creatures fight each other."; + "Then those creatures fight each other. (Each deals damage equal to its power to the other.)"; } private JoustEffect(final JoustEffect effect) { diff --git a/Mage.Sets/src/mage/cards/j/JovensFerrets.java b/Mage.Sets/src/mage/cards/j/JovensFerrets.java index 7b33fc839cc..1f91fe9a7e8 100644 --- a/Mage.Sets/src/mage/cards/j/JovensFerrets.java +++ b/Mage.Sets/src/mage/cards/j/JovensFerrets.java @@ -92,7 +92,7 @@ class JovensFerretsEffect extends OneShotEffect { for (Permanent creature : toTap) { creature.tap(source, game); DontUntapInControllersNextUntapStepTargetEffect effect = new DontUntapInControllersNextUntapStepTargetEffect(); - effect.setTargetPointer(new FixedTarget(creature.getId())); + effect.setTargetPointer(new FixedTarget(creature.getId(), game)); game.addEffect(effect, source); } return true; diff --git a/Mage.Sets/src/mage/cards/j/JuganDefendsTheTemple.java b/Mage.Sets/src/mage/cards/j/JuganDefendsTheTemple.java new file mode 100644 index 00000000000..0ccc1720602 --- /dev/null +++ b/Mage.Sets/src/mage/cards/j/JuganDefendsTheTemple.java @@ -0,0 +1,59 @@ +package mage.cards.j; + +import mage.abilities.common.SagaAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.ExileSagaAndReturnTransformedEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.TransformAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SagaChapter; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.game.permanent.token.HumanMonkToken; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class JuganDefendsTheTemple extends CardImpl { + + public JuganDefendsTheTemple(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}"); + + this.subtype.add(SubType.SAGA); + this.secondSideCardClazz = mage.cards.r.RemnantOfTheRisingStar.class; + + // (As this Saga enters and after your draw step, add a lore counter.) + SagaAbility sagaAbility = new SagaAbility(this); + + // I — Create a 1/1 green Human Monk creature token with "{T}: Add {G}." + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, new CreateTokenEffect(new HumanMonkToken())); + + // II — Put a +1/+1 counter on each of up to two target creatures. + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_II, SagaChapter.CHAPTER_II, + new AddCountersTargetEffect(CounterType.P1P1.createInstance()), + new TargetPermanent(0, 2, StaticFilters.FILTER_PERMANENT_CREATURES) + ); + + // III — Exile this Saga, then return it to the battlefield transformed under your control. + this.addAbility(new TransformAbility()); + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, new ExileSagaAndReturnTransformedEffect()); + + this.addAbility(sagaAbility); + } + + private JuganDefendsTheTemple(final JuganDefendsTheTemple card) { + super(card); + } + + @Override + public JuganDefendsTheTemple copy() { + return new JuganDefendsTheTemple(this); + } +} diff --git a/Mage.Sets/src/mage/cards/j/JukaiNaturalist.java b/Mage.Sets/src/mage/cards/j/JukaiNaturalist.java new file mode 100644 index 00000000000..4c864aa2b38 --- /dev/null +++ b/Mage.Sets/src/mage/cards/j/JukaiNaturalist.java @@ -0,0 +1,46 @@ +package mage.cards.j; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.common.FilterEnchantmentCard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class JukaiNaturalist extends CardImpl { + + private static final FilterCard filter = new FilterEnchantmentCard("enchantment spells"); + + public JukaiNaturalist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{G}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.MONK); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // Enchantment spells you cast cost {1} less to cast. + this.addAbility(new SimpleStaticAbility(new SpellsCostReductionControllerEffect(filter, 1))); + } + + private JukaiNaturalist(final JukaiNaturalist card) { + super(card); + } + + @Override + public JukaiNaturalist copy() { + return new JukaiNaturalist(this); + } +} diff --git a/Mage.Sets/src/mage/cards/j/JukaiPreserver.java b/Mage.Sets/src/mage/cards/j/JukaiPreserver.java new file mode 100644 index 00000000000..eaf11012be3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/j/JukaiPreserver.java @@ -0,0 +1,55 @@ +package mage.cards.j; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.ChannelAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class JukaiPreserver extends CardImpl { + + public JukaiPreserver(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{3}{G}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.DRUID); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // When Jukai Preserver enters the battlefield, put a +1/+1 counter on target creature you control. + Ability ability = new EntersBattlefieldTriggeredAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance()) + ); + ability.addTarget(new TargetControlledCreaturePermanent()); + this.addAbility(ability); + + // Channel — {2}{G}, Discard Jukai Preserver: Put a +1/+1 counter on each of up to two target creatures you control. + ability = new ChannelAbility( + "{2}{G}", new AddCountersTargetEffect(CounterType.P1P1.createInstance()) + ); + ability.addTarget(new TargetPermanent(0, 2, StaticFilters.FILTER_CONTROLLED_CREATURES)); + this.addAbility(ability); + } + + private JukaiPreserver(final JukaiPreserver card) { + super(card); + } + + @Override + public JukaiPreserver copy() { + return new JukaiPreserver(this); + } +} diff --git a/Mage.Sets/src/mage/cards/j/JukaiTrainee.java b/Mage.Sets/src/mage/cards/j/JukaiTrainee.java new file mode 100644 index 00000000000..147dba078fe --- /dev/null +++ b/Mage.Sets/src/mage/cards/j/JukaiTrainee.java @@ -0,0 +1,43 @@ +package mage.cards.j; + +import mage.MageInt; +import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class JukaiTrainee extends CardImpl { + + public JukaiTrainee(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SAMURAI); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Whenever Jukai Trainee blocks or becomes blocked, it gets +1/+1 until end of turn. + this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility( + new BoostSourceEffect(1, 1, Duration.EndOfTurn) + .setText("it gets +1/+1 until end of turn"), + false, false + )); + } + + private JukaiTrainee(final JukaiTrainee card) { + super(card); + } + + @Override + public JukaiTrainee copy() { + return new JukaiTrainee(this); + } +} diff --git a/Mage.Sets/src/mage/cards/j/JuniperOrderAdvocate.java b/Mage.Sets/src/mage/cards/j/JuniperOrderAdvocate.java index 7f5691eabca..a11ea7f36bd 100644 --- a/Mage.Sets/src/mage/cards/j/JuniperOrderAdvocate.java +++ b/Mage.Sets/src/mage/cards/j/JuniperOrderAdvocate.java @@ -42,7 +42,7 @@ public final class JuniperOrderAdvocate extends CardImpl { // As long as Juniper Order Advocate is untapped, green creatures you control get +1/+1. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect( new BoostAllEffect(1, 1, Duration.WhileOnBattlefield, filter, false), - new InvertCondition(SourceTappedCondition.instance), + SourceTappedCondition.UNTAPPED, "As long as {this} is untapped, green creatures you control get +1/+1."))); } diff --git a/Mage.Sets/src/mage/cards/j/JunjiTheMidnightSky.java b/Mage.Sets/src/mage/cards/j/JunjiTheMidnightSky.java new file mode 100644 index 00000000000..5c61ba24e6c --- /dev/null +++ b/Mage.Sets/src/mage/cards/j/JunjiTheMidnightSky.java @@ -0,0 +1,76 @@ +package mage.cards.j; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.common.LoseLifeOpponentsEffect; +import mage.abilities.effects.common.LoseLifeSourceControllerEffect; +import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; +import mage.abilities.effects.common.discard.DiscardEachPlayerEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.TargetController; +import mage.filter.FilterCard; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.Predicates; +import mage.target.common.TargetCardInGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class JunjiTheMidnightSky extends CardImpl { + + private static final FilterCard filter = new FilterCreatureCard("non-Dragon creature card from a graveyard"); + + static { + filter.add(Predicates.not(SubType.DRAGON.getPredicate())); + } + + public JunjiTheMidnightSky(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.DRAGON); + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Menace + this.addAbility(new MenaceAbility(false)); + + // When Junji, the Midnight Sky dies, choose one — + // • Each opponent discards two cards and loses 2 life. + Ability ability = new DiesSourceTriggeredAbility(new DiscardEachPlayerEffect( + StaticValue.get(2), false, TargetController.OPPONENT + )); + ability.addEffect(new LoseLifeOpponentsEffect(2).setText("and loses 2 life")); + + // • Put target non-Dragon creature card from a graveyard onto the battlefield under your control. You lose 2 life. + Mode mode = new Mode(new ReturnFromGraveyardToBattlefieldTargetEffect()); + mode.addEffect(new LoseLifeSourceControllerEffect(2)); + mode.addTarget(new TargetCardInGraveyard(filter)); + ability.addMode(mode); + this.addAbility(ability); + } + + private JunjiTheMidnightSky(final JunjiTheMidnightSky card) { + super(card); + } + + @Override + public JunjiTheMidnightSky copy() { + return new JunjiTheMidnightSky(this); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KabiraEvangel.java b/Mage.Sets/src/mage/cards/k/KabiraEvangel.java index 7fe59316bef..d0ad7a9d2e2 100644 --- a/Mage.Sets/src/mage/cards/k/KabiraEvangel.java +++ b/Mage.Sets/src/mage/cards/k/KabiraEvangel.java @@ -1,36 +1,27 @@ - package mage.cards.k; -import java.util.UUID; import mage.MageInt; -import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; -import mage.abilities.effects.Effect; +import mage.abilities.common.AllyEntersBattlefieldTriggeredAbility; import mage.abilities.effects.common.continuous.GainProtectionFromColorAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.Zone; import mage.filter.FilterPermanent; -import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardIdPredicate; +import mage.filter.common.FilterControlledPermanent; + +import java.util.UUID; /** - * * @author jeffwadsworth */ public final class KabiraEvangel extends CardImpl { - private static final FilterControlledCreaturePermanent FILTER1 = new FilterControlledCreaturePermanent(); - - static { - FILTER1.add(SubType.ALLY.getPredicate()); - } + private static final FilterPermanent FILTER1 = new FilterControlledPermanent(SubType.ALLY, "Ally"); public KabiraEvangel(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.CLERIC); this.subtype.add(SubType.ALLY); @@ -38,13 +29,13 @@ public final class KabiraEvangel extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(3); - FilterPermanent filter2 = new FilterPermanent(getName() + " or another Ally"); - filter2.add(Predicates.or(new CardIdPredicate(this.getId()), SubType.ALLY.getPredicate())); - // Whenever Kabira Evangel or another Ally enters the battlefield under your control, you may choose a color. If you do, Allies you control gain protection from the chosen color until end of turn. - Effect effect = new GainProtectionFromColorAllEffect(Duration.EndOfTurn, FILTER1); - effect.setText("choose a color. If you do, Allies you control gain protection from the chosen color until end of turn."); - this.addAbility(new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD, effect, filter2, true)); + this.addAbility(new AllyEntersBattlefieldTriggeredAbility( + new GainProtectionFromColorAllEffect(Duration.EndOfTurn, FILTER1) + .setText("choose a color. If you do, Allies you control gain protection " + + "from the chosen color until end of turn."), + true + )); } private KabiraEvangel(final KabiraEvangel card) { diff --git a/Mage.Sets/src/mage/cards/k/KaerveksHex.java b/Mage.Sets/src/mage/cards/k/KaerveksHex.java index 11702d8fbb9..ceac6128324 100644 --- a/Mage.Sets/src/mage/cards/k/KaerveksHex.java +++ b/Mage.Sets/src/mage/cards/k/KaerveksHex.java @@ -1,4 +1,3 @@ - package mage.cards.k; import java.util.UUID; @@ -7,8 +6,8 @@ import mage.abilities.effects.common.DamageAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ColorPredicate; /** @@ -17,20 +16,18 @@ import mage.filter.predicate.mageobject.ColorPredicate; */ public final class KaerveksHex extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent("green creature"); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("green creature"); static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - filter2.add(new ColorPredicate(ObjectColor.GREEN)); + filter.add(new ColorPredicate(ObjectColor.GREEN)); } public KaerveksHex(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{B}"); // Kaervek's Hex deals 1 damage to each nonblack creature and an additional 1 damage to each green creature. - this.getSpellAbility().addEffect(new DamageAllEffect(1, filter)); - this.getSpellAbility().addEffect(new DamageAllEffect(1, filter2).setText("and an additional 1 damage to each green creature")); + this.getSpellAbility().addEffect(new DamageAllEffect(1, StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); + this.getSpellAbility().addEffect(new DamageAllEffect(1, filter).setText("and an additional 1 damage to each green creature")); } private KaerveksHex(final KaerveksHex card) { diff --git a/Mage.Sets/src/mage/cards/k/KaheeraTheOrphanguard.java b/Mage.Sets/src/mage/cards/k/KaheeraTheOrphanguard.java index 90a868ddd7b..d9d7611fba8 100644 --- a/Mage.Sets/src/mage/cards/k/KaheeraTheOrphanguard.java +++ b/Mage.Sets/src/mage/cards/k/KaheeraTheOrphanguard.java @@ -80,7 +80,7 @@ enum KaheeraTheOrphanguardCompanionCondition implements CompanionCondition { @Override public String getRule() { - return "Each creature card in your starting deck is a Cat, Elemental, Nightmare, Dinosaur or Beast card."; + return "Each creature card in your starting deck is a Cat, Elemental, Nightmare, Dinosaur, or Beast card."; } private static final List subtypes = Arrays.asList( diff --git a/Mage.Sets/src/mage/cards/k/KaimaTheFracturedCalm.java b/Mage.Sets/src/mage/cards/k/KaimaTheFracturedCalm.java new file mode 100644 index 00000000000..f2944791881 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KaimaTheFracturedCalm.java @@ -0,0 +1,94 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.combat.GoadTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.targetpointer.FixedTarget; + +import java.util.Objects; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KaimaTheFracturedCalm extends CardImpl { + + public KaimaTheFracturedCalm(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // At the beginning of your end step, goad each creature your opponents control that's enchanted by an Aura you control. Put a +1/+1 counter on Kaima, the Fractured Calm for each creature goaded this way. + this.addAbility(new BeginningOfEndStepTriggeredAbility( + new KaimaTheFracturedCalmEffect(), TargetController.YOU, false + )); + } + + private KaimaTheFracturedCalm(final KaimaTheFracturedCalm card) { + super(card); + } + + @Override + public KaimaTheFracturedCalm copy() { + return new KaimaTheFracturedCalm(this); + } +} + +class KaimaTheFracturedCalmEffect extends OneShotEffect { + + KaimaTheFracturedCalmEffect() { + super(Outcome.Benefit); + staticText = "goad each creature your opponents control that's enchanted by an Aura you control. " + + "Put a +1/+1 counter on {this} for each creature goaded this way"; + } + + private KaimaTheFracturedCalmEffect(final KaimaTheFracturedCalmEffect effect) { + super(effect); + } + + @Override + public KaimaTheFracturedCalmEffect copy() { + return new KaimaTheFracturedCalmEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + int goaded = 0; + for (Permanent permanent : game.getBattlefield().getActivePermanents( + StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE, + source.getControllerId(), source.getSourceId(), game + )) { + if (permanent + .getAttachments() + .stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .noneMatch(p -> p.isControlledBy(source.getControllerId()) + && p.hasSubtype(SubType.AURA, game))) { + continue; + } + game.addEffect(new GoadTargetEffect().setTargetPointer(new FixedTarget(permanent, game)), source); + goaded++; + } + if (goaded < 1) { + return false; + } + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (permanent != null) { + permanent.addCounters(CounterType.P1P1.createInstance(goaded), source, game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/k/KairiTheSwirlingSky.java b/Mage.Sets/src/mage/cards/k/KairiTheSwirlingSky.java new file mode 100644 index 00000000000..0469936cee5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KairiTheSwirlingSky.java @@ -0,0 +1,139 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.WardAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterNonlandPermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.TargetPermanent; +import mage.target.common.TargetCardInGraveyard; + +import java.util.Objects; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KairiTheSwirlingSky extends CardImpl { + + public KairiTheSwirlingSky(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.DRAGON); + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(6); + this.toughness = new MageInt(6); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Ward {3} + this.addAbility(new WardAbility(new ManaCostsImpl<>("{3}"))); + + // When Kairi, the Swirling Sky dies, choose one — + // • Return any number of target nonland permanents with total mana value 6 or less to their owners' hands. + Ability ability = new DiesSourceTriggeredAbility(new ReturnToHandTargetEffect() + .setText("return any number of target nonland permanents with total mana value 6 or less to their owners' hands")); + ability.addTarget(new KairiTheSwirlingSkyTarget()); + + // • Mill six cards, then return up to two instant and/or sorcery cards from your graveyard to your hand. + ability.addMode(new Mode(new KairiTheSwirlingSkyEffect())); + this.addAbility(ability); + } + + private KairiTheSwirlingSky(final KairiTheSwirlingSky card) { + super(card); + } + + @Override + public KairiTheSwirlingSky copy() { + return new KairiTheSwirlingSky(this); + } +} + +class KairiTheSwirlingSkyTarget extends TargetPermanent { + + private static final FilterPermanent filter + = new FilterNonlandPermanent("nonland permanents with total mana value 6 or less"); + + KairiTheSwirlingSkyTarget() { + super(0, Integer.MAX_VALUE, filter, false); + } + + private KairiTheSwirlingSkyTarget(final KairiTheSwirlingSkyTarget target) { + super(target); + } + + @Override + public KairiTheSwirlingSkyTarget copy() { + return new KairiTheSwirlingSkyTarget(this); + } + + @Override + public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) { + if (!super.canTarget(controllerId, id, source, game)) { + return false; + } + Permanent permanent = game.getPermanent(id); + if (permanent == null) { + return false; + } + return permanent.getManaValue() + + this.getTargets() + .stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .mapToInt(MageObject::getManaValue) + .sum() <= 6; + } +} + +class KairiTheSwirlingSkyEffect extends OneShotEffect { + + KairiTheSwirlingSkyEffect() { + super(Outcome.ReturnToHand); + staticText = "mill six cards, then return up to two instant " + + "and/or sorcery cards from your graveyard to your hand"; + } + + private KairiTheSwirlingSkyEffect(final KairiTheSwirlingSkyEffect effect) { + super(effect); + } + + @Override + public KairiTheSwirlingSkyEffect copy() { + return new KairiTheSwirlingSkyEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + player.millCards(6, source, game); + TargetCard target = new TargetCardInGraveyard( + 0, 2, StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY + ); + target.setNotTarget(true); + player.choose(outcome, player.getGraveyard(), target, game); + return player.moveCards(new CardsImpl(target.getTargets()), Zone.HAND, source, game); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KaitoShizuki.java b/Mage.Sets/src/mage/cards/k/KaitoShizuki.java new file mode 100644 index 00000000000..b5f45b0770e --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KaitoShizuki.java @@ -0,0 +1,90 @@ +package mage.cards.k; + +import mage.abilities.Ability; +import mage.abilities.LoyaltyAbility; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.InvertCondition; +import mage.abilities.condition.common.RaidCondition; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.GetEmblemEffect; +import mage.abilities.effects.common.PhaseOutSourceEffect; +import mage.abilities.effects.common.discard.DiscardControllerEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.abilities.hint.common.RaidHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.command.emblems.KaitoShizukiEmblem; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.NinjaToken; +import mage.watchers.common.PlayerAttackedWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KaitoShizuki extends CardImpl { + + private static final Hint hint = new ConditionHint( + KaitoShizukiCondition.instance, "This permanent entered the battlefield this turn" + ); + private static final Condition condition = new InvertCondition(RaidCondition.instance); + + public KaitoShizuki(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{1}{U}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.KAITO); + this.setStartingLoyalty(3); + + // At the beginning of your end step, if Kaito Shizuki entered the battlefield this turn, he phases out. + this.addAbility(new BeginningOfEndStepTriggeredAbility( + Zone.BATTLEFIELD, new PhaseOutSourceEffect().setText("he phases out"), + TargetController.YOU, KaitoShizukiCondition.instance, false + ).addHint(hint)); + + // +1: Draw a card. Then discard a card unless you attacked this turn. + Ability ability = new LoyaltyAbility(new DrawCardSourceControllerEffect(1), 1); + ability.addEffect(new ConditionalOneShotEffect( + new DiscardControllerEffect(1), condition, + "Then discard a card unless you attacked this turn" + )); + this.addAbility(ability.addHint(RaidHint.instance), new PlayerAttackedWatcher()); + + // −2: Create a 1/1 blue Ninja creature token with "This creature can't be blocked." + this.addAbility(new LoyaltyAbility(new CreateTokenEffect(new NinjaToken()), -2)); + + // −7: You get an emblem with "Whenever a creature you control deals combat damage to a player, search your library for a blue or black creature card, put it onto the battlefield, then shuffle." + this.addAbility(new LoyaltyAbility(new GetEmblemEffect(new KaitoShizukiEmblem()), -7)); + } + + private KaitoShizuki(final KaitoShizuki card) { + super(card); + } + + @Override + public KaitoShizuki copy() { + return new KaitoShizuki(this); + } +} + +enum KaitoShizukiCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + return permanent != null && permanent.getTurnsOnBattlefield() == 0; + } + + @Override + public String toString() { + return "if {this} entered the battlefield this turn"; + } +} diff --git a/Mage.Sets/src/mage/cards/k/KaitosPursuit.java b/Mage.Sets/src/mage/cards/k/KaitosPursuit.java new file mode 100644 index 00000000000..fa74b38d128 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KaitosPursuit.java @@ -0,0 +1,51 @@ +package mage.cards.k; + +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.effects.common.discard.DiscardTargetEffect; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicates; +import mage.target.TargetPlayer; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KaitosPursuit extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent("Ninjas and Rogues"); + + static { + filter.add(Predicates.or( + SubType.NINJA.getPredicate(), + SubType.ROGUE.getPredicate() + )); + } + + public KaitosPursuit(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}"); + + // Target player discards two cards. Ninjas and Rogues you control gain menace until end of turn. + this.getSpellAbility().addEffect(new DiscardTargetEffect(2)); + this.getSpellAbility().addTarget(new TargetPlayer()); + this.getSpellAbility().addEffect(new GainAbilityControlledEffect( + new MenaceAbility(false), Duration.EndOfTurn, filter + ).setText("Ninjas and Rogues you control gain menace until end of turn. " + + "(They can't be blocked except by two or more creatures.)")); + } + + private KaitosPursuit(final KaitosPursuit card) { + super(card); + } + + @Override + public KaitosPursuit copy() { + return new KaitosPursuit(this); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KaldraCompleat.java b/Mage.Sets/src/mage/cards/k/KaldraCompleat.java index 2167b150181..5316b4c0ae7 100644 --- a/Mage.Sets/src/mage/cards/k/KaldraCompleat.java +++ b/Mage.Sets/src/mage/cards/k/KaldraCompleat.java @@ -65,7 +65,7 @@ public final class KaldraCompleat extends CardImpl { true, false, true, - StaticFilters.FILTER_PERMANENT_CREATURE_A + StaticFilters.FILTER_PERMANENT_A_CREATURE ), AttachmentType.EQUIPMENT, Duration.WhileOnBattlefield, diff --git a/Mage.Sets/src/mage/cards/k/KalonianHydra.java b/Mage.Sets/src/mage/cards/k/KalonianHydra.java index bc76a0a699a..c47a872f4e4 100644 --- a/Mage.Sets/src/mage/cards/k/KalonianHydra.java +++ b/Mage.Sets/src/mage/cards/k/KalonianHydra.java @@ -17,6 +17,7 @@ import mage.constants.SubType; import mage.constants.Outcome; import mage.constants.TargetController; import mage.counters.CounterType; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; @@ -56,12 +57,6 @@ public final class KalonianHydra extends CardImpl { class KalonianHydraEffect extends OneShotEffect { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); - static { - filter.add(TargetController.YOU.getControllerPredicate()); - filter.add(CounterType.P1P1.getPredicate()); - } - public KalonianHydraEffect() { super(Outcome.BoostCreature); this.staticText = "double the number of +1/+1 counters on each creature you control"; @@ -78,7 +73,7 @@ class KalonianHydraEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - List permanents = game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game); + List permanents = game.getBattlefield().getActivePermanents(StaticFilters.FILTER_CONTROLLED_CREATURE_P1P1, source.getControllerId(), source.getSourceId(), game); for (Permanent permanent : permanents) { int existingCounters = permanent.getCounters(game).getCount(CounterType.P1P1); if (existingCounters > 0) { diff --git a/Mage.Sets/src/mage/cards/k/KamberThePlunderer.java b/Mage.Sets/src/mage/cards/k/KamberThePlunderer.java new file mode 100644 index 00000000000..723876e742e --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KamberThePlunderer.java @@ -0,0 +1,56 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DiesCreatureTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.keyword.LifelinkAbility; +import mage.abilities.keyword.PartnerWithAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.StaticFilters; +import mage.game.permanent.token.BloodToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KamberThePlunderer extends CardImpl { + + public KamberThePlunderer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.VAMPIRE); + this.subtype.add(SubType.ROGUE); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // Partner with Laurine, the Diversion + this.addAbility(new PartnerWithAbility("Laurine, the Diversion")); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // Whenever a creature an opponent controls dies, you gain 1 life and create a Blood token. + Ability ability = new DiesCreatureTriggeredAbility( + new GainLifeEffect(1), false, StaticFilters.FILTER_OPPONENTS_PERMANENT_A_CREATURE + ); + ability.addEffect(new CreateTokenEffect(new BloodToken()).concatBy("and")); + this.addAbility(ability); + } + + private KamberThePlunderer(final KamberThePlunderer card) { + super(card); + } + + @Override + public KamberThePlunderer copy() { + return new KamberThePlunderer(this); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KamiOfCelebration.java b/Mage.Sets/src/mage/cards/k/KamiOfCelebration.java new file mode 100644 index 00000000000..23e1899a516 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KamiOfCelebration.java @@ -0,0 +1,85 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.common.AttacksCreatureYouControlTriggeredAbility; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.common.ExileTopXMayPlayUntilEndOfTurnEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.permanent.ModifiedPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KamiOfCelebration extends CardImpl { + + private static final FilterControlledCreaturePermanent filter + = new FilterControlledCreaturePermanent("a modified creature you control"); + + static { + filter.add(ModifiedPredicate.instance); + } + + public KamiOfCelebration(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}"); + + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Whenever a modified creature you control attacks, exile the top card of your library. You may play that card this turn. + this.addAbility(new AttacksCreatureYouControlTriggeredAbility( + new ExileTopXMayPlayUntilEndOfTurnEffect(1), false, filter + )); + + // Whenever you cast a spell from exile, put a +1/+1 counter on target creature you control. + this.addAbility(new KamiOfCelebrationAbility()); + } + + private KamiOfCelebration(final KamiOfCelebration card) { + super(card); + } + + @Override + public KamiOfCelebration copy() { + return new KamiOfCelebration(this); + } +} + +class KamiOfCelebrationAbility extends SpellCastControllerTriggeredAbility { + + KamiOfCelebrationAbility() { + super(new AddCountersTargetEffect(CounterType.P1P1.createInstance()), false); + this.addTarget(new TargetControlledCreaturePermanent()); + } + + private KamiOfCelebrationAbility(final KamiOfCelebrationAbility ability) { + super(ability); + } + + @Override + public KamiOfCelebrationAbility copy() { + return new KamiOfCelebrationAbility(this); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return event.getZone() == Zone.EXILED && super.checkTrigger(event, game); + } + + @Override + public String getRule() { + return "Whenever you cast a spell from exile, put a +1/+1 counter on target creature you control."; + } +} diff --git a/Mage.Sets/src/mage/cards/k/KamiOfFiresRoar.java b/Mage.Sets/src/mage/cards/k/KamiOfFiresRoar.java index 20b877312aa..c1786031541 100644 --- a/Mage.Sets/src/mage/cards/k/KamiOfFiresRoar.java +++ b/Mage.Sets/src/mage/cards/k/KamiOfFiresRoar.java @@ -28,7 +28,7 @@ public final class KamiOfFiresRoar extends CardImpl { this.toughness = new MageInt(3); // Whenever you cast a Spirit or Arcane spell, target creature can't block this turn. - Ability ability = new SpellCastControllerTriggeredAbility(new CantBlockTargetEffect(Duration.EndOfTurn), StaticFilters.SPIRIT_OR_ARCANE_CARD, false); + Ability ability = new SpellCastControllerTriggeredAbility(new CantBlockTargetEffect(Duration.EndOfTurn), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/k/KamiOfIndustry.java b/Mage.Sets/src/mage/cards/k/KamiOfIndustry.java new file mode 100644 index 00000000000..1aa23bac775 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KamiOfIndustry.java @@ -0,0 +1,97 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.SacrificeTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.HasteAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.common.FilterArtifactCard; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCardInYourGraveyard; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KamiOfIndustry extends CardImpl { + + private static final FilterCard filter + = new FilterArtifactCard("artifact card with mana value 3 or less from your graveyard"); + + static { + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); + } + + public KamiOfIndustry(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}"); + + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(3); + this.toughness = new MageInt(6); + + // When Kami of Industry enters the battlefield, return target artifact card with mana value 3 or less from your graveyard to the battlefield. It gains haste. Sacrifice it at the beginning of the next end step. + Ability ability = new EntersBattlefieldTriggeredAbility(new KamiOfIndustryEffect()); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + this.addAbility(ability); + } + + private KamiOfIndustry(final KamiOfIndustry card) { + super(card); + } + + @Override + public KamiOfIndustry copy() { + return new KamiOfIndustry(this); + } +} + +class KamiOfIndustryEffect extends OneShotEffect { + + KamiOfIndustryEffect() { + super(Outcome.Benefit); + staticText = "return target artifact card with mana value 3 or less from your graveyard " + + "to the battlefield. It gains haste. Sacrifice it at the beginning of the next end step"; + } + + private KamiOfIndustryEffect(final KamiOfIndustryEffect effect) { + super(effect); + } + + @Override + public KamiOfIndustryEffect copy() { + return new KamiOfIndustryEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Card card = game.getCard(source.getFirstTarget()); + if (player == null || card == null) { + return false; + } + player.moveCards(card, Zone.BATTLEFIELD, source, game); + Permanent permanent = game.getPermanent(card.getId()); + if (permanent == null) { + return false; + } + game.addEffect(new GainAbilityTargetEffect(HasteAbility.getInstance()) + .setTargetPointer(new FixedTarget(permanent, game)), source); + game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility( + new SacrificeTargetEffect("sacrifice it").setTargetPointer(new FixedTarget(permanent, game)) + ), source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/k/KamiOfRestlessShadows.java b/Mage.Sets/src/mage/cards/k/KamiOfRestlessShadows.java new file mode 100644 index 00000000000..b75ec88bee3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KamiOfRestlessShadows.java @@ -0,0 +1,63 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.PutOnLibraryTargetEffect; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.Predicates; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KamiOfRestlessShadows extends CardImpl { + + private static final FilterCard filter + = new FilterCreatureCard("Ninja or Rogue creature card from your graveyard"); + + static { + filter.add(Predicates.or( + SubType.NINJA.getPredicate(), + SubType.ROGUE.getPredicate() + )); + } + + public KamiOfRestlessShadows(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}"); + + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // When Kami of Restless Shadows enters the battlefield, choose one — + // • Return up to one target Ninja or Rogue creature card from your graveyard to your hand. + Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect()); + ability.addTarget(new TargetCardInYourGraveyard(0, 1, filter)); + + // • Put target creature card from your graveyard on top of your library. + Mode mode = new Mode(new PutOnLibraryTargetEffect(true)); + mode.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); + ability.addMode(mode); + this.addAbility(ability); + } + + private KamiOfRestlessShadows(final KamiOfRestlessShadows card) { + super(card); + } + + @Override + public KamiOfRestlessShadows copy() { + return new KamiOfRestlessShadows(this); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KamiOfTatteredShoji.java b/Mage.Sets/src/mage/cards/k/KamiOfTatteredShoji.java index 60b83d9c868..8ef007fa0cc 100644 --- a/Mage.Sets/src/mage/cards/k/KamiOfTatteredShoji.java +++ b/Mage.Sets/src/mage/cards/k/KamiOfTatteredShoji.java @@ -26,7 +26,7 @@ public final class KamiOfTatteredShoji extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(5); // Whenever you cast a Spirit or Arcane spell, Kami of Tattered Shoji gains flying until end of turn. - this.addAbility(new SpellCastControllerTriggeredAbility(new GainAbilitySourceEffect(FlyingAbility.getInstance(), Duration.EndOfTurn), StaticFilters.SPIRIT_OR_ARCANE_CARD, false)); + this.addAbility(new SpellCastControllerTriggeredAbility(new GainAbilitySourceEffect(FlyingAbility.getInstance(), Duration.EndOfTurn), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false)); } private KamiOfTatteredShoji(final KamiOfTatteredShoji card) { diff --git a/Mage.Sets/src/mage/cards/k/KamiOfTerribleSecrets.java b/Mage.Sets/src/mage/cards/k/KamiOfTerribleSecrets.java new file mode 100644 index 00000000000..fc75ec91879 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KamiOfTerribleSecrets.java @@ -0,0 +1,48 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.common.ControlArtifactAndEnchantmentCondition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.hint.common.ControlArtifactAndEnchantmentHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KamiOfTerribleSecrets extends CardImpl { + + public KamiOfTerribleSecrets(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); + + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // When Kami of Terrible Secrets enters the battlefield, if you control an artifact and an enchantment, you draw a card and you gain 1 life. + Ability ability = new ConditionalInterveningIfTriggeredAbility( + new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(1)), + ControlArtifactAndEnchantmentCondition.instance, "When {this} enters the battlefield, " + + "if you control an artifact and an enchantment, you draw a card and you gain 1 life." + ); + ability.addEffect(new GainLifeEffect(1)); + this.addAbility(ability.addHint(ControlArtifactAndEnchantmentHint.instance)); + } + + private KamiOfTerribleSecrets(final KamiOfTerribleSecrets card) { + super(card); + } + + @Override + public KamiOfTerribleSecrets copy() { + return new KamiOfTerribleSecrets(this); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KamiOfTheCrescentMoon.java b/Mage.Sets/src/mage/cards/k/KamiOfTheCrescentMoon.java index 8b73b4f4749..fd6ad2c89db 100644 --- a/Mage.Sets/src/mage/cards/k/KamiOfTheCrescentMoon.java +++ b/Mage.Sets/src/mage/cards/k/KamiOfTheCrescentMoon.java @@ -1,7 +1,5 @@ - package mage.cards.k; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.BeginningOfDrawTriggeredAbility; import mage.abilities.effects.common.DrawCardTargetEffect; @@ -12,14 +10,15 @@ import mage.constants.SubType; import mage.constants.SuperType; import mage.constants.TargetController; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class KamiOfTheCrescentMoon extends CardImpl { public KamiOfTheCrescentMoon(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{U}{U}"); addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.SPIRIT); @@ -27,7 +26,7 @@ public final class KamiOfTheCrescentMoon extends CardImpl { this.toughness = new MageInt(3); // At the beginning of each player's draw step, that player draws an additional card. - this.addAbility(new BeginningOfDrawTriggeredAbility(new DrawCardTargetEffect(1), TargetController.ANY, false)); + this.addAbility(new BeginningOfDrawTriggeredAbility(new DrawCardTargetEffect(1).setText("that player draws an additional card"), TargetController.ANY, false)); } private KamiOfTheCrescentMoon(final KamiOfTheCrescentMoon card) { diff --git a/Mage.Sets/src/mage/cards/k/KamiOfTheHunt.java b/Mage.Sets/src/mage/cards/k/KamiOfTheHunt.java index b29795afdf0..81d1d7666aa 100644 --- a/Mage.Sets/src/mage/cards/k/KamiOfTheHunt.java +++ b/Mage.Sets/src/mage/cards/k/KamiOfTheHunt.java @@ -24,7 +24,7 @@ public final class KamiOfTheHunt extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); - this.addAbility(new SpellCastControllerTriggeredAbility(new BoostSourceEffect(1, 1, Duration.EndOfTurn), StaticFilters.SPIRIT_OR_ARCANE_CARD, false)); + this.addAbility(new SpellCastControllerTriggeredAbility(new BoostSourceEffect(1, 1, Duration.EndOfTurn), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false)); } private KamiOfTheHunt(final KamiOfTheHunt card) { diff --git a/Mage.Sets/src/mage/cards/k/KamiOfThePaintedRoad.java b/Mage.Sets/src/mage/cards/k/KamiOfThePaintedRoad.java index 7b61bcc40d5..eb3e982a985 100644 --- a/Mage.Sets/src/mage/cards/k/KamiOfThePaintedRoad.java +++ b/Mage.Sets/src/mage/cards/k/KamiOfThePaintedRoad.java @@ -26,7 +26,7 @@ public final class KamiOfThePaintedRoad extends CardImpl { this.toughness = new MageInt(3); // Whenever you cast a Spirit or Arcane spell, Kami of the Painted Road gains protection from the color of your choice until end of turn. - this.addAbility(new SpellCastControllerTriggeredAbility(new GainProtectionFromColorSourceEffect(Duration.EndOfTurn), StaticFilters.SPIRIT_OR_ARCANE_CARD, false)); + this.addAbility(new SpellCastControllerTriggeredAbility(new GainProtectionFromColorSourceEffect(Duration.EndOfTurn), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false)); } diff --git a/Mage.Sets/src/mage/cards/k/KamiOfTheWaningMoon.java b/Mage.Sets/src/mage/cards/k/KamiOfTheWaningMoon.java index 867ff555592..84a3858e174 100644 --- a/Mage.Sets/src/mage/cards/k/KamiOfTheWaningMoon.java +++ b/Mage.Sets/src/mage/cards/k/KamiOfTheWaningMoon.java @@ -29,7 +29,7 @@ public final class KamiOfTheWaningMoon extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); this.addAbility(FlyingAbility.getInstance()); - Ability ability = new SpellCastControllerTriggeredAbility(new GainAbilityTargetEffect(FearAbility.getInstance(), Duration.EndOfTurn), StaticFilters.SPIRIT_OR_ARCANE_CARD, false); + Ability ability = new SpellCastControllerTriggeredAbility(new GainAbilityTargetEffect(FearAbility.getInstance(), Duration.EndOfTurn), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/k/KamiOfTransience.java b/Mage.Sets/src/mage/cards/k/KamiOfTransience.java new file mode 100644 index 00000000000..663d94d0158 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KamiOfTransience.java @@ -0,0 +1,122 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.effects.common.ReturnSourceFromGraveyardToHandEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.FilterSpell; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.watchers.Watcher; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KamiOfTransience extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("an enchantment spell"); + + static { + filter.add(CardType.ENCHANTMENT.getPredicate()); + } + + public KamiOfTransience(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); + + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Whenever you cast an enchantment spell, put a +1/+1 counter on Kami of Transience. + this.addAbility(new SpellCastControllerTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), filter, false + )); + + // At the beginning of each end step, if an enchantment was put into your graveyard from the battlefield this turn, you may return Kami of Transience from your graveyard to your hand. + this.addAbility(new BeginningOfEndStepTriggeredAbility( + Zone.GRAVEYARD, new ReturnSourceFromGraveyardToHandEffect(), + TargetController.ANY, KamiOfTransienceCondition.instance, true + ).addHint(KamiOfTransienceCondition.getHint()), new KamiOfTransienceWatcher()); + } + + private KamiOfTransience(final KamiOfTransience card) { + super(card); + } + + @Override + public KamiOfTransience copy() { + return new KamiOfTransience(this); + } +} + +enum KamiOfTransienceCondition implements Condition { + instance; + private static final Hint hint = new ConditionHint(instance, "An enchantment went to your graveyard this turn"); + + @Override + public boolean apply(Game game, Ability source) { + return KamiOfTransienceWatcher.checkPlayer(game, source); + } + + @Override + public String toString() { + return "if an enchantment was put into your graveyard from the battlefield this turn"; + } + + public static Hint getHint() { + return hint; + } +} + +class KamiOfTransienceWatcher extends Watcher { + + private final Set playerSet = new HashSet<>(); + + KamiOfTransienceWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() != GameEvent.EventType.ZONE_CHANGE) { + return; + } + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + if (!zEvent.isDiesEvent() || !zEvent.getTarget().isEnchantment(game)) { + return; + } + playerSet.add(zEvent.getTarget().getOwnerId()); + } + + @Override + public void reset() { + super.reset(); + playerSet.clear(); + } + + public static boolean checkPlayer(Game game, Ability source) { + return game + .getState() + .getWatcher(KamiOfTransienceWatcher.class) + .playerSet + .contains(source.getControllerId()); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KamisFlare.java b/Mage.Sets/src/mage/cards/k/KamisFlare.java new file mode 100644 index 00000000000..1de874a78ae --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KamisFlare.java @@ -0,0 +1,92 @@ +package mage.cards.k; + +import mage.abilities.Ability; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.permanent.ModifiedPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCreatureOrPlaneswalker; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KamisFlare extends CardImpl { + + public KamisFlare(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{R}"); + + // Kami's Flare deals 3 damage to target creature or planeswalker. Kami's Flare also deals 2 damage to that permanent's controller if you control a modified creature. + this.getSpellAbility().addEffect(new KamisFlareEffect()); + this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalker()); + this.getSpellAbility().addHint(KamisFlareEffect.getHint()); + } + + private KamisFlare(final KamisFlare card) { + super(card); + } + + @Override + public KamisFlare copy() { + return new KamisFlare(this); + } +} + +class KamisFlareEffect extends OneShotEffect { + private static final FilterPermanent filter = new FilterControlledPermanent(); + + static { + filter.add(ModifiedPredicate.instance); + } + + private static final Hint hint = new ConditionHint( + new PermanentsOnTheBattlefieldCondition(filter), "You control a modified creature" + ); + + KamisFlareEffect() { + super(Outcome.Benefit); + staticText = "{this} deals 3 damage to target creature or planeswalker. " + + "{this} also deals 2 damage to that permanent's controller if you control a modified creature"; + } + + private KamisFlareEffect(final KamisFlareEffect effect) { + super(effect); + } + + @Override + public KamisFlareEffect copy() { + return new KamisFlareEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent == null) { + return false; + } + permanent.damage(3, source, game); + if (!game.getBattlefield().contains(filter, source, game, 1)) { + return true; + } + Player player = game.getPlayer(permanent.getControllerId()); + if (player != null) { + player.damage(2, source, game); + } + return true; + } + + public static Hint getHint() { + return hint; + } +} diff --git a/Mage.Sets/src/mage/cards/k/KangeeSkyWarden.java b/Mage.Sets/src/mage/cards/k/KangeeSkyWarden.java index 3670d02910d..b20702eae6a 100644 --- a/Mage.Sets/src/mage/cards/k/KangeeSkyWarden.java +++ b/Mage.Sets/src/mage/cards/k/KangeeSkyWarden.java @@ -2,7 +2,7 @@ package mage.cards.k; import mage.MageInt; import mage.abilities.common.AttacksTriggeredAbility; -import mage.abilities.common.BlocksTriggeredAbility; +import mage.abilities.common.BlocksSourceTriggeredAbility; import mage.abilities.effects.common.continuous.BoostAllEffect; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.VigilanceAbility; @@ -60,7 +60,7 @@ public final class KangeeSkyWarden extends CardImpl { ), false)); // Whenever Kangee blocks, blocking creatures with flying get +0/+2 until end of turn. - this.addAbility(new BlocksTriggeredAbility(new BoostAllEffect( + this.addAbility(new BlocksSourceTriggeredAbility(new BoostAllEffect( 0, 2, Duration.EndOfTurn, filter2, false ), false)); } diff --git a/Mage.Sets/src/mage/cards/k/KappaCannoneer.java b/Mage.Sets/src/mage/cards/k/KappaCannoneer.java new file mode 100644 index 00000000000..7ea0c9a5658 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KappaCannoneer.java @@ -0,0 +1,58 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.combat.CantBeBlockedSourceEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.ImproviseAbility; +import mage.abilities.keyword.WardAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KappaCannoneer extends CardImpl { + + public KappaCannoneer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{5}{U}"); + + this.subtype.add(SubType.TURTLE); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Improvise + this.addAbility(new ImproviseAbility()); + + // Ward {4} + this.addAbility(new WardAbility(new ManaCostsImpl<>("{4}"))); + + // Whenever an artifact enters the battlefield under your control, put a +1/+1 counter on Kappa Cannoneer and it can't be blocked this turn. + Ability ability = new EntersBattlefieldControlledTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), + StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT_AN + ); + ability.addEffect(new CantBeBlockedSourceEffect(Duration.EndOfTurn) + .setText("and it can't be blocked this turn")); + this.addAbility(ability); + } + + private KappaCannoneer(final KappaCannoneer card) { + super(card); + } + + @Override + public KappaCannoneer copy() { + return new KappaCannoneer(this); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KappaTechWrecker.java b/Mage.Sets/src/mage/cards/k/KappaTechWrecker.java new file mode 100644 index 00000000000..95451f47695 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KappaTechWrecker.java @@ -0,0 +1,93 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.costs.common.RemoveCountersSourceCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DoWhenCostPaid; +import mage.abilities.effects.common.ExileTargetEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.NinjutsuAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; +import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.game.Game; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KappaTechWrecker extends CardImpl { + + public KappaTechWrecker(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); + + this.subtype.add(SubType.TURTLE); + this.subtype.add(SubType.NINJA); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // Ninjutsu {1}{G} + this.addAbility(new NinjutsuAbility("{1}{G}")); + + // Kappa Tech-Wrecker enters the battlefield with a deathtouch counter on it. + this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect( + CounterType.DEATHTOUCH.createInstance(1) + ), "with a deathtouch counter on it")); + + // Whenever Kappa Tech-Wrecker deals combat damage to a player, you may remove a deathtouch counter from it. When you do, exile target artifact or enchantment that player controls. + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility( + new KappaTechWreckerEffect(), false, true + )); + } + + private KappaTechWrecker(final KappaTechWrecker card) { + super(card); + } + + @Override + public KappaTechWrecker copy() { + return new KappaTechWrecker(this); + } +} + +class KappaTechWreckerEffect extends OneShotEffect { + + KappaTechWreckerEffect() { + super(Outcome.Benefit); + staticText = "you may remove a deathtouch counter from it. " + + "When you do, exile target artifact or enchantment that player controls"; + } + + private KappaTechWreckerEffect(final KappaTechWreckerEffect effect) { + super(effect); + } + + @Override + public KappaTechWreckerEffect copy() { + return new KappaTechWreckerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + FilterPermanent filter = new FilterArtifactOrEnchantmentPermanent("artifact or enchantment that player controls"); + filter.add(new ControllerIdPredicate(getTargetPointer().getFirst(game, source))); + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility(new ExileTargetEffect(), false); + ability.addTarget(new TargetPermanent(filter)); + return new DoWhenCostPaid( + ability, new RemoveCountersSourceCost(CounterType.DEATHTOUCH.createInstance()), + "Remove a deathtouch counter?" + ).apply(game, source); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KardursViciousReturn.java b/Mage.Sets/src/mage/cards/k/KardursViciousReturn.java index 36ff9e8d073..86fde8115ed 100644 --- a/Mage.Sets/src/mage/cards/k/KardursViciousReturn.java +++ b/Mage.Sets/src/mage/cards/k/KardursViciousReturn.java @@ -37,7 +37,7 @@ public final class KardursViciousReturn extends CardImpl { this.subtype.add(SubType.SAGA); // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) - SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); + SagaAbility sagaAbility = new SagaAbility(this); // I — You may sacrifice a creature. When you do, Kardur's Vicious Return deals 3 damage to any target. ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/k/KariZevSkyshipRaider.java b/Mage.Sets/src/mage/cards/k/KariZevSkyshipRaider.java index c1daa7bf169..d028250a9f7 100644 --- a/Mage.Sets/src/mage/cards/k/KariZevSkyshipRaider.java +++ b/Mage.Sets/src/mage/cards/k/KariZevSkyshipRaider.java @@ -38,7 +38,7 @@ public final class KariZevSkyshipRaider extends CardImpl { this.addAbility(FirstStrikeAbility.getInstance()); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // Whenever Kari Zev, Skyship Raider attacks, create a legendary 2/1 red Monkey creature token named Ragavan that's tapped and attacking. Exile that token at end of combat. this.addAbility(new AttacksTriggeredAbility(new KariZevSkyshipRaiderEffect(), false)); @@ -58,7 +58,7 @@ class KariZevSkyshipRaiderEffect extends OneShotEffect { KariZevSkyshipRaiderEffect() { super(Outcome.PutCreatureInPlay); - staticText = "create Ragaven, a legendary 2/1 red Monkey creature token. Ragavan enters the battlefied tapped and attacking. Exile that token at end of combat"; + staticText = "create Ragavan, a legendary 2/1 red Monkey creature token. Ragavan enters the battlefield tapped and attacking. Exile that token at end of combat"; } KariZevSkyshipRaiderEffect(final KariZevSkyshipRaiderEffect effect) { diff --git a/Mage.Sets/src/mage/cards/k/KarnLiberated.java b/Mage.Sets/src/mage/cards/k/KarnLiberated.java index f9fb4e0cfd1..0908f788ed5 100644 --- a/Mage.Sets/src/mage/cards/k/KarnLiberated.java +++ b/Mage.Sets/src/mage/cards/k/KarnLiberated.java @@ -4,7 +4,6 @@ import mage.MageObject; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ExileTargetForSourceEffect; import mage.cards.*; @@ -35,7 +34,7 @@ public final class KarnLiberated extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{7}"); this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.KARN); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(6)); + this.setStartingLoyalty(6); // +4: Target player exiles a card from their hand. LoyaltyAbility ability1 = new LoyaltyAbility(new KarnPlayerExileEffect(), 4); diff --git a/Mage.Sets/src/mage/cards/k/KarnScionOfUrza.java b/Mage.Sets/src/mage/cards/k/KarnScionOfUrza.java index ed5784b42a6..3192b09c4c1 100644 --- a/Mage.Sets/src/mage/cards/k/KarnScionOfUrza.java +++ b/Mage.Sets/src/mage/cards/k/KarnScionOfUrza.java @@ -3,7 +3,6 @@ package mage.cards.k; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.hint.common.ArtifactYouControlHint; @@ -33,7 +32,7 @@ public final class KarnScionOfUrza extends CardImpl { addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.KARN); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +1: Reveal the top two cards of your library. An opponent chooses one of them. Put that card into your hand and exile the other with a silver counter on it. LoyaltyAbility ability1 = new LoyaltyAbility(new KarnPlus1Effect(), 1); diff --git a/Mage.Sets/src/mage/cards/k/KarnTheGreatCreator.java b/Mage.Sets/src/mage/cards/k/KarnTheGreatCreator.java index 9b03477534b..0c28284bed8 100644 --- a/Mage.Sets/src/mage/cards/k/KarnTheGreatCreator.java +++ b/Mage.Sets/src/mage/cards/k/KarnTheGreatCreator.java @@ -2,7 +2,6 @@ package mage.cards.k; 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.RestrictionEffect; @@ -38,7 +37,7 @@ public final class KarnTheGreatCreator extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.KARN); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // Activated abilities of artifacts your opponents control can't be activated. this.addAbility(new SimpleStaticAbility(new KarnTheGreatCreatorCantActivateEffect())); @@ -48,9 +47,9 @@ public final class KarnTheGreatCreator extends CardImpl { ability.addTarget(new TargetPermanent(0, 1, filter, false)); this.addAbility(ability); - // -2: You may choose an artifact card you own from outside the game or in exile, reveal that card, and put it into your hand. + // -2: You may reveal an artifact card you own from outside the game or choose a face-up artifact card you own in exile. Put that card into your hand. this.addAbility(new LoyaltyAbility(new WishEffect( - StaticFilters.FILTER_CARD_ARTIFACT_AN, true, true + StaticFilters.FILTER_CARD_ARTIFACT, true ), -2).addHint(OpenSideboardHint.instance)); } diff --git a/Mage.Sets/src/mage/cards/k/KaronaFalseGod.java b/Mage.Sets/src/mage/cards/k/KaronaFalseGod.java index d373e173549..f4874f657c7 100644 --- a/Mage.Sets/src/mage/cards/k/KaronaFalseGod.java +++ b/Mage.Sets/src/mage/cards/k/KaronaFalseGod.java @@ -1,4 +1,3 @@ - package mage.cards.k; import java.util.UUID; @@ -95,7 +94,7 @@ class KaronaFalseGodUntapGetControlEffect extends OneShotEffect { ContinuousEffect effect = new GainControlTargetEffect(Duration.Custom, true, newController.getId()); effect.setValue("KaronaFalseGodSourceId", source.getSourceId()); effect.setValue("KaronaFalseGodControllerId", newController.getId()); - effect.setTargetPointer(new FixedTarget(sourcePermanent.getId())); + effect.setTargetPointer(new FixedTarget(sourcePermanent.getId(), game)); effect.setText("and gains control of it"); game.addEffect(effect, source); return true; diff --git a/Mage.Sets/src/mage/cards/k/KarrthusTyrantOfJund.java b/Mage.Sets/src/mage/cards/k/KarrthusTyrantOfJund.java index a6f9211bdfa..31e44588e99 100644 --- a/Mage.Sets/src/mage/cards/k/KarrthusTyrantOfJund.java +++ b/Mage.Sets/src/mage/cards/k/KarrthusTyrantOfJund.java @@ -90,7 +90,7 @@ class KarrthusEffect extends OneShotEffect { List dragons = game.getBattlefield().getAllActivePermanents(filter, game); for (Permanent dragon : dragons) { ContinuousEffect effect = new KarrthusControlEffect(source.getControllerId()); - effect.setTargetPointer(new FixedTarget(dragon.getId())); + effect.setTargetPointer(new FixedTarget(dragon.getId(), game)); game.addEffect(effect, source); } for (Permanent dragon : dragons) { diff --git a/Mage.Sets/src/mage/cards/k/KasminaEnigmaSage.java b/Mage.Sets/src/mage/cards/k/KasminaEnigmaSage.java index 942c8dfae8b..f0cc27190e6 100644 --- a/Mage.Sets/src/mage/cards/k/KasminaEnigmaSage.java +++ b/Mage.Sets/src/mage/cards/k/KasminaEnigmaSage.java @@ -3,7 +3,6 @@ package mage.cards.k; import mage.ApprovingObject; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.dynamicvalue.common.GetXLoyaltyValue; import mage.abilities.effects.ContinuousEffectImpl; @@ -37,13 +36,13 @@ public final class KasminaEnigmaSage extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.KASMINA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(2)); + this.setStartingLoyalty(2); // Each other planeswalker you control has the loyalty abilities of Kasmina, Enigma Sage. this.addAbility(new SimpleStaticAbility(new KasminaEnigmaSageGainAbilitiesEffect())); // +2: Scry 1. - this.addAbility(new LoyaltyAbility(new ScryEffect(1), 2)); + this.addAbility(new LoyaltyAbility(new ScryEffect(1, false), 2)); // −X: Create a 0/0 green and blue Fractal creature token. Put X +1/+1 counters on it. this.addAbility(new LoyaltyAbility(FractalToken.getEffect( diff --git a/Mage.Sets/src/mage/cards/k/KasminaEnigmaticMentor.java b/Mage.Sets/src/mage/cards/k/KasminaEnigmaticMentor.java index 1d88da9af44..fef968cad21 100644 --- a/Mage.Sets/src/mage/cards/k/KasminaEnigmaticMentor.java +++ b/Mage.Sets/src/mage/cards/k/KasminaEnigmaticMentor.java @@ -3,7 +3,6 @@ package mage.cards.k; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.SpellAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.DrawDiscardControllerEffect; @@ -31,7 +30,7 @@ public final class KasminaEnigmaticMentor extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.KASMINA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // Spells your opponents cast that target a creature or planeswalker you control cost {2} more to cast. this.addAbility(new SimpleStaticAbility(new KasminaEnigmaticMentorCostModificationEffect())); diff --git a/Mage.Sets/src/mage/cards/k/KatildaDawnhartMartyr.java b/Mage.Sets/src/mage/cards/k/KatildaDawnhartMartyr.java new file mode 100644 index 00000000000..57b76800392 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KatildaDawnhartMartyr.java @@ -0,0 +1,80 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.continuous.SetPowerToughnessSourceEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.DisturbAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.LifelinkAbility; +import mage.abilities.keyword.ProtectionAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KatildaDawnhartMartyr extends CardImpl { + + private static final FilterPermanent filter + = new FilterPermanent(SubType.VAMPIRE, "Vampires"); + private static final FilterPermanent filter2 + = new FilterControlledPermanent("permanents you control that are Spirits and/or enchantments"); + + static { + filter2.add(Predicates.or( + SubType.SPIRIT.getPredicate(), + CardType.ENCHANTMENT.getPredicate() + )); + } + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter2); + private static final Hint hint = new ValueHint("Spirits and enchantments you control", xValue); + + public KatildaDawnhartMartyr(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.SPIRIT); + this.subtype.add(SubType.WARLOCK); + this.power = new MageInt(0); + this.toughness = new MageInt(0); + this.secondSideCardClazz = mage.cards.k.KatildasRisingDawn.class; + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // Protection from Vampires + this.addAbility(new ProtectionAbility(filter)); + + // Katilda, Dawnhart Martyr's power and toughness are each equal to the number of permanents you control that are Spirits and/or enchantments. + this.addAbility(new SimpleStaticAbility( + Zone.ALL, new SetPowerToughnessSourceEffect(xValue, Duration.EndOfGame) + ).addHint(hint)); + + // Disturb {3}{W}{W} + this.addAbility(new DisturbAbility(this, "{3}{W}{W}")); + } + + private KatildaDawnhartMartyr(final KatildaDawnhartMartyr card) { + super(card); + } + + @Override + public KatildaDawnhartMartyr copy() { + return new KatildaDawnhartMartyr(this); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KatildasRisingDawn.java b/Mage.Sets/src/mage/cards/k/KatildasRisingDawn.java new file mode 100644 index 00000000000..eaeb8cf2566 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KatildasRisingDawn.java @@ -0,0 +1,89 @@ +package mage.cards.k; + +import mage.abilities.Ability; +import mage.abilities.common.PutIntoGraveFromAnywhereSourceAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.ExileSourceEffect; +import mage.abilities.effects.common.continuous.BoostEquippedEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.EnchantAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.LifelinkAbility; +import mage.abilities.keyword.ProtectionAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KatildasRisingDawn extends CardImpl { + + private static final FilterPermanent filter + = new FilterPermanent(SubType.VAMPIRE, "Vampires"); + private static final FilterPermanent filter2 + = new FilterControlledPermanent("permanents you control that are Spirits and/or enchantments"); + + static { + filter2.add(Predicates.or( + SubType.SPIRIT.getPredicate(), + CardType.ENCHANTMENT.getPredicate() + )); + } + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter2); + private static final Hint hint = new ValueHint("Spirits and enchantments you control", xValue); + + public KatildasRisingDawn(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, ""); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.AURA); + this.color.setWhite(true); + this.nightCard = true; + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + this.addAbility(new EnchantAbility(auraTarget.getTargetName())); + + // Enchanted creature has flying, lifelink, and protection from Vampires, and it gets +X/+X where X is the number of permanents you control that are Spirits and/or enchantments. + Ability ability = new SimpleStaticAbility(new GainAbilityAttachedEffect( + FlyingAbility.getInstance(), AttachmentType.AURA + )); + ability.addEffect(new GainAbilityAttachedEffect( + LifelinkAbility.getInstance(), AttachmentType.AURA + ).setText(", lifelink")); + ability.addEffect(new GainAbilityAttachedEffect( + new ProtectionAbility(filter), AttachmentType.AURA + ).setText(", and protection from Vampires")); + ability.addEffect(new BoostEquippedEffect(xValue, xValue) + .setText(", and it gets +X/+X, where X is the number of permanents you control that are Spirits and/or enchantments")); + this.addAbility(ability.addHint(hint)); + + // If Katilda's Rising Dawn would be put into a graveyard from anywhere, exile it instead. + this.addAbility(new PutIntoGraveFromAnywhereSourceAbility(new ExileSourceEffect().setText("exile it instead"))); + } + + private KatildasRisingDawn(final KatildasRisingDawn card) { + super(card); + } + + @Override + public KatildasRisingDawn copy() { + return new KatildasRisingDawn(this); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KatsumasaTheAnimator.java b/Mage.Sets/src/mage/cards/k/KatsumasaTheAnimator.java new file mode 100644 index 00000000000..df4f6e83499 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KatsumasaTheAnimator.java @@ -0,0 +1,114 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.AddCardTypeTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.continuous.SetPowerToughnessTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterArtifactPermanent; +import mage.filter.common.FilterControlledArtifactPermanent; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KatsumasaTheAnimator extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledArtifactPermanent("noncreature artifact you control"); + private static final FilterPermanent filter2 + = new FilterArtifactPermanent("noncreature artifacts"); + + static { + filter.add(Predicates.not(CardType.CREATURE.getPredicate())); + filter2.add(Predicates.not(CardType.CREATURE.getPredicate())); + } + + public KatsumasaTheAnimator(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.MOONFOLK); + this.subtype.add(SubType.ARTIFICER); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // {2}{U}: Until end of turn, target noncreature artifact you control becomes an artifact creature and gains flying. If it's not a Vehicle, it has base power and toughness 1/1 until end of turn. + Ability ability = new SimpleActivatedAbility(new KatsumasaTheAnimatorEffect(), new ManaCostsImpl<>("{2}{U}")); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + + // At the beginning of your upkeep, put a +1/+1 counter on each of up to three target noncreature artifacts. + ability = new BeginningOfUpkeepTriggeredAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance()), TargetController.YOU, false + ); + ability.addTarget(new TargetPermanent(0, 3, filter2)); + this.addAbility(ability); + } + + private KatsumasaTheAnimator(final KatsumasaTheAnimator card) { + super(card); + } + + @Override + public KatsumasaTheAnimator copy() { + return new KatsumasaTheAnimator(this); + } +} + +class KatsumasaTheAnimatorEffect extends OneShotEffect { + + KatsumasaTheAnimatorEffect() { + super(Outcome.Benefit); + staticText = "until end of turn, target noncreature artifact you control becomes an artifact creature " + + "and gains flying. If it's not a Vehicle, it has base power and toughness 1/1 until end of turn"; + } + + private KatsumasaTheAnimatorEffect(final KatsumasaTheAnimatorEffect effect) { + super(effect); + } + + @Override + public KatsumasaTheAnimatorEffect copy() { + return new KatsumasaTheAnimatorEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent == null) { + return false; + } + game.addEffect(new AddCardTypeTargetEffect( + Duration.EndOfTurn, CardType.ARTIFACT, CardType.CREATURE + ), source); + game.addEffect(new GainAbilityTargetEffect( + FlyingAbility.getInstance(), Duration.EndOfTurn + ), source); + if (permanent.hasSubtype(SubType.VEHICLE, game)) { + game.addEffect(new SetPowerToughnessTargetEffect( + 1, 1, Duration.EndOfTurn + ), source); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/k/KavuMauler.java b/Mage.Sets/src/mage/cards/k/KavuMauler.java index 86b80f5afa1..f85a83a2ff9 100644 --- a/Mage.Sets/src/mage/cards/k/KavuMauler.java +++ b/Mage.Sets/src/mage/cards/k/KavuMauler.java @@ -1,9 +1,9 @@ - package mage.cards.k; 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.effects.common.continuous.BoostSourceEffect; import mage.abilities.keyword.TrampleAbility; @@ -28,6 +28,8 @@ public final class KavuMauler extends CardImpl { filter.add(AnotherPredicate.instance); } + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter); + public KavuMauler(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{G}{G}"); this.subtype.add(SubType.KAVU); @@ -37,8 +39,7 @@ public final class KavuMauler extends CardImpl { // Trample this.addAbility(TrampleAbility.getInstance()); // Whenever Kavu Mauler attacks, it gets +1/+1 until end of turn for each other attacking Kavu. - PermanentsOnBattlefieldCount value = new PermanentsOnBattlefieldCount(filter); - this.addAbility(new AttacksTriggeredAbility(new BoostSourceEffect(value, value, Duration.EndOfTurn, true), false)); + this.addAbility(new AttacksTriggeredAbility(new BoostSourceEffect(xValue, xValue, Duration.EndOfTurn, true, "it"), false)); } private KavuMauler(final KavuMauler card) { diff --git a/Mage.Sets/src/mage/cards/k/KavuRunner.java b/Mage.Sets/src/mage/cards/k/KavuRunner.java index 6662584229d..45c08991051 100644 --- a/Mage.Sets/src/mage/cards/k/KavuRunner.java +++ b/Mage.Sets/src/mage/cards/k/KavuRunner.java @@ -44,7 +44,7 @@ public final class KavuRunner extends CardImpl { // Kavu Runner has haste as long as no opponent controls a white or blue creature. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(new GainAbilitySourceEffect(HasteAbility.getInstance(), - Duration.WhileOnBattlefield), new InvertCondition(new PermanentsOnTheBattlefieldCondition(filter, ComparisonType.MORE_THAN, 0, false)), + Duration.WhileOnBattlefield), new InvertCondition(new PermanentsOnTheBattlefieldCondition(filter, false)), "{this} has haste as long as no opponent controls a white or blue creature"))); } diff --git a/Mage.Sets/src/mage/cards/k/KayaBaneOfTheDead.java b/Mage.Sets/src/mage/cards/k/KayaBaneOfTheDead.java index 13ab5564c8d..542a24cbce8 100644 --- a/Mage.Sets/src/mage/cards/k/KayaBaneOfTheDead.java +++ b/Mage.Sets/src/mage/cards/k/KayaBaneOfTheDead.java @@ -2,7 +2,6 @@ package mage.cards.k; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.common.ExileTargetEffect; @@ -25,7 +24,7 @@ public final class KayaBaneOfTheDead extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.KAYA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(7)); + this.setStartingLoyalty(7); // Your opponents and permanents your opponents control with hexproof can be the target of spells and abilities you control as though they didn't have hexproof. this.addAbility(new SimpleStaticAbility(new KayaBaneOfTheDeadEffect())); @@ -50,7 +49,7 @@ class KayaBaneOfTheDeadEffect extends AsThoughEffectImpl { KayaBaneOfTheDeadEffect() { super(AsThoughEffectType.HEXPROOF, Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = "your opponents and creatures with hexproof they control " + + staticText = "your opponents and permanents your opponents control with hexproof " + "can be the targets of spells and abilities you control as though they didn't have hexproof"; } diff --git a/Mage.Sets/src/mage/cards/k/KayaGeistHunter.java b/Mage.Sets/src/mage/cards/k/KayaGeistHunter.java new file mode 100644 index 00000000000..36c28eb247a --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KayaGeistHunter.java @@ -0,0 +1,115 @@ +package mage.cards.k; + +import mage.abilities.Ability; +import mage.abilities.LoyaltyAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.effects.common.replacement.CreateTwiceThatManyTokensEffect; +import mage.abilities.keyword.DeathtouchAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.Game; +import mage.game.permanent.token.SpiritWhiteToken; +import mage.players.Player; +import mage.target.TargetPermanent; + +import java.util.Collection; +import java.util.Objects; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KayaGeistHunter extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("creature token you control"); + + static { + filter.add(TokenPredicate.TRUE); + } + + public KayaGeistHunter(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{1}{W}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.KAYA); + this.setStartingLoyalty(3); + + // +1: Creatures you control gain deathtouch until end of turn. Put a +1/+1 counter on up to one target creature token you control. + Ability ability = new LoyaltyAbility(new GainAbilityControlledEffect( + DeathtouchAbility.getInstance(), Duration.EndOfTurn, + StaticFilters.FILTER_PERMANENT_CREATURES + ), 1); + ability.addEffect(new AddCountersTargetEffect(CounterType.P1P1.createInstance())); + ability.addTarget(new TargetPermanent(0, 1, filter)); + this.addAbility(ability); + + // −2: Until end of turn, if one or more tokens would be created under your control, twice that many of those tokens are created instead. + this.addAbility(new LoyaltyAbility(new CreateTwiceThatManyTokensEffect(Duration.EndOfTurn), -2)); + + // −6: Exile all cards from all graveyards, then create a 1/1 white Spirit creature token with flying for each card exiled this way. + this.addAbility(new LoyaltyAbility(new KayaGeistHunterEffect(), -6)); + } + + private KayaGeistHunter(final KayaGeistHunter card) { + super(card); + } + + @Override + public KayaGeistHunter copy() { + return new KayaGeistHunter(this); + } +} + +class KayaGeistHunterEffect extends OneShotEffect { + + KayaGeistHunterEffect() { + super(Outcome.Benefit); + staticText = "exile all cards from all graveyards, then create " + + "a 1/1 white Spirit creature token with flying for each card exiled this way"; + } + + private KayaGeistHunterEffect(final KayaGeistHunterEffect effect) { + super(effect); + } + + @Override + public KayaGeistHunterEffect copy() { + return new KayaGeistHunterEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Cards cards = new CardsImpl(); + game.getState() + .getPlayersInRange(source.getControllerId(), game) + .stream() + .map(game::getPlayer) + .filter(Objects::nonNull) + .map(Player::getGraveyard) + .map(g -> g.getCards(game)) + .flatMap(Collection::stream) + .forEach(cards::add); + player.moveCards(cards, Zone.EXILED, source, game); + cards.retainZone(Zone.EXILED, game); + if (cards.isEmpty()) { + return true; + } + new SpiritWhiteToken().putOntoBattlefield(cards.size(), game, source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/k/KayaGhostAssassin.java b/Mage.Sets/src/mage/cards/k/KayaGhostAssassin.java index 75183e44148..b30e9f50509 100644 --- a/Mage.Sets/src/mage/cards/k/KayaGhostAssassin.java +++ b/Mage.Sets/src/mage/cards/k/KayaGhostAssassin.java @@ -2,7 +2,6 @@ package mage.cards.k; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.delayed.AtTheBeginOfYourNextUpkeepDelayedTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; @@ -35,7 +34,7 @@ public final class KayaGhostAssassin extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.KAYA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // 0: Exile Kaya, Ghost Assassin or up to one target creature. Return that card to the battlefield under its owner's control at the beginning of your next upkeep. // You lose 2 life. diff --git a/Mage.Sets/src/mage/cards/k/KayaOrzhovUsurper.java b/Mage.Sets/src/mage/cards/k/KayaOrzhovUsurper.java index 5b9dc2e3801..b2a67af4736 100644 --- a/Mage.Sets/src/mage/cards/k/KayaOrzhovUsurper.java +++ b/Mage.Sets/src/mage/cards/k/KayaOrzhovUsurper.java @@ -2,7 +2,6 @@ package mage.cards.k; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ExileTargetEffect; import mage.cards.*; @@ -37,7 +36,7 @@ public final class KayaOrzhovUsurper extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.KAYA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +1: Exile up to two target cards from a single graveyard. You gain 2 life if at least one creature card was exiled this way. Ability ability = new LoyaltyAbility(new KayaOrzhovUsurperExileEffect(), 1); diff --git a/Mage.Sets/src/mage/cards/k/KayaTheInexorable.java b/Mage.Sets/src/mage/cards/k/KayaTheInexorable.java index a5109c9bdd3..c919a5adfcc 100644 --- a/Mage.Sets/src/mage/cards/k/KayaTheInexorable.java +++ b/Mage.Sets/src/mage/cards/k/KayaTheInexorable.java @@ -5,7 +5,6 @@ import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.ExileTargetEffect; @@ -47,7 +46,7 @@ public final class KayaTheInexorable extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.KAYA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +1: Put a ghostform counter on up to one target nontoken creature. It gains "When this creature dies or is put into exile, return it to its owner's hand and create a 1/1 white Spirit creature token with flying." LoyaltyAbility ability = new LoyaltyAbility(new AddCountersTargetEffect(CounterType.GHOSTFORM.createInstance()), 1); diff --git a/Mage.Sets/src/mage/cards/k/KayasGuile.java b/Mage.Sets/src/mage/cards/k/KayasGuile.java index 8526d2fdec2..413d02acfca 100644 --- a/Mage.Sets/src/mage/cards/k/KayasGuile.java +++ b/Mage.Sets/src/mage/cards/k/KayasGuile.java @@ -36,7 +36,7 @@ public final class KayasGuile extends CardImpl { // • Exile all cards from each opponent's graveyard. this.getSpellAbility().addMode(new Mode(new ExileGraveyardAllPlayersEffect( StaticFilters.FILTER_CARD_CARDS, TargetController.OPPONENT - ).setText("exile all cards from each opponent's graveyard"))); + ))); // • Create a 1/1 white and black Spirit creature token with flying. this.getSpellAbility().addMode(new Mode(new CreateTokenEffect(new WhiteBlackSpiritToken()))); diff --git a/Mage.Sets/src/mage/cards/k/KazaRoilChaser.java b/Mage.Sets/src/mage/cards/k/KazaRoilChaser.java index 1beb5ca09df..af14498a2c2 100644 --- a/Mage.Sets/src/mage/cards/k/KazaRoilChaser.java +++ b/Mage.Sets/src/mage/cards/k/KazaRoilChaser.java @@ -141,7 +141,7 @@ class KazaRoilChaserWatcher extends Watcher { } Spell spell = game.getSpell(event.getSourceId()); if (spell != null && spell.isInstantOrSorcery(game)) { - playerMap.compute(event.getPlayerId(), (u, i) -> i == null ? 1 : Integer.sum(i, 1)); + playerMap.compute(event.getPlayerId(), CardUtil::setOrIncrementValue); } } diff --git a/Mage.Sets/src/mage/cards/k/KeepSafe.java b/Mage.Sets/src/mage/cards/k/KeepSafe.java index a7a676677e6..86857f6a1fe 100644 --- a/Mage.Sets/src/mage/cards/k/KeepSafe.java +++ b/Mage.Sets/src/mage/cards/k/KeepSafe.java @@ -31,7 +31,7 @@ public final class KeepSafe extends CardImpl { this.getSpellAbility().addTarget(new TargetSpell(filter)); // Draw a card. - this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("
")); } private KeepSafe(final KeepSafe card) { diff --git a/Mage.Sets/src/mage/cards/k/KeeperOfKeys.java b/Mage.Sets/src/mage/cards/k/KeeperOfKeys.java index 0fc48caacd0..0081b0b7694 100644 --- a/Mage.Sets/src/mage/cards/k/KeeperOfKeys.java +++ b/Mage.Sets/src/mage/cards/k/KeeperOfKeys.java @@ -16,7 +16,7 @@ import mage.constants.SubType; import mage.constants.Duration; import mage.constants.TargetController; import mage.constants.Zone; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; /** * @@ -38,7 +38,7 @@ public final class KeeperOfKeys extends CardImpl { // At the beginning of your upkeep, if you're the monarch, creatures you control can't be blocked this turn. this.addAbility(new ConditionalInterveningIfTriggeredAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, - new CantBeBlockedAllEffect(new FilterControlledCreaturePermanent("creatures you control"), Duration.EndOfTurn), + new CantBeBlockedAllEffect(StaticFilters.FILTER_CONTROLLED_CREATURES, Duration.EndOfTurn), TargetController.YOU, false), MonarchIsSourceControllerCondition.instance, "At the beginning of your upkeep, if you're the monarch, creatures you control can't be blocked this turn.")); } diff --git a/Mage.Sets/src/mage/cards/k/KeeperOfTheDead.java b/Mage.Sets/src/mage/cards/k/KeeperOfTheDead.java index 387a3a2442d..5dc7fef6231 100644 --- a/Mage.Sets/src/mage/cards/k/KeeperOfTheDead.java +++ b/Mage.Sets/src/mage/cards/k/KeeperOfTheDead.java @@ -1,4 +1,3 @@ - package mage.cards.k; import java.util.HashSet; @@ -6,7 +5,6 @@ import java.util.Set; import java.util.UUID; import mage.MageInt; import mage.MageObject; -import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; @@ -18,13 +16,11 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.FilterCard; import mage.filter.FilterPlayer; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.ObjectSourcePlayer; import mage.filter.predicate.ObjectSourcePlayerPredicate; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ColorPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.stack.StackObject; @@ -39,13 +35,9 @@ import mage.target.TargetPlayer; public final class KeeperOfTheDead extends CardImpl { private static final FilterPlayer filter = new FilterPlayer(); - private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent("nonblack creature"); - private static final FilterCard filter3 = new FilterCard("creature cards"); static { filter.add(new KeeperOfDeadPredicate()); - filter2.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - filter3.add(CardType.CREATURE.getPredicate()); } public KeeperOfTheDead(UUID ownerId, CardSetInfo setInfo) { @@ -76,12 +68,6 @@ public final class KeeperOfTheDead extends CardImpl { class KeeperOfDeadPredicate implements ObjectSourcePlayerPredicate { - private static final FilterCard filter = new FilterCard("creature cards"); - - static { - filter.add(CardType.CREATURE.getPredicate()); - } - @Override public boolean apply(ObjectSourcePlayer input, Game game) { Player targetPlayer = input.getObject(); @@ -96,8 +82,8 @@ class KeeperOfDeadPredicate implements ObjectSourcePlayerPredicate { || !controller.hasOpponent(targetPlayer.getId(), game)) { return false; } - int countGraveyardTargetPlayer = targetPlayer.getGraveyard().getCards(filter, game).size(); - int countGraveyardController = controller.getGraveyard().getCards(filter, game).size(); + int countGraveyardTargetPlayer = targetPlayer.getGraveyard().getCards(StaticFilters.FILTER_CARD_CREATURES, game).size(); + int countGraveyardController = controller.getGraveyard().getCards(StaticFilters.FILTER_CARD_CREATURES, game).size(); return countGraveyardController >= countGraveyardTargetPlayer + 2; } @@ -109,12 +95,6 @@ class KeeperOfDeadPredicate implements ObjectSourcePlayerPredicate { class KeeperOfTheDeadCreatureTarget extends TargetPermanent { - private static final FilterCreaturePermanent nonblackCreaturefilter = new FilterCreaturePermanent("nonblack creature"); - - static { - nonblackCreaturefilter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public KeeperOfTheDeadCreatureTarget() { super(1, 1, new FilterCreaturePermanent("nonblack creature that player controls"), false); } @@ -152,7 +132,7 @@ class KeeperOfTheDeadCreatureTarget extends TargetPermanent { UUID playerId = ((StackObject) object).getStackAbility().getFirstTarget(); for (UUID targetId : availablePossibleTargets) { Permanent permanent = game.getPermanent(targetId); - if (permanent != null && nonblackCreaturefilter.match(permanent, game) && permanent.isControlledBy(playerId)) { + if (permanent != null && StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK.match(permanent, game) && permanent.isControlledBy(playerId)) { possibleTargets.add(targetId); } } diff --git a/Mage.Sets/src/mage/cards/k/KefnetsMonument.java b/Mage.Sets/src/mage/cards/k/KefnetsMonument.java index 4fbee3bf2c2..db40016f92f 100644 --- a/Mage.Sets/src/mage/cards/k/KefnetsMonument.java +++ b/Mage.Sets/src/mage/cards/k/KefnetsMonument.java @@ -12,11 +12,10 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SuperType; -import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.FilterSpell; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ColorPredicate; import mage.target.common.TargetCreaturePermanent; @@ -29,18 +28,11 @@ public final class KefnetsMonument extends CardImpl { private static final FilterCard filter = new FilterCard("Blue creature spells"); private static final FilterSpell filter2 = new FilterSpell("a creature spell"); - private static final FilterCreaturePermanent filter3 = new FilterCreaturePermanent("creature an opponent controls"); static { filter.add(Predicates.and(new ColorPredicate(ObjectColor.BLUE), CardType.CREATURE.getPredicate())); - } - static { filter2.add(CardType.CREATURE.getPredicate()); } - static { - filter3.add(TargetController.OPPONENT.getControllerPredicate()); - } - public KefnetsMonument(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); @@ -52,7 +44,7 @@ public final class KefnetsMonument extends CardImpl { // Whenever you cast a creature spell, target creature an opponent controls doesn't untap during its controller's next untap step. Ability ability = new SpellCastControllerTriggeredAbility(new DontUntapInControllersNextUntapStepTargetEffect(), filter2, false); - ability.addTarget(new TargetCreaturePermanent(filter3)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/k/KelsFightFixer.java b/Mage.Sets/src/mage/cards/k/KelsFightFixer.java index b6b408de692..c36ecf32b57 100644 --- a/Mage.Sets/src/mage/cards/k/KelsFightFixer.java +++ b/Mage.Sets/src/mage/cards/k/KelsFightFixer.java @@ -37,7 +37,7 @@ public final class KelsFightFixer extends CardImpl { this.toughness = new MageInt(3); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // Whenever you sacrifice a creature, you may pay {U/B}. If you do, draw a card. this.addAbility(new KelsFightFixerTriggeredAbility()); diff --git a/Mage.Sets/src/mage/cards/k/KemuriOnna.java b/Mage.Sets/src/mage/cards/k/KemuriOnna.java index 7d586928e29..ac916b867b4 100644 --- a/Mage.Sets/src/mage/cards/k/KemuriOnna.java +++ b/Mage.Sets/src/mage/cards/k/KemuriOnna.java @@ -33,7 +33,7 @@ public final class KemuriOnna extends CardImpl { ability.addTarget(new TargetPlayer()); this.addAbility(ability); // Whenever you cast a Spirit or Arcane spell, you may return Kemuri-Onna to its owner's hand. - this.addAbility(new SpellCastControllerTriggeredAbility(new ReturnToHandSourceEffect(true), StaticFilters.SPIRIT_OR_ARCANE_CARD, true)); + this.addAbility(new SpellCastControllerTriggeredAbility(new ReturnToHandSourceEffect(true), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true)); } private KemuriOnna(final KemuriOnna card) { diff --git a/Mage.Sets/src/mage/cards/k/KenBurningBrawler.java b/Mage.Sets/src/mage/cards/k/KenBurningBrawler.java new file mode 100644 index 00000000000..bedfec5b32c --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KenBurningBrawler.java @@ -0,0 +1,89 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DealsCombatDamageTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.effects.common.cost.CastWithoutPayingManaCostEffect; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.abilities.keyword.ProwessAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.game.Game; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KenBurningBrawler extends CardImpl { + + public KenBurningBrawler(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}{R}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(4); + this.toughness = new MageInt(2); + + // Prowess + this.addAbility(new ProwessAbility()); + + // {R/W}: Ken gains first strike until end of turn. + this.addAbility(new SimpleActivatedAbility(new GainAbilitySourceEffect( + FirstStrikeAbility.getInstance(), Duration.EndOfTurn + ), new ManaCostsImpl<>("{R/W}"))); + + // Shoryuken—Whenever Ken deals combat damage, you may cast a sorcery spell from your hand with mana value less than or equal to that damage without paying its mana cost. + this.addAbility(new DealsCombatDamageTriggeredAbility( + new KenBurningBrawlerEffect(), false + ).withFlavorWord("Shoryuken")); + } + + private KenBurningBrawler(final KenBurningBrawler card) { + super(card); + } + + @Override + public KenBurningBrawler copy() { + return new KenBurningBrawler(this); + } +} + +class KenBurningBrawlerEffect extends OneShotEffect { + + private static final FilterCard filter = new FilterCard("sorcery spell"); + + static { + filter.add(CardType.SORCERY.getPredicate()); + } + + KenBurningBrawlerEffect() { + super(Outcome.Benefit); + staticText = "you may cast a sorcery spell from your hand with mana value " + + "less than or equal to that damage without paying its mana cost"; + } + + private KenBurningBrawlerEffect(final KenBurningBrawlerEffect effect) { + super(effect); + } + + @Override + public KenBurningBrawlerEffect copy() { + return new KenBurningBrawlerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return new CastWithoutPayingManaCostEffect( + StaticValue.get((Integer) getValue("damage")), filter + ).apply(game, source); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KessigFlamebreather.java b/Mage.Sets/src/mage/cards/k/KessigFlamebreather.java new file mode 100644 index 00000000000..5d75485ae88 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KessigFlamebreather.java @@ -0,0 +1,43 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.common.DamagePlayersEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KessigFlamebreather extends CardImpl { + + public KessigFlamebreather(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SHAMAN); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // Whenever you cast a noncreature spell, Kessig Flamebreather deals 1 damage to each opponent. + this.addAbility(new SpellCastControllerTriggeredAbility( + new DamagePlayersEffect(1, TargetController.OPPONENT), + StaticFilters.FILTER_SPELL_A_NON_CREATURE, false + )); + } + + private KessigFlamebreather(final KessigFlamebreather card) { + super(card); + } + + @Override + public KessigFlamebreather copy() { + return new KessigFlamebreather(this); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KessigForgemaster.java b/Mage.Sets/src/mage/cards/k/KessigForgemaster.java index 110c958e848..1b2339634d8 100644 --- a/Mage.Sets/src/mage/cards/k/KessigForgemaster.java +++ b/Mage.Sets/src/mage/cards/k/KessigForgemaster.java @@ -26,7 +26,6 @@ public final class KessigForgemaster extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(1); - this.transformable = true; this.secondSideCardClazz = mage.cards.f.FlameheartWerewolf.class; // Whenever Kessig Forgemaster blocks or becomes blocked by a creature, Kessig Forgemaster deals 1 damage to that creature. diff --git a/Mage.Sets/src/mage/cards/k/KessigNaturalist.java b/Mage.Sets/src/mage/cards/k/KessigNaturalist.java index bc9096b8f54..9fef85ab25d 100644 --- a/Mage.Sets/src/mage/cards/k/KessigNaturalist.java +++ b/Mage.Sets/src/mage/cards/k/KessigNaturalist.java @@ -6,7 +6,6 @@ import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.DayboundAbility; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -30,14 +29,12 @@ public final class KessigNaturalist extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); - this.transformable = true; this.secondSideCardClazz = mage.cards.l.LordOfTheUlvenwald.class; // Whenever Kessig Naturalist attacks, add {R} or {G}. Until end of turn, you don't lose this mana as steps and phases end. this.addAbility(new AttacksTriggeredAbility(new KessigNaturalistEffect())); // Daybound - this.addAbility(new TransformAbility()); this.addAbility(new DayboundAbility()); } diff --git a/Mage.Sets/src/mage/cards/k/KessigProwler.java b/Mage.Sets/src/mage/cards/k/KessigProwler.java index 358e2cd7467..93a7767ae4d 100644 --- a/Mage.Sets/src/mage/cards/k/KessigProwler.java +++ b/Mage.Sets/src/mage/cards/k/KessigProwler.java @@ -26,12 +26,11 @@ public final class KessigProwler extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(1); - this.transformable = true; this.secondSideCardClazz = mage.cards.s.SinuousPredator.class; // {4}{G}: Transform Kessig Prowler. this.addAbility(new TransformAbility()); - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new TransformSourceEffect(true), new ManaCostsImpl("{4}{G}"))); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new TransformSourceEffect(), new ManaCostsImpl("{4}{G}"))); } private KessigProwler(final KessigProwler card) { diff --git a/Mage.Sets/src/mage/cards/k/KessigWolfrider.java b/Mage.Sets/src/mage/cards/k/KessigWolfrider.java new file mode 100644 index 00000000000..8b68907f74c --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KessigWolfrider.java @@ -0,0 +1,53 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.ExileFromGraveCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.RedWolfToken; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KessigWolfrider extends CardImpl { + + public KessigWolfrider(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.KNIGHT); + this.power = new MageInt(1); + this.toughness = new MageInt(2); + + // Menace + this.addAbility(new MenaceAbility(false)); + + // {2}{R}, {T}, Exile three cards from your graveyard: Create a 3/2 red Wolf creature token. + Ability ability = new SimpleActivatedAbility( + new CreateTokenEffect(new RedWolfToken()), new ManaCostsImpl<>("2}{R}") + ); + ability.addCost(new TapSourceCost()); + ability.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard(3, 3))); + this.addAbility(ability); + } + + private KessigWolfrider(final KessigWolfrider card) { + super(card); + } + + @Override + public KessigWolfrider copy() { + return new KessigWolfrider(this); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KetriaTriome.java b/Mage.Sets/src/mage/cards/k/KetriaTriome.java index a85a5d44b5d..221c5e540fc 100644 --- a/Mage.Sets/src/mage/cards/k/KetriaTriome.java +++ b/Mage.Sets/src/mage/cards/k/KetriaTriome.java @@ -1,7 +1,7 @@ package mage.cards.k; import mage.abilities.common.EntersBattlefieldTappedAbility; -import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.keyword.CyclingAbility; import mage.abilities.mana.BlueManaAbility; import mage.abilities.mana.GreenManaAbility; @@ -34,7 +34,7 @@ public final class KetriaTriome extends CardImpl { this.addAbility(new EntersBattlefieldTappedAbility()); // Cycling {3} - this.addAbility(new CyclingAbility(new ManaCostsImpl("{3}"))); + this.addAbility(new CyclingAbility(new GenericManaCost(3))); } private KetriaTriome(final KetriaTriome card) { diff --git a/Mage.Sets/src/mage/cards/k/KeyToTheArchive.java b/Mage.Sets/src/mage/cards/k/KeyToTheArchive.java new file mode 100644 index 00000000000..8975adfef77 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KeyToTheArchive.java @@ -0,0 +1,69 @@ +package mage.cards.k; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.DraftFromSpellbookEffect; +import mage.abilities.effects.common.discard.DiscardControllerEffect; +import mage.abilities.effects.mana.AddManaInAnyCombinationEffect; +import mage.abilities.mana.SimpleManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KeyToTheArchive extends CardImpl { + + private static final List spellbook = Collections.unmodifiableList(Arrays.asList( + "Approach of the Second Sun", + "Claim the Firstborn", + "Counterspell", + "Day of Judgment", + "Demonic Tutor", + "Despark", + "Doom Blade", + "Electrolyze", + "Growth Spiral", + "Krosan Grip", + "Lightning Bolt", + "Lightning Helix", + "Putrefy", + "Regrowth", + "Time Warp" + )); + + public KeyToTheArchive(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); + + // Key to the Archive enters the battlefield tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // When Key to the Archive enters the battlefield, draft a card from Key to the Archive's spellbook, then discard a card. + Ability ability = new EntersBattlefieldTriggeredAbility(new DraftFromSpellbookEffect(spellbook)); + ability.addEffect(new DiscardControllerEffect(1).concatBy(", then")); + this.addAbility(ability); + + // {T}: Add two mana in any combination of colors. + this.addAbility(new SimpleManaAbility( + Zone.BATTLEFIELD, new AddManaInAnyCombinationEffect(2), new TapSourceCost() + )); + } + + private KeyToTheArchive(final KeyToTheArchive card) { + super(card); + } + + @Override + public KeyToTheArchive copy() { + return new KeyToTheArchive(this); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KhenraScrapper.java b/Mage.Sets/src/mage/cards/k/KhenraScrapper.java index 61f4c395630..1050b108137 100644 --- a/Mage.Sets/src/mage/cards/k/KhenraScrapper.java +++ b/Mage.Sets/src/mage/cards/k/KhenraScrapper.java @@ -28,7 +28,7 @@ public final class KhenraScrapper extends CardImpl { this.toughness = new MageInt(3); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // You may exert Khenra Scrapper as it attacks. When you do, it gets +2/+0 until end of turn. this.addAbility(new ExertAbility(new BecomesExertSourceTriggeredAbility(new BoostSourceEffect(2, 0, Duration.EndOfTurn)))); diff --git a/Mage.Sets/src/mage/cards/k/KheruMindEater.java b/Mage.Sets/src/mage/cards/k/KheruMindEater.java index 44cc1fca8f7..481e246db10 100644 --- a/Mage.Sets/src/mage/cards/k/KheruMindEater.java +++ b/Mage.Sets/src/mage/cards/k/KheruMindEater.java @@ -41,7 +41,7 @@ public final class KheruMindEater extends CardImpl { this.toughness = new MageInt(3); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // Whenever Kheru Mind-Eater deals combat damage to a player, that player exiles a card from their hand face down. this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new KheruMindEaterExileEffect(), false, true)); diff --git a/Mage.Sets/src/mage/cards/k/KikiJikiMirrorBreaker.java b/Mage.Sets/src/mage/cards/k/KikiJikiMirrorBreaker.java index 3ecfa7660ff..ba97b5e26e5 100644 --- a/Mage.Sets/src/mage/cards/k/KikiJikiMirrorBreaker.java +++ b/Mage.Sets/src/mage/cards/k/KikiJikiMirrorBreaker.java @@ -88,7 +88,7 @@ class KikiJikiMirrorBreakerEffect extends OneShotEffect { CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(source.getControllerId(), null, true); effect.setTargetPointer(new FixedTarget(permanent, game)); effect.apply(game, source); - for (Permanent addedToken : effect.getAddedPermanent()) { + for (Permanent addedToken : effect.getAddedPermanents()) { SacrificeTargetEffect sacrificeEffect = new SacrificeTargetEffect("Sacrifice the token at the beginning of the next end step", source.getControllerId()); sacrificeEffect.setTargetPointer(new FixedTarget(addedToken.getId())); game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(sacrificeEffect), source); diff --git a/Mage.Sets/src/mage/cards/k/KillShot.java b/Mage.Sets/src/mage/cards/k/KillShot.java index 1c578ff2be6..d6ccb2f414e 100644 --- a/Mage.Sets/src/mage/cards/k/KillShot.java +++ b/Mage.Sets/src/mage/cards/k/KillShot.java @@ -1,4 +1,3 @@ - package mage.cards.k; import java.util.UUID; @@ -6,8 +5,7 @@ import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterAttackingCreature; -import mage.target.common.TargetCreaturePermanent; +import mage.target.common.TargetAttackingCreature; /** * @@ -20,7 +18,7 @@ public final class KillShot extends CardImpl { // Destroy target attacking creature. - this.getSpellAbility().addTarget(new TargetCreaturePermanent(new FilterAttackingCreature())); + this.getSpellAbility().addTarget(new TargetAttackingCreature()); this.getSpellAbility().addEffect(new DestroyTargetEffect()); } diff --git a/Mage.Sets/src/mage/cards/k/KillSwitch.java b/Mage.Sets/src/mage/cards/k/KillSwitch.java index 16ecc2596f9..fb21fcfc698 100644 --- a/Mage.Sets/src/mage/cards/k/KillSwitch.java +++ b/Mage.Sets/src/mage/cards/k/KillSwitch.java @@ -1,57 +1,38 @@ - package mage.cards.k; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.condition.common.SourceTappedBeforeUntapStepCondition; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.decorator.ConditionalContinuousRuleModifyingEffect; -import mage.abilities.effects.Effect; -import mage.abilities.effects.common.DontUntapInControllersUntapStepAllEffect; -import mage.abilities.effects.common.TapAllEffect; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; +import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.TargetController; -import mage.constants.Zone; -import mage.filter.common.FilterArtifactPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.PermanentIdPredicate; +import mage.constants.Outcome; +import mage.constants.PhaseStep; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.targetpointer.FixedTargets; + +import java.util.List; +import java.util.UUID; /** - * * @author spjspj */ public final class KillSwitch extends CardImpl { - - //static { - // filter.add(AnotherPredicate.instance); - // } - public KillSwitch(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); // {2}, {tap}: Tap all other artifacts. They don't untap during their controllers' untap steps for as long as Kill Switch remains tapped. - FilterArtifactPermanent filter = new FilterArtifactPermanent(); - filter.add(Predicates.not(new PermanentIdPredicate(getId()))); - - SourceTappedBeforeUntapStepCondition condition = new SourceTappedBeforeUntapStepCondition(); - condition.setPermanentId(this.getId()); - Effect effect = new ConditionalContinuousRuleModifyingEffect( - new DontUntapInControllersUntapStepAllEffect(Duration.WhileOnBattlefield, TargetController.ANY, filter), - condition); - effect.setText("Artifacts tapped this way don't untap during their controllers' untap steps for as long as {this} remains tapped"); - - Effect effect2 = new TapAllEffect(filter); - effect2.setText("Tap all other artifacts"); - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect2, new ManaCostsImpl("{2}")); + Ability ability = new SimpleActivatedAbility(new KillSwitchEffect(), new GenericManaCost(2)); ability.addCost(new TapSourceCost()); - ability.addEffect(effect); - this.addAbility(ability); + this.addAbility(ability); } private KillSwitch(final KillSwitch card) { @@ -63,3 +44,80 @@ public final class KillSwitch extends CardImpl { return new KillSwitch(this); } } + +class KillSwitchEffect extends OneShotEffect { + + KillSwitchEffect() { + super(Outcome.Benefit); + staticText = "tap all other artifacts. They don't untap during their controllers' " + + "untap steps for as long as {this} remains tapped"; + } + + private KillSwitchEffect(final KillSwitchEffect effect) { + super(effect); + } + + @Override + public KillSwitchEffect copy() { + return new KillSwitchEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + List permanents = game + .getBattlefield() + .getActivePermanents( + StaticFilters.FILTER_PERMANENT_ARTIFACT, + source.getControllerId(), source.getSourceId(), game + ); + Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game); + if (sourcePermanent != null) { + permanents.remove(sourcePermanent); + game.addEffect(new KillSwitchUntapEffect().setTargetPointer(new FixedTargets(permanents, game)), source); + } + for (Permanent permanent : permanents) { + permanent.tap(source, game); + } + return true; + } +} + +class KillSwitchUntapEffect extends ContinuousRuleModifyingEffectImpl { + + KillSwitchUntapEffect() { + super(Duration.Custom, Outcome.Detriment); + } + + private KillSwitchUntapEffect(final KillSwitchUntapEffect effect) { + super(effect); + } + + @Override + public KillSwitchUntapEffect copy() { + return new KillSwitchUntapEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.UNTAP; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (game.getTurn().getStepType() != PhaseStep.UNTAP) { + return false; + } + Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game); + if (sourcePermanent == null || !sourcePermanent.isTapped()) { + discard(); + return false; + } + return getTargetPointer().getTargets(game, source).contains(event.getTargetId()) + && game.isActivePlayer(game.getControllerId(event.getTargetId())); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KindledFury.java b/Mage.Sets/src/mage/cards/k/KindledFury.java index ab94e1f9fe8..1b851ea6488 100644 --- a/Mage.Sets/src/mage/cards/k/KindledFury.java +++ b/Mage.Sets/src/mage/cards/k/KindledFury.java @@ -1,7 +1,5 @@ - package mage.cards.k; -import java.util.UUID; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.keyword.FirstStrikeAbility; @@ -11,17 +9,22 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author Loki */ public final class KindledFury extends CardImpl { public KindledFury(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{R}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{R}"); - this.getSpellAbility().addEffect(new BoostTargetEffect(1, 0, Duration.EndOfTurn)); - this.getSpellAbility().addEffect(new GainAbilityTargetEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn)); + this.getSpellAbility().addEffect(new BoostTargetEffect( + 1, 0, Duration.EndOfTurn + ).setText("target creature gets +1/+0")); + this.getSpellAbility().addEffect(new GainAbilityTargetEffect( + FirstStrikeAbility.getInstance(), Duration.EndOfTurn + ).setText("and gains first strike until end of turn")); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } diff --git a/Mage.Sets/src/mage/cards/k/KindlyAncestor.java b/Mage.Sets/src/mage/cards/k/KindlyAncestor.java new file mode 100644 index 00000000000..a9b64514a7a --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KindlyAncestor.java @@ -0,0 +1,42 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.keyword.DisturbAbility; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KindlyAncestor extends CardImpl { + + public KindlyAncestor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + this.secondSideCardClazz = mage.cards.a.AncestorsEmbrace.class; + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // Disturb {1}{W} + this.addAbility(new DisturbAbility(this, "{1}{W}")); + } + + private KindlyAncestor(final KindlyAncestor card) { + super(card); + } + + @Override + public KindlyAncestor copy() { + return new KindlyAncestor(this); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KindlyStranger.java b/Mage.Sets/src/mage/cards/k/KindlyStranger.java index 93e43fbd9e0..d6edfa74ece 100644 --- a/Mage.Sets/src/mage/cards/k/KindlyStranger.java +++ b/Mage.Sets/src/mage/cards/k/KindlyStranger.java @@ -26,13 +26,12 @@ public final class KindlyStranger extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(3); - this.transformable = true; this.secondSideCardClazz = mage.cards.d.DemonPossessedWitch.class; // Delirium — {2}{B}: Transform Kindly Stranger. Activate this ability only if there are four or more card types among cards in your graveyard. this.addAbility(new TransformAbility()); this.addAbility(new ConditionalActivatedAbility( - Zone.BATTLEFIELD, new TransformSourceEffect(true), new ManaCostsImpl<>("{2}{B}"), + Zone.BATTLEFIELD, new TransformSourceEffect(), new ManaCostsImpl<>("{2}{B}"), DeliriumCondition.instance, "Delirium — {2}{B}: Transform {this}. " + "Activate only if there are four or more card types among cards in your graveyard." ).addHint(CardTypesInGraveyardHint.YOU)); diff --git a/Mage.Sets/src/mage/cards/k/KindredCharge.java b/Mage.Sets/src/mage/cards/k/KindredCharge.java index 548e9f4eb96..82318a30d42 100644 --- a/Mage.Sets/src/mage/cards/k/KindredCharge.java +++ b/Mage.Sets/src/mage/cards/k/KindredCharge.java @@ -77,7 +77,7 @@ class KindredChargeEffect extends OneShotEffect { CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(source.getControllerId(), null, true); effect.setTargetPointer(new FixedTarget(permanent, game)); effect.apply(game, source); - for (Permanent addedToken : effect.getAddedPermanent()) { + for (Permanent addedToken : effect.getAddedPermanents()) { Effect exileEffect = new ExileTargetEffect(); exileEffect.setTargetPointer(new FixedTarget(addedToken, game)); game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect), source); diff --git a/Mage.Sets/src/mage/cards/k/KindredDenial.java b/Mage.Sets/src/mage/cards/k/KindredDenial.java new file mode 100644 index 00000000000..4090b8773d1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KindredDenial.java @@ -0,0 +1,72 @@ +package mage.cards.k; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.Outcome; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.game.Game; +import mage.game.stack.Spell; +import mage.players.Player; +import mage.target.TargetSpell; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KindredDenial extends CardImpl { + + public KindredDenial(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{U}{U}"); + + // Counter target spell. Seek a card with the same mana value as that spell. + this.getSpellAbility().addEffect(new KindredDenialEffect()); + this.getSpellAbility().addTarget(new TargetSpell()); + } + + private KindredDenial(final KindredDenial card) { + super(card); + } + + @Override + public KindredDenial copy() { + return new KindredDenial(this); + } +} + +class KindredDenialEffect extends OneShotEffect { + + KindredDenialEffect() { + super(Outcome.Benefit); + staticText = "counter target spell. Seek a card with the same mana value as that spell"; + } + + private KindredDenialEffect(final KindredDenialEffect effect) { + super(effect); + } + + @Override + public KindredDenialEffect copy() { + return new KindredDenialEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Spell spell = game.getSpell(source.getFirstTarget()); + if (player == null || spell == null) { + return false; + } + int manaValue = spell.getManaValue(); + spell.counter(source, game); + FilterCard filter = new FilterCard(); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, manaValue)); + player.seekCard(filter, source, game); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/k/KingNarfisBetrayal.java b/Mage.Sets/src/mage/cards/k/KingNarfisBetrayal.java index 14b44b51e16..68ccae8e71f 100644 --- a/Mage.Sets/src/mage/cards/k/KingNarfisBetrayal.java +++ b/Mage.Sets/src/mage/cards/k/KingNarfisBetrayal.java @@ -33,7 +33,7 @@ public final class KingNarfisBetrayal extends CardImpl { this.subtype.add(SubType.SAGA); // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) - SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); + SagaAbility sagaAbility = new SagaAbility(this); //I — Each player mills four cards. You may exile up to one creature or planeswalker card from each graveyard. sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, new KingNarfisBetrayalFirstEffect()); diff --git a/Mage.Sets/src/mage/cards/k/KinjallisSunwing.java b/Mage.Sets/src/mage/cards/k/KinjallisSunwing.java index 6f06f3eb832..597a57865f0 100644 --- a/Mage.Sets/src/mage/cards/k/KinjallisSunwing.java +++ b/Mage.Sets/src/mage/cards/k/KinjallisSunwing.java @@ -1,26 +1,18 @@ - package mage.cards.k; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.PermanentsEnterBattlefieldTappedEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.EntersTheBattlefieldEvent; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * * @author TheElk801 */ public final class KinjallisSunwing extends CardImpl { @@ -36,7 +28,9 @@ public final class KinjallisSunwing extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Creatures your opponents control enter the battlefield tapped. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new KinjallisSunwingEffect())); + this.addAbility(new SimpleStaticAbility(new PermanentsEnterBattlefieldTappedEffect( + StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE + ).setText("creatures your opponents control enter the battlefield tapped"))); } private KinjallisSunwing(final KinjallisSunwing card) { @@ -48,45 +42,3 @@ public final class KinjallisSunwing extends CardImpl { return new KinjallisSunwing(this); } } - -class KinjallisSunwingEffect extends ReplacementEffectImpl { - - KinjallisSunwingEffect() { - super(Duration.WhileOnBattlefield, Outcome.Tap); - staticText = "Creatures your opponents control enter the battlefield tapped"; - } - - KinjallisSunwingEffect(final KinjallisSunwingEffect effect) { - super(effect); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Permanent target = ((EntersTheBattlefieldEvent) event).getTarget(); - if (target != null) { - target.tap(source, game); - } - return false; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (game.getOpponents(source.getControllerId()).contains(event.getPlayerId())) { - Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget(); - if (permanent != null && permanent.isCreature(game)) { - return true; - } - } - return false; - } - - @Override - public KinjallisSunwingEffect copy() { - return new KinjallisSunwingEffect(this); - } -} diff --git a/Mage.Sets/src/mage/cards/k/KioraBehemothBeckoner.java b/Mage.Sets/src/mage/cards/k/KioraBehemothBeckoner.java index 4e2483500a2..1c741ab6b13 100644 --- a/Mage.Sets/src/mage/cards/k/KioraBehemothBeckoner.java +++ b/Mage.Sets/src/mage/cards/k/KioraBehemothBeckoner.java @@ -3,7 +3,6 @@ package mage.cards.k; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.UntapTargetEffect; import mage.cards.CardImpl; @@ -33,7 +32,7 @@ public final class KioraBehemothBeckoner extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.KIORA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(7)); + this.setStartingLoyalty(7); // Whenever a creature with power 4 or greater enters the battlefield under your control, draw a card. this.addAbility(new EntersBattlefieldControlledTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/k/KioraBestsTheSeaGod.java b/Mage.Sets/src/mage/cards/k/KioraBestsTheSeaGod.java index 6fce8da796c..df514944c21 100644 --- a/Mage.Sets/src/mage/cards/k/KioraBestsTheSeaGod.java +++ b/Mage.Sets/src/mage/cards/k/KioraBestsTheSeaGod.java @@ -39,7 +39,7 @@ public final class KioraBestsTheSeaGod extends CardImpl { this.subtype.add(SubType.SAGA); // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) - SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); + SagaAbility sagaAbility = new SagaAbility(this); // I — Create an 8/8 blue Kraken creature token with hexproof. sagaAbility.addChapterEffect( diff --git a/Mage.Sets/src/mage/cards/k/KioraMasterOfTheDepths.java b/Mage.Sets/src/mage/cards/k/KioraMasterOfTheDepths.java index dafd0b930d1..b43bd8a4743 100644 --- a/Mage.Sets/src/mage/cards/k/KioraMasterOfTheDepths.java +++ b/Mage.Sets/src/mage/cards/k/KioraMasterOfTheDepths.java @@ -3,7 +3,6 @@ package mage.cards.k; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.GetEmblemEffect; @@ -36,7 +35,7 @@ public final class KioraMasterOfTheDepths extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.KIORA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Untap up to one target creature and up to one target land. LoyaltyAbility ability = new LoyaltyAbility(new KioraUntapEffect(), 1); diff --git a/Mage.Sets/src/mage/cards/k/KioraTheCrashingWave.java b/Mage.Sets/src/mage/cards/k/KioraTheCrashingWave.java index 8034982a791..9361f0c87b9 100644 --- a/Mage.Sets/src/mage/cards/k/KioraTheCrashingWave.java +++ b/Mage.Sets/src/mage/cards/k/KioraTheCrashingWave.java @@ -4,7 +4,6 @@ package mage.cards.k; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.PreventionEffectImpl; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.GetEmblemEffect; @@ -42,7 +41,7 @@ public final class KioraTheCrashingWave extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.KIORA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(2)); + this.setStartingLoyalty(2); // +1: Until your next turn, prevent all damage that would be dealt to and dealt by target permanent an opponent controls. LoyaltyAbility ability = new LoyaltyAbility(new KioraPreventionEffect(), 1); diff --git a/Mage.Sets/src/mage/cards/k/KiorasDismissal.java b/Mage.Sets/src/mage/cards/k/KiorasDismissal.java index cb5901a5e2a..34a1888286a 100644 --- a/Mage.Sets/src/mage/cards/k/KiorasDismissal.java +++ b/Mage.Sets/src/mage/cards/k/KiorasDismissal.java @@ -24,7 +24,7 @@ public final class KiorasDismissal extends CardImpl { this.addAbility(new StriveAbility("{U}")); // Return any number of target enchantments to their owners' hands. - this.getSpellAbility().addTarget(new TargetPermanent(0, Integer.MAX_VALUE, StaticFilters.FILTER_ENCHANTMENT_PERMANENT, false)); + this.getSpellAbility().addTarget(new TargetPermanent(0, Integer.MAX_VALUE, StaticFilters.FILTER_PERMANENT_ENCHANTMENT, false)); Effect effect = new ReturnToHandTargetEffect(); effect.setText("Return any number of target enchantments to their owners' hands"); this.getSpellAbility().addEffect(effect); diff --git a/Mage.Sets/src/mage/cards/k/KiraGreatGlassSpinner.java b/Mage.Sets/src/mage/cards/k/KiraGreatGlassSpinner.java index eb2d7f32522..12109edbb25 100644 --- a/Mage.Sets/src/mage/cards/k/KiraGreatGlassSpinner.java +++ b/Mage.Sets/src/mage/cards/k/KiraGreatGlassSpinner.java @@ -1,4 +1,3 @@ - package mage.cards.k; import java.util.UUID; @@ -84,7 +83,7 @@ class KiraGreatGlassSpinnerAbility extends TriggeredAbilityImpl { NumberOfTimesPermanentTargetedATurnWatcher watcher = game.getState().getWatcher(NumberOfTimesPermanentTargetedATurnWatcher.class); if (watcher != null && watcher.notMoreThanOnceTargetedThisTurn(permanent, game)) { for (Effect effect : getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getSourceId())); + effect.setTargetPointer(new FixedTarget(event.getSourceId(), game)); } return true; } diff --git a/Mage.Sets/src/mage/cards/k/KiriOnna.java b/Mage.Sets/src/mage/cards/k/KiriOnna.java index 23b4efd871b..8328d3b120d 100644 --- a/Mage.Sets/src/mage/cards/k/KiriOnna.java +++ b/Mage.Sets/src/mage/cards/k/KiriOnna.java @@ -33,7 +33,7 @@ public final class KiriOnna extends CardImpl { ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); // Whenever you cast a Spirit or Arcane spell, you may return Kiri-Onna to its owner's hand. - this.addAbility(new SpellCastControllerTriggeredAbility(new ReturnToHandSourceEffect(true), StaticFilters.SPIRIT_OR_ARCANE_CARD, true)); + this.addAbility(new SpellCastControllerTriggeredAbility(new ReturnToHandSourceEffect(true), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true)); } private KiriOnna(final KiriOnna card) { diff --git a/Mage.Sets/src/mage/cards/k/KirinTouchedOrochi.java b/Mage.Sets/src/mage/cards/k/KirinTouchedOrochi.java new file mode 100644 index 00000000000..77aa764a2cd --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KirinTouchedOrochi.java @@ -0,0 +1,134 @@ +package mage.cards.k; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.cards.Card; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.filter.common.FilterCreatureCard; +import mage.filter.common.FilterNoncreatureCard; +import mage.game.Game; +import mage.game.permanent.token.SpiritToken; +import mage.players.Player; +import mage.target.common.TargetCardInGraveyard; +import mage.target.common.TargetControlledCreaturePermanent; + +/** + * + * @author weirddan455 + */ +public final class KirinTouchedOrochi extends CardImpl { + + private static final FilterCreatureCard filter = new FilterCreatureCard("creature card from a graveyard"); + private static final FilterNoncreatureCard filter2 = new FilterNoncreatureCard("noncreature card from a graveyard"); + + public KirinTouchedOrochi(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, ""); + + this.subtype.add(SubType.SNAKE); + this.subtype.add(SubType.MONK); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + this.color.setGreen(true); + this.nightCard = true; + + // Whenever Kirin-Touched Orochi attacks, choose one — + // • Exile target creature card from a graveyard. When you do, create a 1/1 colorless Spirit creature token. + Ability ability = new AttacksTriggeredAbility(new KirinTouchedOrochiTokenEffect()); + ability.addTarget(new TargetCardInGraveyard(filter)); + + // • Exile target noncreature card from a graveyard. When you do, put a +1/+1 counter on target creature you control. + Mode mode = new Mode(new KirinTouchedOrochiCounterEffect()); + mode.addTarget(new TargetCardInGraveyard(filter2)); + ability.addMode(mode); + this.addAbility(ability); + } + + private KirinTouchedOrochi(final KirinTouchedOrochi card) { + super(card); + } + + @Override + public KirinTouchedOrochi copy() { + return new KirinTouchedOrochi(this); + } +} + +class KirinTouchedOrochiTokenEffect extends OneShotEffect { + + public KirinTouchedOrochiTokenEffect() { + super(Outcome.Exile); + this.staticText = "Exile target creature card from a graveyard. When you do, create a 1/1 colorless Spirit creature token"; + } + + private KirinTouchedOrochiTokenEffect(final KirinTouchedOrochiTokenEffect effect) { + super(effect); + } + + @Override + public KirinTouchedOrochiTokenEffect copy() { + return new KirinTouchedOrochiTokenEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + UUID targetId = source.getFirstTarget(); + Card card = game.getCard(targetId); + if (controller == null || card == null || game.getState().getZone(targetId) != Zone.GRAVEYARD) { + return false; + } + if (!controller.moveCards(card, Zone.EXILED, source, game)) { + return false; + } + ReflexiveTriggeredAbility reflexiveTokenAbility = new ReflexiveTriggeredAbility(new CreateTokenEffect(new SpiritToken()), false); + game.fireReflexiveTriggeredAbility(reflexiveTokenAbility, source); + return true; + } +} + +class KirinTouchedOrochiCounterEffect extends OneShotEffect { + + public KirinTouchedOrochiCounterEffect() { + super(Outcome.Exile); + this.staticText = "Exile target noncreature card from a graveyard. When you do, put a +1/+1 counter on target creature you control"; + } + + private KirinTouchedOrochiCounterEffect(final KirinTouchedOrochiCounterEffect effect) { + super(effect); + } + + @Override + public KirinTouchedOrochiCounterEffect copy() { + return new KirinTouchedOrochiCounterEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + UUID targetId = source.getFirstTarget(); + Card card = game.getCard(targetId); + if (controller == null || card == null || game.getState().getZone(targetId) != Zone.GRAVEYARD) { + return false; + } + if (!controller.moveCards(card, Zone.EXILED, source, game)) { + return false; + } + ReflexiveTriggeredAbility reflexiveCounterAbility = new ReflexiveTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance()), false); + reflexiveCounterAbility.addTarget(new TargetControlledCreaturePermanent()); + game.fireReflexiveTriggeredAbility(reflexiveCounterAbility, source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/k/KirtarsWrath.java b/Mage.Sets/src/mage/cards/k/KirtarsWrath.java index 728d5d7206c..e2f91b34e7a 100644 --- a/Mage.Sets/src/mage/cards/k/KirtarsWrath.java +++ b/Mage.Sets/src/mage/cards/k/KirtarsWrath.java @@ -1,4 +1,3 @@ - package mage.cards.k; import java.util.UUID; @@ -12,7 +11,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.token.SpiritWhiteToken; @@ -25,15 +24,13 @@ public final class KirtarsWrath extends CardImpl { public KirtarsWrath(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{4}{W}{W}"); - // Destroy all creatures. They can't be regenerated. // Threshold - If seven or more cards are in your graveyard, instead destroy all creatures, then create two 1/1 white Spirit creature tokens with flying. Creatures destroyed this way can't be regenerated. this.getSpellAbility().addEffect(new ConditionalOneShotEffect( new KirtarsWrathEffect(), - new DestroyAllEffect(new FilterCreaturePermanent("all creatures"), true), + new DestroyAllEffect(StaticFilters.FILTER_PERMANENT_CREATURES, true), new CardsInControllerGraveyardCondition(7), "Destroy all creatures. They can't be regenerated.

Threshold — If seven or more cards are in your graveyard, instead destroy all creatures, then create two 1/1 white Spirit creature tokens with flying. Creatures destroyed this way can't be regenerated")); - } private KirtarsWrath(final KirtarsWrath card) { @@ -64,7 +61,7 @@ class KirtarsWrathEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - new DestroyAllEffect(new FilterCreaturePermanent("all creatures"), true).apply(game, source); + new DestroyAllEffect(StaticFilters.FILTER_PERMANENT_CREATURES, true).apply(game, source); return new CreateTokenEffect(new SpiritWhiteToken(), 2).apply(game, source); } } diff --git a/Mage.Sets/src/mage/cards/k/Kismet.java b/Mage.Sets/src/mage/cards/k/Kismet.java index 7d4e9072103..df5f4a59811 100644 --- a/Mage.Sets/src/mage/cards/k/Kismet.java +++ b/Mage.Sets/src/mage/cards/k/Kismet.java @@ -1,32 +1,39 @@ package mage.cards.k; -import java.util.UUID; -import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.PermanentsEnterBattlefieldTappedEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.EntersTheBattlefieldEvent; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicates; + +import java.util.UUID; /** - * * @author Quercitron */ public final class Kismet extends CardImpl { + private static final FilterPermanent filter + = new FilterPermanent("artifacts, creatures, and lands your opponents control"); + + static { + filter.add(TargetController.OPPONENT.getControllerPredicate()); + filter.add(Predicates.or( + CardType.ARTIFACT.getPredicate(), + CardType.CREATURE.getPredicate(), + CardType.LAND.getPredicate() + )); + } + public Kismet(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{3}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{W}"); // Artifacts, creatures, and lands your opponents control enter the battlefield tapped. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new KismetEffect())); + this.addAbility(new SimpleStaticAbility(new PermanentsEnterBattlefieldTappedEffect(filter))); } private Kismet(final Kismet card) { @@ -38,47 +45,3 @@ public final class Kismet extends CardImpl { return new Kismet(this); } } - -class KismetEffect extends ReplacementEffectImpl { - - KismetEffect() { - super(Duration.WhileOnBattlefield, Outcome.Tap); - staticText = "Artifacts, creatures, and lands your opponents control enter the battlefield tapped"; - } - - KismetEffect(final KismetEffect effect) { - super(effect); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Permanent target = ((EntersTheBattlefieldEvent) event).getTarget(); - if (target != null) { - target.setTapped(true); - } - return false; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (game.getOpponents(source.getControllerId()).contains(event.getPlayerId())) { - Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget(); - if (permanent != null && (permanent.isArtifact(game) - || permanent.isCreature(game) - || permanent.isLand(game))) { - return true; - } - } - return false; - } - - @Override - public KismetEffect copy() { - return new KismetEffect(this); - } -} diff --git a/Mage.Sets/src/mage/cards/k/KitsuneAce.java b/Mage.Sets/src/mage/cards/k/KitsuneAce.java new file mode 100644 index 00000000000..fd141b9ae5b --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KitsuneAce.java @@ -0,0 +1,55 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.AttacksCreatureYouControlTriggeredAbility; +import mage.abilities.effects.common.UntapSourceEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.common.FilterControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KitsuneAce extends CardImpl { + + private static final FilterControlledCreaturePermanent filter + = new FilterControlledCreaturePermanent(SubType.VEHICLE, "a Vehicle you control"); + + public KitsuneAce(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.subtype.add(SubType.FOX); + this.subtype.add(SubType.PILOT); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Whenever a Vehicle you control attacks, choose one — + // • That vehicle gains first strike until end of turn. + Ability ability = new AttacksCreatureYouControlTriggeredAbility( + new GainAbilityTargetEffect(FirstStrikeAbility.getInstance()) + .setText("that Vehicle gains first strike until end of turn"), + false, filter, true + ); + + // • Untap Kitsune Ace. + ability.addMode(new Mode(new UntapSourceEffect())); + this.addAbility(ability); + } + + private KitsuneAce(final KitsuneAce card) { + super(card); + } + + @Override + public KitsuneAce copy() { + return new KitsuneAce(this); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KjeldoranEliteGuard.java b/Mage.Sets/src/mage/cards/k/KjeldoranEliteGuard.java new file mode 100644 index 00000000000..7f036c0b26e --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KjeldoranEliteGuard.java @@ -0,0 +1,117 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.condition.common.IsPhaseCondition; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.decorator.ConditionalActivatedAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.SacrificeSourceEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * @author Alex-Vasile + */ +public final class KjeldoranEliteGuard extends CardImpl { + + public KjeldoranEliteGuard(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Target creature gets +2/+2 until end of turn. + // When that creature leaves the battlefield this turn, sacrifice Kjeldoran Elite Guard. + // Activate only during combat. + Ability ability = new ConditionalActivatedAbility( + Zone.BATTLEFIELD, + new KjeldoranEliteGuardEffect(), + new TapSourceCost(), + new IsPhaseCondition(TurnPhase.COMBAT, false)); + ability.addTarget(new TargetCreaturePermanent()); + + this.addAbility(ability); + } + + private KjeldoranEliteGuard(final KjeldoranEliteGuard card) { super(card); } + + @Override + public Card copy() { + return new KjeldoranEliteGuard(this); + } +} + +class KjeldoranEliteGuardEffect extends OneShotEffect { + + KjeldoranEliteGuardEffect() { + super(Outcome.Neutral); + staticText = "Target creature gets +2/+2 until end of turn. " + + "When that creature leaves the battlefield this turn, sacrifice Kjeldoran Elite Guard. " + + "Activate only during combat."; + } + + @Override + public boolean apply(Game game, Ability source) { + if (game.getPermanent(source.getFirstTarget()) == null) { return false; } + + // Target creature gets +2/+2 until end of turn. + BoostTargetEffect buffEffect = new BoostTargetEffect(2, 2, Duration.EndOfTurn); + buffEffect.setTargetPointer(new FixedTarget(source.getFirstTarget(), game)); + game.addEffect(buffEffect, source); + + // When that creature leaves the battlefield this turn, sacrifice Kjeldoran Elite Guard. + game.addDelayedTriggeredAbility( + new KjeldoranEliteGuardDelayedTriggeredAbility(source.getFirstTarget()), + source); + + return true; + } + + private KjeldoranEliteGuardEffect(KjeldoranEliteGuardEffect effect) { super(effect); } + + @Override + public KjeldoranEliteGuardEffect copy() { return new KjeldoranEliteGuardEffect(this); } +} + +class KjeldoranEliteGuardDelayedTriggeredAbility extends DelayedTriggeredAbility { + + private final UUID creatureId; + + KjeldoranEliteGuardDelayedTriggeredAbility(UUID creatureId) { + super(new SacrificeSourceEffect(), Duration.EndOfTurn, true); + this.creatureId = creatureId; + } + + KjeldoranEliteGuardDelayedTriggeredAbility(KjeldoranEliteGuardDelayedTriggeredAbility ability) { + super(ability); + this.creatureId = ability.creatureId; + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { return event.getTargetId().equals(creatureId); } + + @Override + public KjeldoranEliteGuardDelayedTriggeredAbility copy() { + return new KjeldoranEliteGuardDelayedTriggeredAbility(this); + } + + @Override + public String getRule() { return "that creature left the battlefield this turn"; } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/k/KnacksawClique.java b/Mage.Sets/src/mage/cards/k/KnacksawClique.java index fb1e740b969..86bbe3e8fe6 100644 --- a/Mage.Sets/src/mage/cards/k/KnacksawClique.java +++ b/Mage.Sets/src/mage/cards/k/KnacksawClique.java @@ -1,4 +1,3 @@ - package mage.cards.k; import java.util.UUID; @@ -34,7 +33,7 @@ import mage.target.targetpointer.FixedTarget; public final class KnacksawClique extends CardImpl { public KnacksawClique(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); this.subtype.add(SubType.FAERIE); this.subtype.add(SubType.ROGUE); @@ -43,13 +42,13 @@ public final class KnacksawClique extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); - + // {1}{U}, {untap}: Target opponent exiles the top card of their library. Until end of turn, you may play that card. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new KnacksawCliqueEffect(), new ManaCostsImpl("{1}{U}")); ability.addCost(new UntapSourceCost()); ability.addTarget(new TargetOpponent()); this.addAbility(ability); - + } private KnacksawClique(final KnacksawClique card) { @@ -89,7 +88,7 @@ class KnacksawCliqueEffect extends OneShotEffect { if (card != null) { opponent.moveCardToExileWithInfo(card, source.getSourceId(), sourceObject.getName(), source, game, Zone.LIBRARY, true); ContinuousEffect effect = new KnacksawCliqueCastFromExileEffect(); - effect.setTargetPointer(new FixedTarget(card.getId())); + effect.setTargetPointer(new FixedTarget(card.getId(), game)); game.addEffect(effect, source); } } diff --git a/Mage.Sets/src/mage/cards/k/KnowledgePool.java b/Mage.Sets/src/mage/cards/k/KnowledgePool.java index c813eea3cb1..122b18ddfbd 100644 --- a/Mage.Sets/src/mage/cards/k/KnowledgePool.java +++ b/Mage.Sets/src/mage/cards/k/KnowledgePool.java @@ -18,7 +18,6 @@ import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.CardIdPredicate; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.game.stack.Spell; import mage.players.Player; @@ -143,22 +142,29 @@ class KnowledgePoolEffect2 extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Spell spell = game.getStack().getSpell(targetPointer.getFirst(game, source)); + if (spell == null) { + return false; + } Permanent sourceObject = game.getPermanentOrLKIBattlefield(source.getSourceId()); - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null && spell != null && sourceObject != null) { + Player spellController = game.getPlayer(spell.getControllerId()); + if (spellController != null + && sourceObject != null) { UUID exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), sourceObject.getZoneChangeCounter(game)); - if (controller.moveCardsToExile(spell, source, game, true, exileZoneId, sourceObject.getIdName())) { - Player player = game.getPlayer(spell.getControllerId()); - if (player != null && player.chooseUse(Outcome.PlayForFree, "Cast another nonland card exiled with " + sourceObject.getLogName() + " without paying that card's mana cost?", source, game)) { + if (exileZoneId == null) { + return false; + } + if (spellController.moveCardsToExile(spell, source, game, true, exileZoneId, sourceObject.getIdName())) { + if (spellController.chooseUse(Outcome.PlayForFree, "Cast another nonland card exiled with " + sourceObject.getLogName() + " without paying that card's mana cost?", source, game)) { FilterNonlandCard realFilter = filter.copy(); realFilter.add(Predicates.not(new CardIdPredicate(spell.getSourceId()))); TargetCardInExile target = new TargetCardInExile(0, 1, realFilter, source.getSourceId()); target.setNotTarget(true); - if (player.choose(Outcome.PlayForFree, game.getExile().getExileZone(exileZoneId), target, game)) { + if (spellController.choose(Outcome.PlayForFree, game.getExile().getExileZone(exileZoneId), target, game)) { Card card = game.getCard(target.getFirstTarget()); - if (card != null && !card.getId().equals(spell.getSourceId())) { + if (card != null + && !card.getId().equals(spell.getSourceId())) { game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); - player.cast(player.chooseAbilityForCast(card, game, true), game, true, new ApprovingObject(source, game)); + spellController.cast(spellController.chooseAbilityForCast(card, game, true), game, true, new ApprovingObject(source, game)); game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); } } diff --git a/Mage.Sets/src/mage/cards/k/KodamaOfTheSouthTree.java b/Mage.Sets/src/mage/cards/k/KodamaOfTheSouthTree.java index 3b6b9fc633f..09b7c7e4734 100644 --- a/Mage.Sets/src/mage/cards/k/KodamaOfTheSouthTree.java +++ b/Mage.Sets/src/mage/cards/k/KodamaOfTheSouthTree.java @@ -29,7 +29,7 @@ public final class KodamaOfTheSouthTree extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(4); - Ability ability = new SpellCastControllerTriggeredAbility(new BoostControlledEffect(1, 1, Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURE, true), StaticFilters.SPIRIT_OR_ARCANE_CARD, false); + Ability ability = new SpellCastControllerTriggeredAbility(new BoostControlledEffect(1, 1, Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURE, true), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false); ability.addEffect(new GainAbilityControlledEffect(TrampleAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURE, true)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/k/KodamaOfTheWestTree.java b/Mage.Sets/src/mage/cards/k/KodamaOfTheWestTree.java new file mode 100644 index 00000000000..3400a42e9c6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KodamaOfTheWestTree.java @@ -0,0 +1,65 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.common.DealsDamageToAPlayerAllTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.continuous.GainAbilityAllEffect; +import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; +import mage.abilities.keyword.ReachAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.permanent.ModifiedPredicate; +import mage.target.common.TargetCardInLibrary; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KodamaOfTheWestTree extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("a modified creature you control"); + + static { + filter.add(ModifiedPredicate.instance); + } + + public KodamaOfTheWestTree(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Reach + this.addAbility(ReachAbility.getInstance()); + + // Modified creatures you control have trample. + this.addAbility(new SimpleStaticAbility(new GainAbilityAllEffect( + TrampleAbility.getInstance(), Duration.WhileOnBattlefield, filter + ).setText("modified creatures you control have trample"))); + + // Whenever a modified creature you control deals combat damage to a player, search your library for a basic land card, put it onto the battlefield tapped, then shuffle. + this.addAbility(new DealsDamageToAPlayerAllTriggeredAbility( + new SearchLibraryPutInPlayEffect( + new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), true + ), filter, false, SetTargetPointer.NONE, true + )); + } + + private KodamaOfTheWestTree(final KodamaOfTheWestTree card) { + super(card); + } + + @Override + public KodamaOfTheWestTree copy() { + return new KodamaOfTheWestTree(this); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KolaghanForerunners.java b/Mage.Sets/src/mage/cards/k/KolaghanForerunners.java index 166cae7369c..91ee35e5728 100644 --- a/Mage.Sets/src/mage/cards/k/KolaghanForerunners.java +++ b/Mage.Sets/src/mage/cards/k/KolaghanForerunners.java @@ -14,7 +14,7 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; import java.util.UUID; @@ -34,7 +34,7 @@ public final class KolaghanForerunners extends CardImpl { this.addAbility(TrampleAbility.getInstance()); // Kolaghan Forerunners' power is equal to the number of creatures you control. - Effect effect = new SetPowerSourceEffect(new PermanentsOnBattlefieldCount(new FilterControlledCreaturePermanent("creatures you control")), Duration.EndOfGame); + Effect effect = new SetPowerSourceEffect(new PermanentsOnBattlefieldCount(StaticFilters.FILTER_CONTROLLED_CREATURES), Duration.EndOfGame); this.addAbility(new SimpleStaticAbility(Zone.ALL, effect).addHint(CreaturesYouControlHint.instance)); // Dash {2}{R} diff --git a/Mage.Sets/src/mage/cards/k/KomainuBattleArmor.java b/Mage.Sets/src/mage/cards/k/KomainuBattleArmor.java new file mode 100644 index 00000000000..61da72ca954 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KomainuBattleArmor.java @@ -0,0 +1,137 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.combat.GoadTargetEffect; +import mage.abilities.effects.common.continuous.BoostEquippedEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.MenaceAbility; +import mage.abilities.keyword.ReconfigureAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.DamagedEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KomainuBattleArmor extends CardImpl { + + public KomainuBattleArmor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{2}{R}"); + + this.subtype.add(SubType.EQUIPMENT); + this.subtype.add(SubType.DOG); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Menace + this.addAbility(new MenaceAbility(false)); + + // Equipped creature gets +2/+2 and has menace. + Ability ability = new SimpleStaticAbility(new BoostEquippedEffect(2, 2)); + ability.addEffect(new GainAbilityAttachedEffect( + new MenaceAbility(false), AttachmentType.EQUIPMENT + ).setText("and has menace")); + this.addAbility(ability); + + // Whenever Komainu Battle Armor or equipped creature deals combat damage to a player, goad each creature that player controls. + this.addAbility(new KomainuBattleArmorTriggeredAbility()); + + // Reconfigure {4} + this.addAbility(new ReconfigureAbility("{4}")); + } + + private KomainuBattleArmor(final KomainuBattleArmor card) { + super(card); + } + + @Override + public KomainuBattleArmor copy() { + return new KomainuBattleArmor(this); + } +} + +class KomainuBattleArmorTriggeredAbility extends TriggeredAbilityImpl { + + KomainuBattleArmorTriggeredAbility() { + super(Zone.BATTLEFIELD, new KomainuBattleArmorEffect()); + } + + private KomainuBattleArmorTriggeredAbility(final KomainuBattleArmorTriggeredAbility ability) { + super(ability); + } + + @Override + public KomainuBattleArmorTriggeredAbility copy() { + return new KomainuBattleArmorTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return false; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (!((DamagedEvent) event).isCombatDamage()) { + return false; + } + if (getSourceId().equals(event.getSourceId())) { + getEffects().setTargetPointer(new FixedTarget(event.getTargetId())); + return true; + } + Permanent permanent = getSourcePermanentOrLKI(game); + if (permanent != null && event.getSourceId().equals(permanent.getAttachedTo())) { + getEffects().setTargetPointer(new FixedTarget(event.getTargetId())); + return true; + } + return false; + } + + @Override + public String getRule() { + return "Whenever {this} or equipped creature deals combat damage to a player, goad each creature that player controls."; + } +} + +class KomainuBattleArmorEffect extends OneShotEffect { + + KomainuBattleArmorEffect() { + super(Outcome.Benefit); + } + + private KomainuBattleArmorEffect(final KomainuBattleArmorEffect effect) { + super(effect); + } + + @Override + public KomainuBattleArmorEffect copy() { + return new KomainuBattleArmorEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + UUID playerId = getTargetPointer().getFirst(game, source); + if (playerId == null) { + return false; + } + for (Permanent permanent : game.getBattlefield().getActivePermanents( + StaticFilters.FILTER_CONTROLLED_CREATURE, + playerId, source.getSourceId(), game + )) { + game.addEffect(new GoadTargetEffect().setTargetPointer(new FixedTarget(permanent, game)), source); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/k/KongmingsContraptions.java b/Mage.Sets/src/mage/cards/k/KongmingsContraptions.java index 314309bfe09..7ebae6d9a1f 100644 --- a/Mage.Sets/src/mage/cards/k/KongmingsContraptions.java +++ b/Mage.Sets/src/mage/cards/k/KongmingsContraptions.java @@ -1,4 +1,3 @@ - package mage.cards.k; import java.util.UUID; @@ -16,8 +15,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.PhaseStep; import mage.constants.Zone; -import mage.filter.common.FilterAttackingCreature; -import mage.target.TargetPermanent; +import mage.target.common.TargetAttackingCreature; import mage.watchers.common.PlayerAttackedStepWatcher; /** @@ -38,7 +36,7 @@ public final class KongmingsContraptions extends CardImpl { new CompoundCondition("during the declare attackers step and only if you've been attacked this step", new IsStepCondition(PhaseStep.DECLARE_ATTACKERS, false), AttackedThisStepCondition.instance) ); - ability.addTarget(new TargetPermanent(new FilterAttackingCreature())); + ability.addTarget(new TargetAttackingCreature()); this.addAbility(ability, new PlayerAttackedStepWatcher()); } @@ -50,5 +48,4 @@ public final class KongmingsContraptions extends CardImpl { public KongmingsContraptions copy() { return new KongmingsContraptions(this); } - } diff --git a/Mage.Sets/src/mage/cards/k/KorEntanglers.java b/Mage.Sets/src/mage/cards/k/KorEntanglers.java index 606ea04db3a..33f40197917 100644 --- a/Mage.Sets/src/mage/cards/k/KorEntanglers.java +++ b/Mage.Sets/src/mage/cards/k/KorEntanglers.java @@ -10,8 +10,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; /** @@ -20,12 +19,6 @@ import mage.target.common.TargetCreaturePermanent; */ public final class KorEntanglers extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public KorEntanglers(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{W}"); this.subtype.add(SubType.KOR); @@ -36,7 +29,7 @@ public final class KorEntanglers extends CardImpl { // Rally — Whenever Kor Entanglers or another Ally enters the battlefield under your control, tap target creature an opponent controls. Ability ability = new AllyEntersBattlefieldTriggeredAbility(new TapTargetEffect(), false); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/k/KorHookmaster.java b/Mage.Sets/src/mage/cards/k/KorHookmaster.java index 84fb177984c..91b330cfa5d 100644 --- a/Mage.Sets/src/mage/cards/k/KorHookmaster.java +++ b/Mage.Sets/src/mage/cards/k/KorHookmaster.java @@ -10,8 +10,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; /** @@ -20,12 +19,6 @@ import mage.target.common.TargetCreaturePermanent; */ public final class KorHookmaster extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public KorHookmaster(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}"); this.subtype.add(SubType.KOR); @@ -38,7 +31,7 @@ public final class KorHookmaster extends CardImpl { // That creature doesn't untap during its controller's next untap step. EntersBattlefieldTriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new TapTargetEffect()); ability.addEffect(new DontUntapInControllersNextUntapStepTargetEffect()); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/k/KorozdaGuildmage.java b/Mage.Sets/src/mage/cards/k/KorozdaGuildmage.java index 64bcc640084..b52a1d5fddc 100644 --- a/Mage.Sets/src/mage/cards/k/KorozdaGuildmage.java +++ b/Mage.Sets/src/mage/cards/k/KorozdaGuildmage.java @@ -1,4 +1,3 @@ - package mage.cards.k; import java.util.UUID; @@ -8,6 +7,7 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.dynamicvalue.common.SacrificeCostCreaturesToughness; +import mage.abilities.effects.Effect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; @@ -17,7 +17,6 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.permanent.token.SaprolingToken; @@ -46,13 +45,17 @@ public final class KorozdaGuildmage extends CardImpl { this.toughness = new MageInt(2); // {1}{B}{G}: Target creature gets +1/+1 and gains intimidate until end of turn. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostTargetEffect(1,1, Duration.EndOfTurn),new ManaCostsImpl("{1}{B}{G}")); - ability.addEffect(new GainAbilityTargetEffect(IntimidateAbility.getInstance(), Duration.EndOfTurn)); + Effect effect = new BoostTargetEffect(1, 1, Duration.EndOfTurn); + effect.setText("target creature gets +1/+1"); + Ability ability = new SimpleActivatedAbility(effect, new ManaCostsImpl("{1}{B}{G}")); + effect = new GainAbilityTargetEffect(IntimidateAbility.getInstance(), Duration.EndOfTurn); + effect.setText("and gains intimidate until end of turn. (It can't be blocked except by artifact creatures and/or creatures that share a color with it.)"); + ability.addEffect(effect); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); // {2}{B}{G}, Sacrifice a nontoken creature: create X 1/1 green Saproling creature tokens, where X is the sacrificed creature's toughness. - ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new SaprolingToken(),SacrificeCostCreaturesToughness.instance),new ManaCostsImpl("{2}{B}{G}")); + ability = new SimpleActivatedAbility(new CreateTokenEffect(new SaprolingToken(),SacrificeCostCreaturesToughness.instance),new ManaCostsImpl("{2}{B}{G}")); ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1,1,filter, true))); this.addAbility(ability); @@ -66,4 +69,4 @@ public final class KorozdaGuildmage extends CardImpl { public KorozdaGuildmage copy() { return new KorozdaGuildmage(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/k/KoseiPenitentWarlord.java b/Mage.Sets/src/mage/cards/k/KoseiPenitentWarlord.java new file mode 100644 index 00000000000..4e113e3d678 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KoseiPenitentWarlord.java @@ -0,0 +1,122 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DealsDamageToOpponentTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.Counter; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +import java.util.Objects; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KoseiPenitentWarlord extends CardImpl { + + public KoseiPenitentWarlord(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.OGRE); + this.subtype.add(SubType.SAMURAI); + this.power = new MageInt(0); + this.toughness = new MageInt(5); + + // As long as Kosei, Penitent Warlord is enchanted, equipped, and has a counter on it, Kosei has "Whenever Kosei, Penitent Warlord deals combat damage to an opponent, you draw that many cards and Kosei deals that much damage to each other opponent." + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new GainAbilitySourceEffect(new DealsDamageToOpponentTriggeredAbility( + new KoseiPenitentWarlordEffect(), false, true, true + )), KoseiPenitentWarlordCondition.instance, "as long as {this} is enchanted, equipped, " + + "and has a counter on it, {this} has \"Whenever {this} deals combat damage to an opponent, " + + "you draw that many cards and {this} deals that much damage to each other opponent.\"" + ))); + } + + private KoseiPenitentWarlord(final KoseiPenitentWarlord card) { + super(card); + } + + @Override + public KoseiPenitentWarlord copy() { + return new KoseiPenitentWarlord(this); + } +} + +enum KoseiPenitentWarlordCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game); + return sourcePermanent != null + && sourcePermanent.getCounters(game) + .values() + .stream() + .mapToInt(Counter::getCount) + .anyMatch(x -> x > 0) + && sourcePermanent + .getAttachments() + .stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .anyMatch(permanent -> permanent.hasSubtype(SubType.EQUIPMENT, game)) + && sourcePermanent + .getAttachments() + .stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .anyMatch(permanent -> permanent.hasSubtype(SubType.AURA, game)); + } +} + +class KoseiPenitentWarlordEffect extends OneShotEffect { + + KoseiPenitentWarlordEffect() { + super(Outcome.Benefit); + staticText = "you draw that many cards and {this} deals that much damage to each other opponent"; + } + + private KoseiPenitentWarlordEffect(final KoseiPenitentWarlordEffect effect) { + super(effect); + } + + @Override + public KoseiPenitentWarlordEffect copy() { + return new KoseiPenitentWarlordEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + int damage = (Integer) getValue("damage"); + if (controller == null || damage < 1) { + return false; + } + controller.drawCards(damage, source, game); + UUID damagedOpponentId = getTargetPointer().getFirst(game, source); + for (UUID opponentId : game.getOpponents(source.getControllerId())) { + if (opponentId.equals(damagedOpponentId)) { + continue; + } + Player opponent = game.getPlayer(opponentId); + if (opponent != null) { + opponent.damage(damage, source, game); + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/k/KothOfTheHammer.java b/Mage.Sets/src/mage/cards/k/KothOfTheHammer.java index 6f0b674845a..864a76c9f8e 100644 --- a/Mage.Sets/src/mage/cards/k/KothOfTheHammer.java +++ b/Mage.Sets/src/mage/cards/k/KothOfTheHammer.java @@ -6,7 +6,6 @@ import mage.MageInt; import mage.Mana; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.mana.DynamicManaEffect; import mage.abilities.effects.common.GetEmblemEffect; @@ -43,7 +42,7 @@ public final class KothOfTheHammer extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.KOTH); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +1: Untap target Mountain. It becomes a 4/4 red Elemental creature until end of turn. It's still a land. Ability ability = new LoyaltyAbility(new UntapTargetEffect(), 1); diff --git a/Mage.Sets/src/mage/cards/k/KothophedSoulHoarder.java b/Mage.Sets/src/mage/cards/k/KothophedSoulHoarder.java index f117bb484c6..fca295312f4 100644 --- a/Mage.Sets/src/mage/cards/k/KothophedSoulHoarder.java +++ b/Mage.Sets/src/mage/cards/k/KothophedSoulHoarder.java @@ -48,11 +48,11 @@ public final class KothophedSoulHoarder extends CardImpl { // Whenever a permanent owned by another player is put into the graveyard from the battlefield, you draw one card and lose 1 life. Effect effect = new DrawCardSourceControllerEffect(1); - effect.setText("you draw one card"); + effect.setText("you draw a card"); Ability ability = new ZoneChangeAllTriggeredAbility(Zone.BATTLEFIELD, Zone.BATTLEFIELD, Zone.GRAVEYARD, effect, filter, - "Whenever a permanent owned by another player is put into the graveyard from the battlefield, ", false); + "Whenever a permanent owned by another player is put into a graveyard from the battlefield, ", false); effect = new LoseLifeSourceControllerEffect(1); - effect.setText("and lose 1 life"); + effect.setText("and you lose 1 life"); ability.addEffect(effect); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/k/KotoriPilotProdigy.java b/Mage.Sets/src/mage/cards/k/KotoriPilotProdigy.java new file mode 100644 index 00000000000..b19a7ef1cb5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KotoriPilotProdigy.java @@ -0,0 +1,68 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfCombatTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.CrewAbility; +import mage.abilities.keyword.LifelinkAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterArtifactCreaturePermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KotoriPilotProdigy extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent(SubType.VEHICLE, "Vehicles"); + private static final FilterPermanent filter2 = new FilterArtifactCreaturePermanent("artifact creature you control"); + + static { + filter.add(TargetController.YOU.getControllerPredicate()); + } + + public KotoriPilotProdigy(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.MOONFOLK); + this.subtype.add(SubType.PILOT); + this.power = new MageInt(2); + this.toughness = new MageInt(4); + + // Vehicles you control have crew 2. + this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( + new CrewAbility(2), Duration.WhileOnBattlefield, filter + ))); + + // At the beginning of combat on your turn, target artifact creature you control gains lifelink and vigilance until end of turn. + Ability ability = new BeginningOfCombatTriggeredAbility( + new GainAbilityTargetEffect(LifelinkAbility.getInstance()) + .setText("target artifact creature you control gains lifelink"), + TargetController.YOU, false + ); + ability.addEffect(new GainAbilityTargetEffect( + VigilanceAbility.getInstance() + ).setText("and vigilance until end of turn")); + ability.addTarget(new TargetPermanent(filter2)); + this.addAbility(ability); + } + + private KotoriPilotProdigy(final KotoriPilotProdigy card) { + super(card); + } + + @Override + public KotoriPilotProdigy copy() { + return new KotoriPilotProdigy(this); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KotoseTheSilentSpider.java b/Mage.Sets/src/mage/cards/k/KotoseTheSilentSpider.java new file mode 100644 index 00000000000..5f89cc4ac04 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KotoseTheSilentSpider.java @@ -0,0 +1,208 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.effects.OneShotEffect; +import mage.cards.*; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.NamePredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.stack.Spell; +import mage.players.Player; +import mage.target.common.TargetCardInGraveyard; +import mage.target.common.TargetCardInHand; +import mage.target.common.TargetCardInLibrary; +import mage.target.common.TargetCardInOpponentsGraveyard; +import mage.util.CardUtil; +import mage.watchers.Watcher; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class KotoseTheSilentSpider extends CardImpl { + + private static final FilterCard filter + = new FilterCard("card other than a basic land card in an opponent's graveyard"); + + static { + filter.add(Predicates.not(Predicates.and( + CardType.LAND.getPredicate(), + SuperType.BASIC.getPredicate() + ))); + } + + public KotoseTheSilentSpider(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{B}"); + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.NINJA); + + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // When Kotose, Silent Spider enters the battlefield, exile target card in an opponent's graveyard other than a basic land card. Search that player's graveyard, hand, and library for any number of cards with the same name as that card and exile them. For as long as you control Kotose, you may play one of the exiled cards, and you may spend mana as though it were mana of any color to cast it. + Ability ability = new EntersBattlefieldTriggeredAbility(new KotoseTheSilentSpiderEffect()); + ability.addTarget(new TargetCardInOpponentsGraveyard(filter)); + this.addAbility(ability, new KotoseTheSilentSpiderWatcher()); + } + + private KotoseTheSilentSpider(final KotoseTheSilentSpider card) { + super(card); + } + + @Override + public KotoseTheSilentSpider copy() { + return new KotoseTheSilentSpider(this); + } +} + +class KotoseTheSilentSpiderEffect extends OneShotEffect { + + public KotoseTheSilentSpiderEffect() { + super(Outcome.Exile); + this.staticText = "exile target card other than a basic land card from an opponent's graveyard. " + + "Search that player's graveyard, hand, and library for any number of cards with the same name " + + "as that card and exile them. Then that player shuffles. For as long as you control {this}, you may " + + "play one of the exiled cards, and you may spend mana as though it were mana of any color to cast it"; + } + + public KotoseTheSilentSpiderEffect(final KotoseTheSilentSpiderEffect effect) { + super(effect); + } + + @Override + public KotoseTheSilentSpiderEffect copy() { + return new KotoseTheSilentSpiderEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Card card = game.getCard(getTargetPointer().getFirst(game, source)); + if (controller == null || card == null) { + return false; + } + Player opponent = game.getPlayer(card.getOwnerId()); + if (opponent == null) { + return false; + } + UUID exileId = CardUtil.getExileZoneId(game, source); + String exileName = CardUtil.getSourceName(game, source); + controller.moveCardsToExile(card, source, game, true, exileId, exileName); + Cards cards = new CardsImpl(); + FilterCard filter = new FilterCard("cards named " + card.getName() + " from " + opponent.getName() + "'s graveyard"); + filter.add(new NamePredicate(card.getName())); + + TargetCardInGraveyard targetCardInGraveyard = new TargetCardInGraveyard(0, Integer.MAX_VALUE, filter); + controller.choose(outcome, opponent.getGraveyard(), targetCardInGraveyard, game); + cards.addAll(targetCardInGraveyard.getTargets()); + + filter.setMessage("cards named " + card.getName() + " from " + opponent.getName() + "'s hand"); + TargetCardInHand targetCardInHand = new TargetCardInHand(0, Integer.MAX_VALUE, filter); + controller.choose(outcome, opponent.getHand(), targetCardInHand, game); + cards.addAll(targetCardInHand.getTargets()); + + filter.setMessage("cards named " + card.getName() + " from " + opponent.getName() + "'s library"); + TargetCardInLibrary target = new TargetCardInLibrary(0, Integer.MAX_VALUE, filter); + controller.searchLibrary(target, source, game, opponent.getId()); + target.getTargets() + .stream() + .map(cardId -> opponent.getLibrary().getCard(cardId, game)) + .forEach(cards::add); + + Set cardSet = cards.getCards(game); + controller.moveCardsToExile(cardSet, source, game, true, exileId, exileName); + opponent.shuffleLibrary(source, game); + cardSet.add(card); + if (cardSet.isEmpty() || source.getSourcePermanentIfItStillExists(game) == null) { + return true; + } + KotoseTheSilentSpiderWatcher.addCards(source, cardSet, game); + for (Card exiledCard : cardSet) { + CardUtil.makeCardPlayable( + game, source, exiledCard, Duration.WhileControlled, true, + null, new KotoseTheSilentSpiderCondition(exiledCard, game) + ); + } + return true; + } +} + +class KotoseTheSilentSpiderCondition implements Condition { + + private final MageObjectReference mor; + + KotoseTheSilentSpiderCondition(Card card, Game game) { + this.mor = new MageObjectReference(card, game); + } + + @Override + public boolean apply(Game game, Ability source) { + return KotoseTheSilentSpiderWatcher.checkCard(game, source, mor); + } +} + +class KotoseTheSilentSpiderWatcher extends Watcher { + + private final Map>> morMap = new HashMap<>(); + + public KotoseTheSilentSpiderWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.CLEANUP_STEP_POST) { + morMap.entrySet().removeIf(e -> !e.getKey().zoneCounterIsCurrent(game)); + morMap.values() + .stream() + .flatMap(Collection::stream) + .map(set -> set.removeIf(mor -> !mor.zoneCounterIsCurrent(game))); + morMap.values().removeIf(Set::isEmpty); + return; + } + if (event.getType() != GameEvent.EventType.SPELL_CAST || event.getAdditionalReference() == null) { + return; + } + Spell spell = game.getSpell(event.getTargetId()); + if (spell == null) { + return; + } + morMap.getOrDefault( + event.getAdditionalReference().getApprovingMageObjectReference(), Collections.emptySet() + ).removeIf(set -> set + .stream() + .anyMatch(mor -> mor.getSourceId().equals(spell.getMainCard().getId()) + && mor.getZoneChangeCounter() + 1 == spell.getZoneChangeCounter(game))); + } + + static void addCards(Ability source, Set cards, Game game) { + game.getState() + .getWatcher(KotoseTheSilentSpiderWatcher.class) + .morMap + .computeIfAbsent(new MageObjectReference(source), x -> new HashSet<>()) + .add(cards + .stream() + .map(card -> new MageObjectReference(card, game)) + .collect(Collectors.toSet())); + } + + static boolean checkCard(Game game, Ability source, MageObjectReference mor) { + return game.getState() + .getWatcher(KotoseTheSilentSpiderWatcher.class) + .morMap + .getOrDefault(new MageObjectReference(source), Collections.emptySet()) + .stream() + .flatMap(Collection::stream) + .anyMatch(mor::equals); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KozilekTheGreatDistortion.java b/Mage.Sets/src/mage/cards/k/KozilekTheGreatDistortion.java index 24c768ee926..23050d59ae0 100644 --- a/Mage.Sets/src/mage/cards/k/KozilekTheGreatDistortion.java +++ b/Mage.Sets/src/mage/cards/k/KozilekTheGreatDistortion.java @@ -53,7 +53,7 @@ public final class KozilekTheGreatDistortion extends CardImpl { new CardsInHandCondition(ComparisonType.FEWER_THAN, 7), "When you cast this spell, if you have fewer than seven cards in hand, draw cards equal to the difference.")); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // Discard a card with converted mana cost X: Counter target spell with converted mana cost X. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CounterTargetEffect(), new KozilekDiscardCost()); diff --git a/Mage.Sets/src/mage/cards/k/KozileksShrieker.java b/Mage.Sets/src/mage/cards/k/KozileksShrieker.java index 07e31ce87ee..59b2420d18b 100644 --- a/Mage.Sets/src/mage/cards/k/KozileksShrieker.java +++ b/Mage.Sets/src/mage/cards/k/KozileksShrieker.java @@ -38,7 +38,8 @@ public final class KozileksShrieker extends CardImpl { effect.setText("{this} gets +1/+0"); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl("{C}")); effect = new GainAbilitySourceEffect(new MenaceAbility(), Duration.EndOfTurn); - effect.setText("and gains menace until end of turn"); + effect.setText("and gains menace until end of turn. " + + "(It can't be blocked except by two or more creatures. {C} represents colorless mana.)"); ability.addEffect(effect); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/k/KrallenhordeHowler.java b/Mage.Sets/src/mage/cards/k/KrallenhordeHowler.java index 6d7fcccf936..c57d2e864bb 100644 --- a/Mage.Sets/src/mage/cards/k/KrallenhordeHowler.java +++ b/Mage.Sets/src/mage/cards/k/KrallenhordeHowler.java @@ -29,7 +29,6 @@ public final class KrallenhordeHowler extends CardImpl { // this card is the second face of double-faced card this.nightCard = true; - this.transformable = true; // Creature spells you cast cost {1} less to cast. this.addAbility(new SimpleStaticAbility(new SpellsCostReductionControllerEffect(FILTER, 1))); diff --git a/Mage.Sets/src/mage/cards/k/KrallenhordeKiller.java b/Mage.Sets/src/mage/cards/k/KrallenhordeKiller.java index 71088f54106..6e7e4c4c8dd 100644 --- a/Mage.Sets/src/mage/cards/k/KrallenhordeKiller.java +++ b/Mage.Sets/src/mage/cards/k/KrallenhordeKiller.java @@ -28,7 +28,6 @@ public final class KrallenhordeKiller extends CardImpl { this.toughness = new MageInt(2); this.nightCard = true; - this.transformable = true; // {3}{G}: Krallenhorde Killer gets +4/+4 until end of turn. Activate this ability only once each turn. this.addAbility(new LimitedTimesPerTurnActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(4, 4, Duration.EndOfTurn), new ManaCostsImpl("{3}{G}"))); diff --git a/Mage.Sets/src/mage/cards/k/KrallenhordeWantons.java b/Mage.Sets/src/mage/cards/k/KrallenhordeWantons.java index abd6712e7e5..dc8e2624601 100644 --- a/Mage.Sets/src/mage/cards/k/KrallenhordeWantons.java +++ b/Mage.Sets/src/mage/cards/k/KrallenhordeWantons.java @@ -21,7 +21,6 @@ public final class KrallenhordeWantons extends CardImpl { // this card is the second face of double-faced card this.nightCard = true; - this.transformable = true; this.power = new MageInt(7); this.toughness = new MageInt(7); diff --git a/Mage.Sets/src/mage/cards/k/KraulForagers.java b/Mage.Sets/src/mage/cards/k/KraulForagers.java index 6677fcc4675..cf48129e93e 100644 --- a/Mage.Sets/src/mage/cards/k/KraulForagers.java +++ b/Mage.Sets/src/mage/cards/k/KraulForagers.java @@ -1,18 +1,19 @@ package mage.cards.k; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; import mage.abilities.effects.common.GainLifeEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.SubType; import mage.filter.StaticFilters; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class KraulForagers extends CardImpl { @@ -26,10 +27,9 @@ public final class KraulForagers extends CardImpl { this.toughness = new MageInt(4); // Undergrowth — When Kraul Foragers enters the battlefield, you gain 1 life for each creature card in your graveyard. - this.addAbility(new EntersBattlefieldTriggeredAbility( - new GainLifeEffect(new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_CREATURE)), false) - .withFlavorWord("Undergrowth") - ); + this.addAbility(new EntersBattlefieldTriggeredAbility(new GainLifeEffect( + new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_CREATURE) + ).setText("you gain 1 life for each creature card in your graveyard"), false).setAbilityWord(AbilityWord.UNDERGROWTH)); } private KraulForagers(final KraulForagers card) { diff --git a/Mage.Sets/src/mage/cards/k/KrothussLordOfTheDeep.java b/Mage.Sets/src/mage/cards/k/KrothussLordOfTheDeep.java new file mode 100644 index 00000000000..abd4bca4cf0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KrothussLordOfTheDeep.java @@ -0,0 +1,98 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenCopyTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterAttackingCreature; +import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KrothussLordOfTheDeep extends CardImpl { + + private static final FilterPermanent filter = new FilterAttackingCreature("another attacking creature"); + + static { + filter.add(AnotherPredicate.instance); + } + + public KrothussLordOfTheDeep(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.KRAKEN); + this.subtype.add(SubType.HORROR); + this.power = new MageInt(3); + this.toughness = new MageInt(5); + this.color.setBlue(true); + this.color.setBlack(true); + this.nightCard = true; + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever Krothuss, Lord of the Deep attacks, create a tapped and attacking token that's a copy of another target attacking creature. If that creature is a Kraken, Leviathan, Octopus, or Serpent, create two of those tokens instead. + Ability ability = new AttacksTriggeredAbility(new KrothussLordOfTheDeepEffect()); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + } + + private KrothussLordOfTheDeep(final KrothussLordOfTheDeep card) { + super(card); + } + + @Override + public KrothussLordOfTheDeep copy() { + return new KrothussLordOfTheDeep(this); + } +} + +class KrothussLordOfTheDeepEffect extends OneShotEffect { + + KrothussLordOfTheDeepEffect() { + super(Outcome.Benefit); + staticText = "create a tapped and attacking token that's a copy of another target attacking creature. " + + "If that creature is a Kraken, Leviathan, Octopus, or Serpent, create two of those tokens instead"; + } + + private KrothussLordOfTheDeepEffect(final KrothussLordOfTheDeepEffect effect) { + super(effect); + } + + @Override + public KrothussLordOfTheDeepEffect copy() { + return new KrothussLordOfTheDeepEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent == null) { + return false; + } + int count = permanent.hasSubtype(SubType.KRAKEN, game) + || permanent.hasSubtype(SubType.LEVIATHAN, game) + || permanent.hasSubtype(SubType.OCTOPUS, game) + || permanent.hasSubtype(SubType.SERPENT, game) ? 2 : 1; + return new CreateTokenCopyTargetEffect( + null, null, + false, count, true, true + ).apply(game, source); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KrovikanVampire.java b/Mage.Sets/src/mage/cards/k/KrovikanVampire.java index 8acfa763d22..ad8746a4fcc 100644 --- a/Mage.Sets/src/mage/cards/k/KrovikanVampire.java +++ b/Mage.Sets/src/mage/cards/k/KrovikanVampire.java @@ -84,7 +84,7 @@ class KrovikanVampireEffect extends OneShotEffect { }).map((creatureId) -> { OneShotEffect effect = new SacrificeTargetEffect(); effect.setText("Sacrifice this if Krovikan Vampire leaves the battlefield or its current controller loses control of it."); - effect.setTargetPointer(new FixedTarget(creatureId)); + effect.setTargetPointer(new FixedTarget(creatureId, game)); return effect; }).map((effect) -> new KrovikanVampireDelayedTriggeredAbility(effect, krovikanVampire.getId())).forEachOrdered((dTA) -> { game.addDelayedTriggeredAbility(dTA, source); diff --git a/Mage.Sets/src/mage/cards/k/KruinOutlaw.java b/Mage.Sets/src/mage/cards/k/KruinOutlaw.java index 63c8a27ebc3..71b48621976 100644 --- a/Mage.Sets/src/mage/cards/k/KruinOutlaw.java +++ b/Mage.Sets/src/mage/cards/k/KruinOutlaw.java @@ -22,7 +22,6 @@ public final class KruinOutlaw extends CardImpl { this.subtype.add(SubType.ROGUE); this.subtype.add(SubType.WEREWOLF); - this.transformable = true; this.secondSideCardClazz = mage.cards.t.TerrorOfKruinPass.class; this.power = new MageInt(2); diff --git a/Mage.Sets/src/mage/cards/k/KuldothaFlamefiend.java b/Mage.Sets/src/mage/cards/k/KuldothaFlamefiend.java index 28aa10b5a6c..c89a37a98e6 100644 --- a/Mage.Sets/src/mage/cards/k/KuldothaFlamefiend.java +++ b/Mage.Sets/src/mage/cards/k/KuldothaFlamefiend.java @@ -1,4 +1,3 @@ - package mage.cards.k; import java.util.UUID; @@ -11,7 +10,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.filter.common.FilterControlledArtifactPermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetControlledPermanent; import mage.target.common.TargetAnyTargetAmount; @@ -28,9 +27,9 @@ public final class KuldothaFlamefiend extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(4); - // When Kuldotha Flamefiend enters the battlefield, you may sacrifice an artifact. If you do, Kuldotha Flamefiend deals 4 damage divided as you choose among any number of target creatures and/or players. + // When Kuldotha Flamefiend enters the battlefield, you may sacrifice an artifact. If you do, Kuldotha Flamefiend deals 4 damage divided as you choose among any number of targets. EntersBattlefieldTriggeredAbility ability = - new EntersBattlefieldTriggeredAbility(new DoIfCostPaid(new DamageMultiEffect(4), new SacrificeTargetCost(new TargetControlledPermanent(new FilterControlledArtifactPermanent("an artifact")))), false); + new EntersBattlefieldTriggeredAbility(new DoIfCostPaid(new DamageMultiEffect(4), new SacrificeTargetCost(new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT_AN))), false); ability.addTarget(new TargetAnyTargetAmount(4)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/k/KumanoFacesKakkazan.java b/Mage.Sets/src/mage/cards/k/KumanoFacesKakkazan.java new file mode 100644 index 00000000000..80ea9b04903 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KumanoFacesKakkazan.java @@ -0,0 +1,176 @@ +package mage.cards.k; + +import java.util.Set; +import java.util.UUID; + +import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.common.SagaAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.common.ExileSagaAndReturnTransformedEffect; +import mage.abilities.keyword.TransformAbility; +import mage.constants.*; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.events.EntersTheBattlefieldEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.stack.Spell; +import mage.players.Player; + +/** + * + * @author weirddan455 + */ +public final class KumanoFacesKakkazan extends CardImpl { + + public KumanoFacesKakkazan(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{R}"); + + this.subtype.add(SubType.SAGA); + this.secondSideCardClazz = mage.cards.e.EtchingOfKumano.class; + + // (As this Saga enters and after your draw step, add a lore counter.) + SagaAbility sagaAbility = new SagaAbility(this); + + // I — Kumano Faces Kakkazan deals 1 damage to each opponent and each planeswalker they control. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, new KumanoFacesKakkazanDamageEffect()); + + // II — When you cast your next creature spell this turn, that creature enters the battlefield with an additional +1/+1 counter on it. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_II, new CreateDelayedTriggeredAbilityEffect(new KumanoFacesKakkazanTriggeredAbility())); + + // III — Exile this Saga, then return it to the battlefield transformed under your control. + this.addAbility(new TransformAbility()); + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, new ExileSagaAndReturnTransformedEffect()); + + this.addAbility(sagaAbility); + } + + private KumanoFacesKakkazan(final KumanoFacesKakkazan card) { + super(card); + } + + @Override + public KumanoFacesKakkazan copy() { + return new KumanoFacesKakkazan(this); + } +} + +class KumanoFacesKakkazanDamageEffect extends OneShotEffect { + + public KumanoFacesKakkazanDamageEffect() { + super(Outcome.Damage); + this.staticText = "{this} deals 1 damage to each opponent and each planeswalker they control"; + } + + private KumanoFacesKakkazanDamageEffect(final KumanoFacesKakkazanDamageEffect effect) { + super(effect); + } + + @Override + public KumanoFacesKakkazanDamageEffect copy() { + return new KumanoFacesKakkazanDamageEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Set opponents = game.getOpponents(source.getControllerId()); + if (opponents.isEmpty()) { + return false; + } + for (UUID opponentId : opponents) { + Player opponent = game.getPlayer(opponentId); + if (opponent != null) { + opponent.damage(1, source, game); + } + } + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(CardType.PLANESWALKER, game)) { + if (opponents.contains(permanent.getControllerId())) { + permanent.damage(1, source, game); + } + } + return true; + } +} + +class KumanoFacesKakkazanTriggeredAbility extends DelayedTriggeredAbility { + + public KumanoFacesKakkazanTriggeredAbility() { + super(null, Duration.EndOfTurn); + } + + private KumanoFacesKakkazanTriggeredAbility(final KumanoFacesKakkazanTriggeredAbility ability) { + super(ability); + } + + @Override + public KumanoFacesKakkazanTriggeredAbility copy() { + return new KumanoFacesKakkazanTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.SPELL_CAST; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (this.isControlledBy(event.getPlayerId())) { + Spell spell = game.getSpell(event.getTargetId()); + if (spell != null && spell.isCreature(game)) { + this.getEffects().clear(); + this.getEffects().add(new KumanoFacesKakkazanCounterEffect(spell.getSourceId())); + return true; + } + } + return false; + } + + @Override + public String getRule() { + return "When you cast your next creature spell this turn, that creature enters the battlefield with an additional +1/+1 counter on it."; + } +} + +class KumanoFacesKakkazanCounterEffect extends ReplacementEffectImpl { + + private final UUID spellCastId; + + public KumanoFacesKakkazanCounterEffect(UUID spellCastId) { + super(Duration.EndOfTurn, Outcome.BoostCreature); + this.spellCastId = spellCastId; + } + + private KumanoFacesKakkazanCounterEffect(final KumanoFacesKakkazanCounterEffect effect) { + super(effect); + this.spellCastId = effect.spellCastId; + } + + @Override + public KumanoFacesKakkazanCounterEffect copy() { + return new KumanoFacesKakkazanCounterEffect(this); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return spellCastId.equals(event.getTargetId()); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Permanent creature = ((EntersTheBattlefieldEvent) event).getTarget(); + if (creature != null) { + creature.addCounters(CounterType.P1P1.createInstance(), source.getControllerId(), source, game, event.getAppliedEffects()); + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/k/KunorosHoundOfAthreos.java b/Mage.Sets/src/mage/cards/k/KunorosHoundOfAthreos.java index 2f1c71eedc1..04ada10ff12 100644 --- a/Mage.Sets/src/mage/cards/k/KunorosHoundOfAthreos.java +++ b/Mage.Sets/src/mage/cards/k/KunorosHoundOfAthreos.java @@ -34,7 +34,7 @@ public final class KunorosHoundOfAthreos extends CardImpl { this.addAbility(VigilanceAbility.getInstance()); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // Lifelink this.addAbility(LifelinkAbility.getInstance()); diff --git a/Mage.Sets/src/mage/cards/k/KuraTheBoundlessSky.java b/Mage.Sets/src/mage/cards/k/KuraTheBoundlessSky.java new file mode 100644 index 00000000000..801a4ddbfa5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KuraTheBoundlessSky.java @@ -0,0 +1,88 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.dynamicvalue.common.LandsYouControlCount; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; +import mage.abilities.hint.common.LandsYouControlHint; +import mage.abilities.keyword.DeathtouchAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.token.SpiritGreenXToken; +import mage.target.common.TargetCardInLibrary; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KuraTheBoundlessSky extends CardImpl { + + public KuraTheBoundlessSky(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.DRAGON); + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Deathtouch + this.addAbility(DeathtouchAbility.getInstance()); + + // When Kura, the Boundless Sky dies, choose one — + // • Search your library for up to three land cards, reveal them, put them into your hand, then shuffle. + Ability ability = new DiesSourceTriggeredAbility(new SearchLibraryPutInHandEffect( + new TargetCardInLibrary(0, 3, StaticFilters.FILTER_CARD_LANDS), true + )); + + // • Create an X/X green Spirit creature token, where X is the number of lands you control. + ability.addMode(new Mode(new KuraTheBoundlessSkyEffect())); + this.addAbility(ability.addHint(LandsYouControlHint.instance)); + } + + private KuraTheBoundlessSky(final KuraTheBoundlessSky card) { + super(card); + } + + @Override + public KuraTheBoundlessSky copy() { + return new KuraTheBoundlessSky(this); + } +} + +class KuraTheBoundlessSkyEffect extends OneShotEffect { + + KuraTheBoundlessSkyEffect() { + super(Outcome.Benefit); + staticText = "create an X/X green Spirit creature token, where X is the number of lands you control"; + } + + private KuraTheBoundlessSkyEffect(final KuraTheBoundlessSkyEffect effect) { + super(effect); + } + + @Override + public KuraTheBoundlessSkyEffect copy() { + return new KuraTheBoundlessSkyEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + int xValue = LandsYouControlCount.instance.calculate(game, source, this); + return new SpiritGreenXToken(xValue).putOntoBattlefield(1, game, source); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KyodaiSoulOfKamigawa.java b/Mage.Sets/src/mage/cards/k/KyodaiSoulOfKamigawa.java new file mode 100644 index 00000000000..10e194c2782 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KyodaiSoulOfKamigawa.java @@ -0,0 +1,72 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.FlashAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.IndestructibleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterPermanent; +import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KyodaiSoulOfKamigawa extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent("another target permanent"); + + static { + filter.add(AnotherPredicate.instance); + } + + public KyodaiSoulOfKamigawa(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.DRAGON); + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When Kyodai, Soul of Kamigawa enters the battlefield, another target permanent gains indestructible for as long as you control Kyodai. + Ability ability = new EntersBattlefieldTriggeredAbility(new GainAbilityTargetEffect( + IndestructibleAbility.getInstance(), Duration.WhileControlled + ), false); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + + // {W}{U}{B}{R}{G}: Kyodai, Soul of Kamigawa gets +5/+5 until end of turn. + this.addAbility(new SimpleActivatedAbility(new BoostSourceEffect( + 5, 5, Duration.EndOfTurn + ), new ManaCostsImpl<>("{W}{U}{B}{R}{G}"))); + } + + private KyodaiSoulOfKamigawa(final KyodaiSoulOfKamigawa card) { + super(card); + } + + @Override + public KyodaiSoulOfKamigawa copy() { + return new KyodaiSoulOfKamigawa(this); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KyokiSanitysEclipse.java b/Mage.Sets/src/mage/cards/k/KyokiSanitysEclipse.java index f602e4985b4..d920c9cc1cf 100644 --- a/Mage.Sets/src/mage/cards/k/KyokiSanitysEclipse.java +++ b/Mage.Sets/src/mage/cards/k/KyokiSanitysEclipse.java @@ -32,7 +32,7 @@ public final class KyokiSanitysEclipse extends CardImpl { // Whenever you cast a Spirit or Arcane spell, target opponent exiles a card from their hand. Ability ability = new SpellCastControllerTriggeredAbility( new ExileFromZoneTargetEffect(Zone.HAND, false), - StaticFilters.SPIRIT_OR_ARCANE_CARD, false + StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false ); ability.addTarget(new TargetOpponent()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/k/KytheonHeroOfAkros.java b/Mage.Sets/src/mage/cards/k/KytheonHeroOfAkros.java index 0cb6d0ab75d..49348aea3ad 100644 --- a/Mage.Sets/src/mage/cards/k/KytheonHeroOfAkros.java +++ b/Mage.Sets/src/mage/cards/k/KytheonHeroOfAkros.java @@ -6,7 +6,7 @@ import java.util.UUID; import mage.MageInt; import mage.MageObjectReference; import mage.abilities.Ability; -import mage.abilities.Gender; +import mage.abilities.Pronoun; import mage.abilities.common.EndOfCombatTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.condition.Condition; @@ -40,13 +40,12 @@ public final class KytheonHeroOfAkros extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(1); - this.transformable = true; this.secondSideCardClazz = mage.cards.g.GideonBattleForged.class; // At end of combat, if Kytheon, Hero of Akros and at least two other creatures attacked this combat, exile Kytheon, // then return him to the battlefield transformed under his owner's control. this.addAbility(new TransformAbility()); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(new EndOfCombatTriggeredAbility(new ExileAndReturnTransformedSourceEffect(Gender.MALE), false), + this.addAbility(new ConditionalInterveningIfTriggeredAbility(new EndOfCombatTriggeredAbility(new ExileAndReturnTransformedSourceEffect(Pronoun.HE), false), new KytheonHeroOfAkrosCondition(), "At end of combat, if {this} and at least two other creatures attacked this combat, exile {this}, " + "then return him to the battlefield transformed under his owner's control."), new AttackedOrBlockedThisCombatWatcher()); diff --git a/Mage.Sets/src/mage/cards/l/LabyrinthRaptor.java b/Mage.Sets/src/mage/cards/l/LabyrinthRaptor.java index 0ad3bdccf87..f7ed9df1318 100644 --- a/Mage.Sets/src/mage/cards/l/LabyrinthRaptor.java +++ b/Mage.Sets/src/mage/cards/l/LabyrinthRaptor.java @@ -51,7 +51,7 @@ public final class LabyrinthRaptor extends CardImpl { this.toughness = new MageInt(2); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // Whenever a creature you control with menace becomes blocked, defending player sacrifices a creature blocking it. this.addAbility(new BecomesBlockedAllTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/l/LacerateFlesh.java b/Mage.Sets/src/mage/cards/l/LacerateFlesh.java new file mode 100644 index 00000000000..0d4f8c4eea3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LacerateFlesh.java @@ -0,0 +1,69 @@ +package mage.cards.l; + +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.Permanent; +import mage.game.permanent.token.BloodToken; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LacerateFlesh extends CardImpl { + + public LacerateFlesh(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{R}"); + + // Lacerate Flesh deals 4 damage to target creature. Create a number of Blood tokens equal to the amount of excess damage dealt to that creature this way. + this.getSpellAbility().addEffect(new LacerateFleshEffect()); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + private LacerateFlesh(final LacerateFlesh card) { + super(card); + } + + @Override + public LacerateFlesh copy() { + return new LacerateFlesh(this); + } +} + +class LacerateFleshEffect extends OneShotEffect { + + LacerateFleshEffect() { + super(Outcome.Benefit); + staticText = "{this} deals 4 damage to target creature. Create a number of Blood tokens " + + "equal to the amount of excess damage dealt to that creature this way"; + } + + private LacerateFleshEffect(final LacerateFleshEffect effect) { + super(effect); + } + + @Override + public LacerateFleshEffect copy() { + return new LacerateFleshEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent == null) { + return false; + } + int lethal = Math.min(permanent.getLethalDamage(source.getSourceId(), game), 4); + permanent.damage(4, source.getSourceId(), source, game); + if (lethal < 4) { + new BloodToken().putOntoBattlefield(4 - lethal, game, source); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/l/LagonnaBandElder.java b/Mage.Sets/src/mage/cards/l/LagonnaBandElder.java index daa7c07f50c..2c6fba72e17 100644 --- a/Mage.Sets/src/mage/cards/l/LagonnaBandElder.java +++ b/Mage.Sets/src/mage/cards/l/LagonnaBandElder.java @@ -31,7 +31,7 @@ public final class LagonnaBandElder extends CardImpl { // When Lagonna-Band Elder enters the battlefield, if you control an enchantment, you gain 3 life. Ability ability = new ConditionalInterveningIfTriggeredAbility( new EntersBattlefieldTriggeredAbility(new GainLifeEffect(3), false), - new PermanentsOnTheBattlefieldCondition(StaticFilters.FILTER_ENCHANTMENT_PERMANENT), + new PermanentsOnTheBattlefieldCondition(StaticFilters.FILTER_PERMANENT_ENCHANTMENT), "When Lagonna-Band Elder enters the battlefield, if you control an enchantment, you gain 3 life"); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/l/LaidToRest.java b/Mage.Sets/src/mage/cards/l/LaidToRest.java new file mode 100644 index 00000000000..d186f091be9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LaidToRest.java @@ -0,0 +1,44 @@ +package mage.cards.l; + +import mage.abilities.common.DiesCreatureTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LaidToRest extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledPermanent(SubType.HUMAN, "a Human you control"); + + public LaidToRest(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{G}"); + + // Whenever a Human you control dies, draw a card. + this.addAbility(new DiesCreatureTriggeredAbility( + new DrawCardSourceControllerEffect(1), false, filter + )); + + // Whenever a creature you control with a +1/+1 counter on it dies, you gain 2 life. + this.addAbility(new DiesCreatureTriggeredAbility(new GainLifeEffect(2), false, StaticFilters.FILTER_A_CONTROLLED_CREATURE_P1P1)); + } + + private LaidToRest(final LaidToRest card) { + super(card); + } + + @Override + public LaidToRest copy() { + return new LaidToRest(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LambholtButcher.java b/Mage.Sets/src/mage/cards/l/LambholtButcher.java index b44499d9a5f..7e168fbffb3 100644 --- a/Mage.Sets/src/mage/cards/l/LambholtButcher.java +++ b/Mage.Sets/src/mage/cards/l/LambholtButcher.java @@ -2,18 +2,11 @@ package mage.cards.l; import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.WerewolfBackTriggeredAbility; -import mage.abilities.condition.common.TwoOrMoreSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.TransformSourceEffect; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; /** * @@ -30,7 +23,6 @@ public final class LambholtButcher extends CardImpl { // this card is the second face of double-faced card this.nightCard = true; - this.transformable = true; // At the beginning of each upkeep, if a player cast two or more spells last turn, transform Lambholt Butcher. this.addAbility(new WerewolfBackTriggeredAbility()); diff --git a/Mage.Sets/src/mage/cards/l/LambholtElder.java b/Mage.Sets/src/mage/cards/l/LambholtElder.java index 6ef1223ee39..9706367e69e 100644 --- a/Mage.Sets/src/mage/cards/l/LambholtElder.java +++ b/Mage.Sets/src/mage/cards/l/LambholtElder.java @@ -23,7 +23,6 @@ public final class LambholtElder extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(2); - this.transformable = true; this.secondSideCardClazz = mage.cards.s.SilverpeltWerewolf.class; // At the beginning of each upkeep, if no spells were cast last turn, transform Lambholt Elder. diff --git a/Mage.Sets/src/mage/cards/l/LambholtPacifist.java b/Mage.Sets/src/mage/cards/l/LambholtPacifist.java index 023c468e6c2..3c19a77b79b 100644 --- a/Mage.Sets/src/mage/cards/l/LambholtPacifist.java +++ b/Mage.Sets/src/mage/cards/l/LambholtPacifist.java @@ -29,7 +29,6 @@ public final class LambholtPacifist extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(3); - this.transformable = true; this.secondSideCardClazz = LambholtButcher.class; // Lambholt Pacifist can't attack unless you control a creature with power 4 or greater. diff --git a/Mage.Sets/src/mage/cards/l/LambholtRaconteur.java b/Mage.Sets/src/mage/cards/l/LambholtRaconteur.java new file mode 100644 index 00000000000..8c0b060799b --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LambholtRaconteur.java @@ -0,0 +1,48 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.common.DamagePlayersEffect; +import mage.abilities.keyword.DayboundAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LambholtRaconteur extends CardImpl { + + public LambholtRaconteur(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WEREWOLF); + this.power = new MageInt(2); + this.toughness = new MageInt(4); + this.secondSideCardClazz = mage.cards.l.LambholtRavager.class; + + // Whenever you cast a noncreature spell, Lambholt Raconteur deals 1 damage to each opponent. + this.addAbility(new SpellCastControllerTriggeredAbility( + new DamagePlayersEffect(1, TargetController.OPPONENT), + StaticFilters.FILTER_SPELL_A_NON_CREATURE, false + )); + + // Daybound + this.addAbility(new DayboundAbility()); + } + + private LambholtRaconteur(final LambholtRaconteur card) { + super(card); + } + + @Override + public LambholtRaconteur copy() { + return new LambholtRaconteur(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LambholtRavager.java b/Mage.Sets/src/mage/cards/l/LambholtRavager.java new file mode 100644 index 00000000000..36eabbf2986 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LambholtRavager.java @@ -0,0 +1,48 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.common.DamagePlayersEffect; +import mage.abilities.keyword.NightboundAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LambholtRavager extends CardImpl { + + public LambholtRavager(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); + + this.subtype.add(SubType.WEREWOLF); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + this.color.setRed(true); + this.nightCard = true; + + // Whenever you cast a noncreature spell, Lambholt Ravager deals 2 damage to each opponent. + this.addAbility(new SpellCastControllerTriggeredAbility( + new DamagePlayersEffect(2, TargetController.OPPONENT), + StaticFilters.FILTER_SPELL_A_NON_CREATURE, false + )); + + // Nightbound + this.addAbility(new NightboundAbility()); + } + + private LambholtRavager(final LambholtRavager card) { + super(card); + } + + @Override + public LambholtRavager copy() { + return new LambholtRavager(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LamplighterOfSelhoff.java b/Mage.Sets/src/mage/cards/l/LamplighterOfSelhoff.java index a354078460a..b19c75453f7 100644 --- a/Mage.Sets/src/mage/cards/l/LamplighterOfSelhoff.java +++ b/Mage.Sets/src/mage/cards/l/LamplighterOfSelhoff.java @@ -41,7 +41,7 @@ public final class LamplighterOfSelhoff extends CardImpl { this.addAbility(new ConditionalInterveningIfTriggeredAbility( triggeredAbility, new PermanentsOnTheBattlefieldCondition(filter), - "When {this} enters the battlefield, if you control another Zombie, you may a draw card. If you do, discard a card.")); + "When {this} enters the battlefield, if you control another Zombie, you may draw a card. If you do, discard a card.")); } private LamplighterOfSelhoff(final LamplighterOfSelhoff card) { diff --git a/Mage.Sets/src/mage/cards/l/LancerSliver.java b/Mage.Sets/src/mage/cards/l/LancerSliver.java index c1eccc28917..b3e4f497227 100644 --- a/Mage.Sets/src/mage/cards/l/LancerSliver.java +++ b/Mage.Sets/src/mage/cards/l/LancerSliver.java @@ -28,7 +28,7 @@ public final class LancerSliver extends CardImpl { // Sliver creatures you control have first strike. this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( FirstStrikeAbility.getInstance(), Duration.WhileOnBattlefield, - StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS + StaticFilters.FILTER_PERMANENT_SLIVERS ))); } diff --git a/Mage.Sets/src/mage/cards/l/LanternBearer.java b/Mage.Sets/src/mage/cards/l/LanternBearer.java new file mode 100644 index 00000000000..4f44b1d854d --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LanternBearer.java @@ -0,0 +1,42 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.keyword.DisturbAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LanternBearer extends CardImpl { + + public LanternBearer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{U}"); + + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + this.secondSideCardClazz = mage.cards.l.LanternsLift.class; + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Disturb {2}{U} + this.addAbility(new DisturbAbility(this, "{2}{U}")); + } + + private LanternBearer(final LanternBearer card) { + super(card); + } + + @Override + public LanternBearer copy() { + return new LanternBearer(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LanternFlare.java b/Mage.Sets/src/mage/cards/l/LanternFlare.java new file mode 100644 index 00000000000..b127bd8df92 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LanternFlare.java @@ -0,0 +1,53 @@ +package mage.cards.l; + +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.common.CreaturesYouControlCount; +import mage.abilities.dynamicvalue.common.ManacostVariableValue; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.hint.common.CreaturesYouControlHint; +import mage.abilities.keyword.CleaveAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.target.common.TargetCreatureOrPlaneswalker; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LanternFlare extends CardImpl { + + public LanternFlare(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}"); + + // Cleave {X}{R}{W} + Ability ability = new CleaveAbility( + this, new DamageTargetEffect(ManacostVariableValue.REGULAR), "{X}{R}{W}" + ); + ability.addEffect(new GainLifeEffect(ManacostVariableValue.REGULAR)); + ability.addTarget(new TargetCreatureOrPlaneswalker()); + this.addAbility(ability); + + // Lantern Flare deals X damage to target creature or planeswalker and you gain X life. [X is the number of creatures you control.] + this.getSpellAbility().addEffect(new DamageTargetEffect( + CreaturesYouControlCount.instance + ).setText("{this} deals X damage to target creature or planeswalker")); + this.getSpellAbility().addEffect(new GainLifeEffect( + CreaturesYouControlCount.instance, + "and you gain X life. [X is the number of creatures you control.]" + )); + this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalker()); + this.getSpellAbility().addHint(CreaturesYouControlHint.instance); + } + + private LanternFlare(final LanternFlare card) { + super(card); + } + + @Override + public LanternFlare copy() { + return new LanternFlare(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LanternOfTheLost.java b/Mage.Sets/src/mage/cards/l/LanternOfTheLost.java new file mode 100644 index 00000000000..6da5e390ba2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LanternOfTheLost.java @@ -0,0 +1,89 @@ +package mage.cards.l; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.ExileSourceCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ExileTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInGraveyard; + +import java.util.Objects; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LanternOfTheLost extends CardImpl { + + public LanternOfTheLost(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}"); + + // When Lantern of the Lost enters the battlefield, exile target card from a graveyard. + Ability ability = new EntersBattlefieldTriggeredAbility(new ExileTargetEffect()); + ability.addTarget(new TargetCardInGraveyard()); + this.addAbility(ability); + + // {1}, {T}, Exile Lantern of the Lost: Exile all cards from all graveyards, then draw a card. + ability = new SimpleActivatedAbility(new LanternOfTheLostEffect(), new GenericManaCost(1)); + ability.addCost(new TapSourceCost()); + ability.addCost(new ExileSourceCost()); + this.addAbility(ability); + } + + private LanternOfTheLost(final LanternOfTheLost card) { + super(card); + } + + @Override + public LanternOfTheLost copy() { + return new LanternOfTheLost(this); + } +} + +class LanternOfTheLostEffect extends OneShotEffect { + + LanternOfTheLostEffect() { + super(Outcome.Benefit); + staticText = "exile all cards from all graveyards, then draw a card"; + } + + private LanternOfTheLostEffect(final LanternOfTheLostEffect effect) { + super(effect); + } + + @Override + public LanternOfTheLostEffect copy() { + return new LanternOfTheLostEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Cards cards = new CardsImpl(); + game.getState() + .getPlayersInRange(source.getControllerId(), game) + .stream() + .map(game::getPlayer) + .filter(Objects::nonNull) + .map(Player::getGraveyard) + .forEach(cards::addAll); + player.moveCards(cards, Zone.EXILED, source, game); + player.drawCards(1, source, game); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/l/LanternScout.java b/Mage.Sets/src/mage/cards/l/LanternScout.java index 042d19bd2a5..44304962ae5 100644 --- a/Mage.Sets/src/mage/cards/l/LanternScout.java +++ b/Mage.Sets/src/mage/cards/l/LanternScout.java @@ -1,48 +1,37 @@ - package mage.cards.l; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.AllyEntersBattlefieldTriggeredAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.GainAbilityAllEffect; import mage.abilities.keyword.LifelinkAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardIdPredicate; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * * @author fireshoes */ public final class LanternScout extends CardImpl { public LanternScout(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.SCOUT); this.subtype.add(SubType.ALLY); this.power = new MageInt(3); this.toughness = new MageInt(2); - FilterPermanent filter = new FilterPermanent("{this} or another Ally"); - filter.add(Predicates.or( - new CardIdPredicate(this.getId()), - SubType.ALLY.getPredicate())); - // Rally — Whenever Lantern Scout or another Ally enters the battlefield under your control, creatures you control gain lifelink until end of turn. - Effect effect = new GainAbilityAllEffect(LifelinkAbility.getInstance(), Duration.EndOfTurn, new FilterControlledCreaturePermanent()); - effect.setText("creatures you control gain lifelink until end of turn"); - Ability ability = new AllyEntersBattlefieldTriggeredAbility( - effect, false); - this.addAbility(ability); + this.addAbility(new AllyEntersBattlefieldTriggeredAbility(new GainAbilityAllEffect( + LifelinkAbility.getInstance(), Duration.EndOfTurn, + StaticFilters.FILTER_CONTROLLED_CREATURES + ), false).setAbilityWord(AbilityWord.RALLY)); } private LanternScout(final LanternScout card) { diff --git a/Mage.Sets/src/mage/cards/l/LanternsLift.java b/Mage.Sets/src/mage/cards/l/LanternsLift.java new file mode 100644 index 00000000000..2ab3b0e1d03 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LanternsLift.java @@ -0,0 +1,60 @@ +package mage.cards.l; + +import mage.abilities.Ability; +import mage.abilities.common.PutIntoGraveFromAnywhereSourceAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.ExileSourceEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LanternsLift extends CardImpl { + + public LanternsLift(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, ""); + + this.subtype.add(SubType.AURA); + this.color.setBlue(true); + this.nightCard = true; + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // Enchanted creature gets +1/+1 and has flying. + ability = new SimpleStaticAbility(new BoostEnchantedEffect( + 1, 1, Duration.WhileOnBattlefield + )); + ability.addEffect(new GainAbilityAttachedEffect( + FlyingAbility.getInstance(), AttachmentType.AURA + ).setText("and has flying")); + this.addAbility(ability); + + // If Lanterns' Lift would be put into a graveyard from anywhere, exile it instead. + this.addAbility(new PutIntoGraveFromAnywhereSourceAbility(new ExileSourceEffect().setText("exile it instead"))); + } + + private LanternsLift(final LanternsLift card) { + super(card); + } + + @Override + public LanternsLift copy() { + return new LanternsLift(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LathrilBladeOfTheElves.java b/Mage.Sets/src/mage/cards/l/LathrilBladeOfTheElves.java index b959655d1a8..c51eca941a8 100644 --- a/Mage.Sets/src/mage/cards/l/LathrilBladeOfTheElves.java +++ b/Mage.Sets/src/mage/cards/l/LathrilBladeOfTheElves.java @@ -6,8 +6,7 @@ import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.common.TapTargetCost; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.effects.Effect; +import mage.abilities.dynamicvalue.common.SavedDamageValue; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.LoseLifeOpponentsEffect; @@ -19,7 +18,6 @@ import mage.constants.SubType; import mage.constants.SuperType; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.permanent.TappedPredicate; -import mage.game.Game; import mage.game.permanent.token.ElfWarriorToken; import mage.target.common.TargetControlledPermanent; @@ -47,11 +45,11 @@ public final class LathrilBladeOfTheElves extends CardImpl { this.toughness = new MageInt(3); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // Whenever Lathril, Blade of the Elves deals combat damage to a player, create that many 1/1 green Elf Warrior creature tokens. this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new CreateTokenEffect( - new ElfWarriorToken(), LathrilBladeOfTheElvesValue.instance + new ElfWarriorToken(), SavedDamageValue.instance ).setText("create that many 1/1 green Elf Warrior creature tokens"), false, true)); // {T}, Tap ten untapped Elves you control: Each opponent loses 10 life and you gain 10 life. @@ -70,22 +68,3 @@ public final class LathrilBladeOfTheElves extends CardImpl { return new LathrilBladeOfTheElves(this); } } - -enum LathrilBladeOfTheElvesValue implements DynamicValue { - instance; - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - return (Integer) effect.getValue("damage"); - } - - @Override - public LathrilBladeOfTheElvesValue copy() { - return instance; - } - - @Override - public String getMessage() { - return ""; - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/l/LaurineTheDiversion.java b/Mage.Sets/src/mage/cards/l/LaurineTheDiversion.java new file mode 100644 index 00000000000..e7b7082d370 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LaurineTheDiversion.java @@ -0,0 +1,67 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.combat.GoadTargetEffect; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.abilities.keyword.PartnerWithAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.target.common.TargetControlledPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LaurineTheDiversion extends CardImpl { + + private static final FilterControlledPermanent filter = new FilterControlledPermanent("artifact or creature"); + + static { + filter.add(Predicates.or( + CardType.ARTIFACT.getPredicate(), + CardType.CREATURE.getPredicate() + )); + } + + public LaurineTheDiversion(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ROGUE); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Partner with Kamber, the Plunderer + this.addAbility(new PartnerWithAbility("Kamber, the Plunderer")); + + // First strike + this.addAbility(FirstStrikeAbility.getInstance()); + + // {2}, Sacrifice an artifact or creature: Goad target creature. + Ability ability = new SimpleActivatedAbility(new GoadTargetEffect(), new GenericManaCost(2)); + ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(filter))); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + private LaurineTheDiversion(final LaurineTheDiversion card) { + super(card); + } + + @Override + public LaurineTheDiversion copy() { + return new LaurineTheDiversion(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LavabellySliver.java b/Mage.Sets/src/mage/cards/l/LavabellySliver.java index 86d764c8918..303049facca 100644 --- a/Mage.Sets/src/mage/cards/l/LavabellySliver.java +++ b/Mage.Sets/src/mage/cards/l/LavabellySliver.java @@ -30,13 +30,15 @@ public final class LavabellySliver extends CardImpl { this.toughness = new MageInt(2); // Sliver creatures you control have "When this creature enters the battlefield, it deals 1 damage to target player or planeswalker and you gain 1 life." - Ability ability = new EntersBattlefieldTriggeredAbility(new DamageTargetEffect(1, "When this creature enters the battlefield, it"),false,true); + Ability ability = new EntersBattlefieldTriggeredAbility( + new DamageTargetEffect(1, "it"), + false, true + ).setTriggerPhrase("When this creature enters the battlefield, "); ability.addEffect(new GainLifeEffect(1).concatBy("and")); ability.addTarget(new TargetPlayerOrPlaneswalker()); this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( - ability, Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS) - .withForceQuotes() - )); + ability, Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_SLIVERS + ).withForceQuotes())); } private LavabellySliver(final LavabellySliver card) { diff --git a/Mage.Sets/src/mage/cards/l/LavacoreElemental.java b/Mage.Sets/src/mage/cards/l/LavacoreElemental.java index 0f4c20c0685..7166ff87572 100644 --- a/Mage.Sets/src/mage/cards/l/LavacoreElemental.java +++ b/Mage.Sets/src/mage/cards/l/LavacoreElemental.java @@ -16,7 +16,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SetTargetPointer; import mage.counters.CounterType; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; /** * @@ -42,7 +42,7 @@ public final class LavacoreElemental extends CardImpl { effect.setText("put a time counter on {this}"); this.addAbility(new DealsDamageToAPlayerAllTriggeredAbility( effect, - new FilterControlledCreaturePermanent("a creature you control"), false, SetTargetPointer.PERMANENT, true + StaticFilters.FILTER_CONTROLLED_A_CREATURE, false, SetTargetPointer.PERMANENT, true )); } diff --git a/Mage.Sets/src/mage/cards/l/LayBareTheHeart.java b/Mage.Sets/src/mage/cards/l/LayBareTheHeart.java index 1e0c52bedcd..7e6248399c3 100644 --- a/Mage.Sets/src/mage/cards/l/LayBareTheHeart.java +++ b/Mage.Sets/src/mage/cards/l/LayBareTheHeart.java @@ -1,4 +1,3 @@ - package mage.cards.l; import java.util.UUID; @@ -10,7 +9,7 @@ import mage.constants.SuperType; import mage.constants.TargetController; import mage.filter.FilterCard; import mage.filter.predicate.Predicates; -import mage.target.TargetPlayer; +import mage.target.common.TargetOpponent; /** * @@ -29,8 +28,8 @@ public final class LayBareTheHeart extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}"); // Target opponent reveals their hand. You choose a nonlegendary, nonland card from it. That player discards that card. - this.getSpellAbility().addTarget(new TargetPlayer()); - this.getSpellAbility().addEffect(new DiscardCardYouChooseTargetEffect(filter, TargetController.ANY)); + this.getSpellAbility().addTarget(new TargetOpponent()); + this.getSpellAbility().addEffect(new DiscardCardYouChooseTargetEffect(filter)); } private LayBareTheHeart(final LayBareTheHeart card) { diff --git a/Mage.Sets/src/mage/cards/l/LeagueGuildmage.java b/Mage.Sets/src/mage/cards/l/LeagueGuildmage.java index e51f1a594b7..cb9c694d33e 100644 --- a/Mage.Sets/src/mage/cards/l/LeagueGuildmage.java +++ b/Mage.Sets/src/mage/cards/l/LeagueGuildmage.java @@ -27,7 +27,7 @@ import mage.target.targetadjustment.TargetAdjuster; */ public final class LeagueGuildmage extends CardImpl { - private static final FilterSpell filter = new FilterInstantOrSorcerySpell("instant or sorcery you control with mana value X"); + private static final FilterSpell filter = new FilterInstantOrSorcerySpell("instant or sorcery spell you control with mana value X"); static { filter.add(TargetController.YOU.getControllerPredicate()); diff --git a/Mage.Sets/src/mage/cards/l/LeaveInTheDust.java b/Mage.Sets/src/mage/cards/l/LeaveInTheDust.java index 8647ad4a527..fdaf2059c2a 100644 --- a/Mage.Sets/src/mage/cards/l/LeaveInTheDust.java +++ b/Mage.Sets/src/mage/cards/l/LeaveInTheDust.java @@ -23,7 +23,7 @@ public final class LeaveInTheDust extends CardImpl { this.getSpellAbility().addTarget(new TargetNonlandPermanent()); // Draw a card. - this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("
")); } diff --git a/Mage.Sets/src/mage/cards/l/LeechGauntlet.java b/Mage.Sets/src/mage/cards/l/LeechGauntlet.java new file mode 100644 index 00000000000..a377d4ca590 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LeechGauntlet.java @@ -0,0 +1,49 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.LifelinkAbility; +import mage.abilities.keyword.ReconfigureAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LeechGauntlet extends CardImpl { + + public LeechGauntlet(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{1}{B}"); + + this.subtype.add(SubType.EQUIPMENT); + this.subtype.add(SubType.LEECH); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // Equipped creature has lifelink. + this.addAbility(new SimpleStaticAbility(new GainAbilityAttachedEffect( + LifelinkAbility.getInstance(), AttachmentType.EQUIPMENT + ))); + + // Reconfigure {4} + this.addAbility(new ReconfigureAbility("{4}")); + } + + private LeechGauntlet(final LeechGauntlet card) { + super(card); + } + + @Override + public LeechGauntlet copy() { + return new LeechGauntlet(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LeechingLurker.java b/Mage.Sets/src/mage/cards/l/LeechingLurker.java new file mode 100644 index 00000000000..1fc7474793a --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LeechingLurker.java @@ -0,0 +1,43 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.abilities.keyword.LifelinkAbility; +import mage.abilities.keyword.NightboundAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LeechingLurker extends CardImpl { + + public LeechingLurker(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); + + this.subtype.add(SubType.LEECH); + this.subtype.add(SubType.HORROR); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + this.color.setBlack(true); + this.nightCard = true; + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // Nightbound + this.addAbility(new NightboundAbility()); + } + + private LeechingLurker(final LeechingLurker card) { + super(card); + } + + @Override + public LeechingLurker copy() { + return new LeechingLurker(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LegionAngel.java b/Mage.Sets/src/mage/cards/l/LegionAngel.java index c9ed5e7a014..28de6e50402 100644 --- a/Mage.Sets/src/mage/cards/l/LegionAngel.java +++ b/Mage.Sets/src/mage/cards/l/LegionAngel.java @@ -37,7 +37,7 @@ public final class LegionAngel extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Legion Angel enters the battlefield, you may reveal a card you own named Legion Angel from outside the game and put it into your hand. - this.addAbility(new EntersBattlefieldTriggeredAbility(new WishEffect(filter, true, false) + this.addAbility(new EntersBattlefieldTriggeredAbility(new WishEffect(filter) .setText("you may reveal a card you own named Legion Angel from outside the game and put it into your hand")) .addHint(OpenSideboardHint.instance)); } diff --git a/Mage.Sets/src/mage/cards/l/LegionsInitiative.java b/Mage.Sets/src/mage/cards/l/LegionsInitiative.java index 228eecf1a86..59f63017d12 100644 --- a/Mage.Sets/src/mage/cards/l/LegionsInitiative.java +++ b/Mage.Sets/src/mage/cards/l/LegionsInitiative.java @@ -1,6 +1,5 @@ package mage.cards.l; -import mage.MageObject; import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -97,8 +96,7 @@ class LegionsInitiativeExileEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - MageObject sourceObject = source.getSourceObject(game); - if (player == null || sourceObject == null) { + if (player == null) { return false; } Cards cards = new CardsImpl(); @@ -108,7 +106,7 @@ class LegionsInitiativeExileEffect extends OneShotEffect { ).stream().filter(Objects::nonNull).forEach(cards::add); return player.moveCardsToExile( cards.getCards(game), source, game, true, - CardUtil.getExileZoneId(game, source), sourceObject.getIdName() + CardUtil.getExileZoneId(game, source), CardUtil.getSourceName(game, source) ); } diff --git a/Mage.Sets/src/mage/cards/l/LegionsLanding.java b/Mage.Sets/src/mage/cards/l/LegionsLanding.java index 62dc9fed1ce..baed5ab2413 100644 --- a/Mage.Sets/src/mage/cards/l/LegionsLanding.java +++ b/Mage.Sets/src/mage/cards/l/LegionsLanding.java @@ -28,7 +28,6 @@ public final class LegionsLanding extends CardImpl { this.addSuperType(SuperType.LEGENDARY); - this.transformable = true; this.secondSideCardClazz = mage.cards.a.AdantoTheFirstFort.class; // When Legion's Landing enters the battlefield, create a 1/1 white Vampire creature token with lifelink. @@ -36,7 +35,7 @@ public final class LegionsLanding extends CardImpl { // When you attack with three or more creatures, transform Legion's Landing. this.addAbility(new TransformAbility()); - this.addAbility(new LegionsLandingTriggeredAbility(new TransformSourceEffect(true))); + this.addAbility(new LegionsLandingTriggeredAbility(new TransformSourceEffect())); } private LegionsLanding(final LegionsLanding card) { diff --git a/Mage.Sets/src/mage/cards/l/LethalExploit.java b/Mage.Sets/src/mage/cards/l/LethalExploit.java new file mode 100644 index 00000000000..634113aa0e4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LethalExploit.java @@ -0,0 +1,81 @@ +package mage.cards.l; + +import java.util.UUID; + +import mage.MageObject; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCreaturePermanent; +import mage.watchers.common.ControlledModifiedCreatureAsSpellCastWatcher; + +/** + * + * @author weirddan455 + */ +public final class LethalExploit extends CardImpl { + + public LethalExploit(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{B}"); + + // Target creature gets -2/-2 until end of turn. It gets an additional -1/-1 until end of turn for each modified creature you controlled as you cast this spell. + this.getSpellAbility().addEffect(new LethalExploitEffect()); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + this.getSpellAbility().addWatcher(new ControlledModifiedCreatureAsSpellCastWatcher()); + } + + private LethalExploit(final LethalExploit card) { + super(card); + } + + @Override + public LethalExploit copy() { + return new LethalExploit(this); + } +} + +class LethalExploitEffect extends ContinuousEffectImpl { + + private int boostValue = -2; + + public LethalExploitEffect() { + super(Duration.EndOfTurn, Layer.PTChangingEffects_7, SubLayer.ModifyPT_7c, Outcome.UnboostCreature); + this.staticText = "Target creature gets -2/-2 until end of turn. It gets an additional -1/-1 until end of turn for each modified creature you controlled as you cast this spell"; + } + + private LethalExploitEffect(final LethalExploitEffect effect) { + super(effect); + this.boostValue = effect.boostValue; + } + + @Override + public LethalExploitEffect copy() { + return new LethalExploitEffect(this); + } + + @Override + public void init(Ability source, Game game) { + super.init(source, game); + ControlledModifiedCreatureAsSpellCastWatcher watcher = game.getState().getWatcher(ControlledModifiedCreatureAsSpellCastWatcher.class); + MageObject sourceObject = source.getSourceObject(game); + if (watcher != null && sourceObject != null) { + boostValue -= watcher.getModifiedCreatureCount(new MageObjectReference(sourceObject, game)); + } + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent target = game.getPermanent(source.getFirstTarget()); + if (target == null) { + return false; + } + target.addPower(boostValue); + target.addToughness(boostValue); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/l/LethargyTrap.java b/Mage.Sets/src/mage/cards/l/LethargyTrap.java index d00e7be8a9d..6450f30c337 100644 --- a/Mage.Sets/src/mage/cards/l/LethargyTrap.java +++ b/Mage.Sets/src/mage/cards/l/LethargyTrap.java @@ -1,4 +1,3 @@ - package mage.cards.l; import mage.abilities.Ability; @@ -11,8 +10,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AttackingPredicate; +import mage.filter.StaticFilters; import mage.game.Game; import java.util.UUID; @@ -23,12 +21,6 @@ import java.util.UUID; */ public final class LethargyTrap extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("attacking creatures"); - - static { - filter.add(AttackingPredicate.instance); - } - public LethargyTrap(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{3}{U}"); this.subtype.add(SubType.TRAP); @@ -37,8 +29,7 @@ public final class LethargyTrap extends CardImpl { this.addAbility(new AlternativeCostSourceAbility(new ManaCostsImpl("{U}"), LethargyTrapCondition.instance)); // Attacking creatures get -3/-0 until end of turn. - this.getSpellAbility().addEffect(new BoostAllEffect(-3, 0, Duration.EndOfTurn, filter, false)); - + this.getSpellAbility().addEffect(new BoostAllEffect(-3, 0, Duration.EndOfTurn, StaticFilters.FILTER_ATTACKING_CREATURES, false)); } private LethargyTrap(final LethargyTrap card) { diff --git a/Mage.Sets/src/mage/cards/l/LeylineOfVitality.java b/Mage.Sets/src/mage/cards/l/LeylineOfVitality.java index ceddbc9c130..6d8d57d6df8 100644 --- a/Mage.Sets/src/mage/cards/l/LeylineOfVitality.java +++ b/Mage.Sets/src/mage/cards/l/LeylineOfVitality.java @@ -33,7 +33,7 @@ public final class LeylineOfVitality extends CardImpl { this.addAbility(new EntersBattlefieldControlledTriggeredAbility( Zone.BATTLEFIELD, new GainLifeEffect(1), - StaticFilters.FILTER_PERMANENT_CREATURE_A, + StaticFilters.FILTER_PERMANENT_A_CREATURE, true) ); } diff --git a/Mage.Sets/src/mage/cards/l/LiberatingCombustion.java b/Mage.Sets/src/mage/cards/l/LiberatingCombustion.java index 939aabfa81b..56d2c4efe96 100644 --- a/Mage.Sets/src/mage/cards/l/LiberatingCombustion.java +++ b/Mage.Sets/src/mage/cards/l/LiberatingCombustion.java @@ -1,7 +1,5 @@ - package mage.cards.l; -import java.util.UUID; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.search.SearchLibraryGraveyardPutInHandEffect; import mage.cards.CardImpl; @@ -11,8 +9,9 @@ import mage.filter.FilterCard; import mage.filter.predicate.mageobject.NamePredicate; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class LiberatingCombustion extends CardImpl { @@ -29,9 +28,9 @@ public final class LiberatingCombustion extends CardImpl { // Liberating Combustion deals 6 damage to target creature. this.getSpellAbility().addEffect(new DamageTargetEffect(6)); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); - + // You may search your library and/or graveyard for a card named Chandra, Pyrogenius, reveal it, and put it into your hand. If you search your library this way, shuffle it. - this.getSpellAbility().addEffect(new SearchLibraryGraveyardPutInHandEffect(filter)); + this.getSpellAbility().addEffect(new SearchLibraryGraveyardPutInHandEffect(filter, false, true)); } private LiberatingCombustion(final LiberatingCombustion card) { diff --git a/Mage.Sets/src/mage/cards/l/LifeFromTheLoam.java b/Mage.Sets/src/mage/cards/l/LifeFromTheLoam.java index 31e5e2845fb..e6cbeb750dd 100644 --- a/Mage.Sets/src/mage/cards/l/LifeFromTheLoam.java +++ b/Mage.Sets/src/mage/cards/l/LifeFromTheLoam.java @@ -1,8 +1,6 @@ - package mage.cards.l; -import java.util.UUID; -import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; import mage.abilities.keyword.DredgeAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -10,18 +8,18 @@ import mage.constants.CardType; import mage.filter.common.FilterLandCard; import mage.target.common.TargetCardInYourGraveyard; +import java.util.UUID; + /** - * * @author jonubuu */ public final class LifeFromTheLoam extends CardImpl { public LifeFromTheLoam(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{G}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{G}"); // Return up to three target land cards from your graveyard to your hand. - this.getSpellAbility().addEffect(new ReturnToHandTargetEffect()); + this.getSpellAbility().addEffect(new ReturnFromGraveyardToHandTargetEffect()); this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, 3, new FilterLandCard("land cards from your graveyard"))); // Dredge 3 (If you would draw a card, instead you may put exactly three cards from the top of your library into your graveyard. If you do, return this card from your graveyard to your hand. Otherwise, draw a card.) this.addAbility(new DredgeAbility(3)); diff --git a/Mage.Sets/src/mage/cards/l/LifeMatrix.java b/Mage.Sets/src/mage/cards/l/LifeMatrix.java index 842430660a9..f1f6d57d3fe 100644 --- a/Mage.Sets/src/mage/cards/l/LifeMatrix.java +++ b/Mage.Sets/src/mage/cards/l/LifeMatrix.java @@ -1,6 +1,5 @@ package mage.cards.l; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.condition.common.IsStepCondition; @@ -20,8 +19,9 @@ import mage.constants.Zone; import mage.counters.CounterType; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author L_J */ public final class LifeMatrix extends CardImpl { @@ -37,8 +37,8 @@ public final class LifeMatrix extends CardImpl { new AddCountersTargetEffect(CounterType.MATRIX.createInstance()), new GenericManaCost(4), new IsStepCondition(PhaseStep.UPKEEP), "Put a matrix counter on target creature and " - + "that creature gains “Remove a matrix counter from this creature: " - + "Regenerate this creature.” Activate only during your upkeep."); + + "that creature gains \"Remove a matrix counter from this creature: " + + "Regenerate this creature.\" Activate only during your upkeep."); Ability ability2 = new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateSourceEffect(), new RemoveCountersSourceCost(CounterType.MATRIX.createInstance())); diff --git a/Mage.Sets/src/mage/cards/l/LifeOfToshiroUmezawa.java b/Mage.Sets/src/mage/cards/l/LifeOfToshiroUmezawa.java new file mode 100644 index 00000000000..1a1f049cdfe --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LifeOfToshiroUmezawa.java @@ -0,0 +1,62 @@ +package mage.cards.l; + +import mage.abilities.Mode; +import mage.abilities.common.SagaAbility; +import mage.abilities.effects.Effects; +import mage.abilities.effects.common.ExileSagaAndReturnTransformedEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.keyword.TransformAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SagaChapter; +import mage.constants.SubType; +import mage.target.Targets; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LifeOfToshiroUmezawa extends CardImpl { + + public LifeOfToshiroUmezawa(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}"); + + this.subtype.add(SubType.SAGA); + this.secondSideCardClazz = mage.cards.m.MemoryOfToshiro.class; + + // (As this Saga enters and after your draw step, add a lore counter.) + SagaAbility sagaAbility = new SagaAbility(this); + + // I, II — Choose one — + // • Target creature gets +2/+2 until end of turn. + // • Target creature gets -1/-1 until end of turn. + // • You gain 2 life. + Mode mode = new Mode(new BoostTargetEffect(-1, -1)); + mode.addTarget(new TargetCreaturePermanent()); + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_II, + new Effects(new BoostTargetEffect(2, 2)), + new Targets(new TargetCreaturePermanent()), false, + mode, new Mode(new GainLifeEffect(2)) + ); + + // III — Exile this Saga, then return it to the battlefield transformed under your control. + this.addAbility(new TransformAbility()); + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, new ExileSagaAndReturnTransformedEffect()); + + this.addAbility(sagaAbility); + } + + private LifeOfToshiroUmezawa(final LifeOfToshiroUmezawa card) { + super(card); + } + + @Override + public LifeOfToshiroUmezawa copy() { + return new LifeOfToshiroUmezawa(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LifecraftAwakening.java b/Mage.Sets/src/mage/cards/l/LifecraftAwakening.java index 9c08e51a19b..8a539a44f70 100644 --- a/Mage.Sets/src/mage/cards/l/LifecraftAwakening.java +++ b/Mage.Sets/src/mage/cards/l/LifecraftAwakening.java @@ -37,8 +37,9 @@ public final class LifecraftAwakening extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{X}{G}"); // Put X +1/+1 counters on target artifact you control. If it isn't a creature or Vehicle, it becomes a 0/0 Construct artifact creature. - ManacostVariableValue manaX = ManacostVariableValue.REGULAR; - getSpellAbility().addEffect(new AddCountersTargetEffect(CounterType.P1P1.createInstance(), manaX)); + getSpellAbility().addEffect(new AddCountersTargetEffect( + CounterType.P1P1.createInstance(), ManacostVariableValue.REGULAR + ).setText("put X +1/+1 counters on target artifact you control")); getSpellAbility().addTarget(new TargetArtifactPermanent(filter)); getSpellAbility().addEffect(new LifecraftAwakeningEffect()); } diff --git a/Mage.Sets/src/mage/cards/l/LifecraftersBestiary.java b/Mage.Sets/src/mage/cards/l/LifecraftersBestiary.java index bb3d1fa2fd5..31a0a343772 100644 --- a/Mage.Sets/src/mage/cards/l/LifecraftersBestiary.java +++ b/Mage.Sets/src/mage/cards/l/LifecraftersBestiary.java @@ -25,7 +25,7 @@ public final class LifecraftersBestiary extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); // At the beginning of your upkeep, scry 1. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new ScryEffect(1), TargetController.YOU, false)); + this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new ScryEffect(1, false), TargetController.YOU, false)); // Whenever you cast a creature spell, you may pay {G}. If you do, draw a card. this.addAbility(new SpellCastControllerTriggeredAbility(new DoIfCostPaid(new DrawCardSourceControllerEffect(1), new ManaCostsImpl("{G}")), StaticFilters.FILTER_SPELL_A_CREATURE, false)); diff --git a/Mage.Sets/src/mage/cards/l/LifecraftersGift.java b/Mage.Sets/src/mage/cards/l/LifecraftersGift.java index ce894c68822..afcc0dd1035 100644 --- a/Mage.Sets/src/mage/cards/l/LifecraftersGift.java +++ b/Mage.Sets/src/mage/cards/l/LifecraftersGift.java @@ -2,19 +2,13 @@ package mage.cards.l; import java.util.UUID; -import mage.MageObject; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.counter.AddCountersAllEffect; import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.counters.CounterType; -import mage.filter.common.FilterCreaturePermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; /** @@ -26,10 +20,14 @@ public final class LifecraftersGift extends CardImpl { public LifecraftersGift(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{G}"); - // Put a +1/+1 counter on target creature, then put a +1/+1 counter on each creature you control with a +1/+1 counter on it. + // Put a +1/+1 counter on target creature, getSpellAbility().addEffect(new AddCountersTargetEffect(CounterType.P1P1.createInstance(1))); getSpellAbility().addTarget(new TargetCreaturePermanent()); - getSpellAbility().addEffect(new LifecraftersGiftEffect()); + + // then put a +1/+1 counter on each creature you control with a +1/+1 counter on it. + getSpellAbility().addEffect(new AddCountersAllEffect( + CounterType.P1P1.createInstance(), StaticFilters.FILTER_CONTROLLED_CREATURE_P1P1 + ).concatBy(", then")); } private LifecraftersGift(final LifecraftersGift card) { @@ -41,39 +39,3 @@ public final class LifecraftersGift extends CardImpl { return new LifecraftersGift(this); } } - -class LifecraftersGiftEffect extends OneShotEffect { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); - - static { - filter.add(CounterType.P1P1.getPredicate()); - } - - public LifecraftersGiftEffect() { - super(Outcome.Benefit); - this.staticText = ", then put a +1/+1 counter on each creature you control with a +1/+1 counter on it"; - } - - public LifecraftersGiftEffect(final LifecraftersGiftEffect effect) { - super(effect); - } - - @Override - public LifecraftersGiftEffect copy() { - return new LifecraftersGiftEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = source.getSourceObject(game); - if (controller != null && sourceObject != null) { - for(Permanent permanent: game.getState().getBattlefield().getAllActivePermanents(filter , controller.getId(), game)) { - permanent.addCounters(CounterType.P1P1.createInstance(), source.getControllerId(), source, game); - game.informPlayers(sourceObject.getName() + ": Put a +1/+1 counter on " + permanent.getLogName()); - } - } - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/l/Lifegift.java b/Mage.Sets/src/mage/cards/l/Lifegift.java index e53da4bb838..d1a80b28252 100644 --- a/Mage.Sets/src/mage/cards/l/Lifegift.java +++ b/Mage.Sets/src/mage/cards/l/Lifegift.java @@ -8,7 +8,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Zone; -import mage.filter.common.FilterLandPermanent; +import mage.filter.StaticFilters; /** * @@ -21,7 +21,7 @@ public final class Lifegift extends CardImpl { // Whenever a land enters the battlefield, you may gain 1 life. - this.addAbility(new EntersBattlefieldAllTriggeredAbility(Zone.BATTLEFIELD, new GainLifeEffect(1), new FilterLandPermanent("a land"), true)); + this.addAbility(new EntersBattlefieldAllTriggeredAbility(Zone.BATTLEFIELD, new GainLifeEffect(1), StaticFilters.FILTER_LAND_A, true)); } private Lifegift(final Lifegift card) { diff --git a/Mage.Sets/src/mage/cards/l/Lifeline.java b/Mage.Sets/src/mage/cards/l/Lifeline.java index e828d0daa6c..f5003f3fd69 100644 --- a/Mage.Sets/src/mage/cards/l/Lifeline.java +++ b/Mage.Sets/src/mage/cards/l/Lifeline.java @@ -31,7 +31,7 @@ public final class Lifeline extends CardImpl { // Whenever a creature dies, if another creature is on the battlefield, return the first card to the battlefield under its owner's control at the beginning of the next end step. Ability ability = new ConditionalInterveningIfTriggeredAbility( new DiesCreatureTriggeredAbility(Zone.BATTLEFIELD, new LifelineEffect(), false, StaticFilters.FILTER_PERMANENT_CREATURE, true), - new PermanentsOnTheBattlefieldCondition(StaticFilters.FILTER_PERMANENT_CREATURE, ComparisonType.MORE_THAN, 0, false), + new PermanentsOnTheBattlefieldCondition(StaticFilters.FILTER_PERMANENT_CREATURE, false), "Whenever a creature dies, if another creature is on the battlefield, return the first card to the battlefield under its owner's control at the beginning of the next end step."); this.addAbility(ability); } @@ -69,7 +69,7 @@ class LifelineEffect extends OneShotEffect { Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false); effect.setTargetPointer(new FixedTarget(card, game)); effect.setText("return that card to the battlefield under it's owner's control at the beginning of the next end step"); - game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(Zone.BATTLEFIELD, effect, TargetController.ANY), source); + game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect, TargetController.ANY), source); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/l/LightPawsEmperorsVoice.java b/Mage.Sets/src/mage/cards/l/LightPawsEmperorsVoice.java new file mode 100644 index 00000000000..df403f8f1f1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LightPawsEmperorsVoice.java @@ -0,0 +1,135 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.filter.predicate.Predicate; +import mage.filter.predicate.card.AuraCardCanAttachToPermanentId; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.stack.Spell; +import mage.players.Player; +import mage.target.common.TargetCardInLibrary; +import mage.util.CardUtil; + +import java.util.Objects; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LightPawsEmperorsVoice extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent(SubType.AURA, "an Aura"); + + static { + filter.add(LightPawsEmperorsVoicePredicate.instance); + } + + public LightPawsEmperorsVoice(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.FOX); + this.subtype.add(SubType.ADVISOR); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Whenever an Aura enters the battlefield under your control, if you cast it, you may search your library for an Aura card with mana value less than or equal to that Aura and with a different name than each Aura you control, put that card onto the battlefield attached to Light-Paws, Emperor's Voice, then shuffle. + this.addAbility(new EntersBattlefieldControlledTriggeredAbility(new LightPawsEmperorsVoiceEffect(), filter)); + } + + private LightPawsEmperorsVoice(final LightPawsEmperorsVoice card) { + super(card); + } + + @Override + public LightPawsEmperorsVoice copy() { + return new LightPawsEmperorsVoice(this); + } +} + +enum LightPawsEmperorsVoicePredicate implements Predicate { + instance; + + @Override + public boolean apply(Permanent input, Game game) { + int zcc = input.getZoneChangeCounter(game); + Spell spell = game.getStack().getSpell(input.getId()); + return (spell != null && spell.getZoneChangeCounter(game) == zcc - 1) + || game.getLastKnownInformation(input.getId(), Zone.STACK, zcc - 1) != null; + } +} + +class LightPawsEmperorsVoiceEffect extends OneShotEffect { + + private static enum LightPawsEmperorsVoiceEffectPredicate implements ObjectSourcePlayerPredicate { + instance; + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.AURA); + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + return game.getBattlefield() + .getActivePermanents( + filter, input.getPlayerId(), + input.getSourceId(), game + ).stream() + .filter(Objects::nonNull) + .noneMatch(permanent -> CardUtil.haveSameNames(permanent, input.getObject())); + } + } + + LightPawsEmperorsVoiceEffect() { + super(Outcome.Benefit); + staticText = "if you cast it, you may search your library for an Aura card with mana value " + + "less than or equal to that Aura and with a different name than each Aura you control, " + + "put that card onto the battlefield attached to {this}, then shuffle"; + } + + private LightPawsEmperorsVoiceEffect(final LightPawsEmperorsVoiceEffect effect) { + super(effect); + } + + @Override + public LightPawsEmperorsVoiceEffect copy() { + return new LightPawsEmperorsVoiceEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Permanent aura = (Permanent) getValue("permanentEnteringBattlefield"); + if (player == null || aura == null || !player.chooseUse( + outcome, "Search for an Aura?", source, game + )) { + return false; + } + FilterCard filter = new FilterCard("Aura card with mana value less than or equal to " + aura.getManaValue()); + filter.add(SubType.AURA.getPredicate()); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, aura.getManaValue() + 1)); + filter.add(LightPawsEmperorsVoiceEffectPredicate.instance); + TargetCardInLibrary target = new TargetCardInLibrary(filter); + player.searchLibrary(target, source, game); + Card card = player.getLibrary().getCard(target.getFirstTarget(), game); + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (card != null && permanent != null + && new AuraCardCanAttachToPermanentId(permanent.getId()).apply(card, game)) { + game.getState().setValue("attachTo:" + card.getId(), permanent); + player.moveCards(card, Zone.BATTLEFIELD, source, game); + permanent.addAttachment(card.getId(), source, game); + } + player.shuffleLibrary(source, game); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/l/LightTheWay.java b/Mage.Sets/src/mage/cards/l/LightTheWay.java new file mode 100644 index 00000000000..1e1e9174df2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LightTheWay.java @@ -0,0 +1,54 @@ +package mage.cards.l; + +import java.util.UUID; + +import mage.abilities.Mode; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.abilities.effects.common.UntapTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicates; +import mage.target.TargetPermanent; +import mage.target.common.TargetControlledPermanent; + +/** + * + * @author weirddan455 + */ +public final class LightTheWay extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent("creature or Vehicle"); + + static { + filter.add(Predicates.or(CardType.CREATURE.getPredicate(), SubType.VEHICLE.getPredicate())); + } + + public LightTheWay(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{W}"); + + // Choose one — + // • Put a +1/+1 counter on target creature or Vehicle. Untap it. + this.getSpellAbility().addEffect(new AddCountersTargetEffect(CounterType.P1P1.createInstance())); + this.getSpellAbility().addEffect(new UntapTargetEffect().setText("Untap it")); + this.getSpellAbility().addTarget(new TargetPermanent(filter)); + + // • Return target permanent you control to its owner's hand. + Mode mode = new Mode(new ReturnToHandTargetEffect()); + mode.addTarget(new TargetControlledPermanent()); + this.getSpellAbility().addMode(mode); + } + + private LightTheWay(final LightTheWay card) { + super(card); + } + + @Override + public LightTheWay copy() { + return new LightTheWay(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LightUpTheStage.java b/Mage.Sets/src/mage/cards/l/LightUpTheStage.java index 991aa6c6326..111a0e26fc1 100644 --- a/Mage.Sets/src/mage/cards/l/LightUpTheStage.java +++ b/Mage.Sets/src/mage/cards/l/LightUpTheStage.java @@ -2,18 +2,11 @@ package mage.cards.l; import mage.abilities.Ability; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.AsThoughEffectImpl; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ExileTopXMayPlayUntilEndOfTurnEffect; import mage.abilities.keyword.SpectacleAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.game.Game; -import mage.players.Player; -import mage.target.targetpointer.FixedTarget; -import mage.util.CardUtil; import java.util.Set; import java.util.UUID; @@ -27,10 +20,12 @@ public final class LightUpTheStage extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{R}"); // Exile the top two cards of your library. Until the end of your next turn, you may play those cards. - this.getSpellAbility().addEffect(new LightUpTheStageEffect()); + this.getSpellAbility().addEffect(new ExileTopXMayPlayUntilEndOfTurnEffect( + 2, false, Duration.UntilEndOfYourNextTurn + )); // Spectacle {R} - this.addAbility(new SpectacleAbility(this, new ManaCostsImpl("{R}"))); + this.addAbility(new SpectacleAbility(this, new ManaCostsImpl<>("{R}"))); } private LightUpTheStage(final LightUpTheStage card) { @@ -42,85 +37,3 @@ public final class LightUpTheStage extends CardImpl { return new LightUpTheStage(this); } } - -class LightUpTheStageEffect extends OneShotEffect { - - LightUpTheStageEffect() { - super(Outcome.PlayForFree); - this.staticText = "Exile the top two cards of your library. Until the end of your next turn, you may play those cards"; - } - - private LightUpTheStageEffect(final LightUpTheStageEffect effect) { - super(effect); - } - - @Override - public LightUpTheStageEffect copy() { - return new LightUpTheStageEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Set cards = controller.getLibrary().getTopCards(game, 2); - Card sourceCard = game.getCard(source.getSourceId()); - controller.moveCardsToExile(cards, source, game, true, CardUtil.getCardExileZoneId(game, source), sourceCard != null ? sourceCard.getIdName() : ""); - - for (Card card : cards) { - ContinuousEffect effect = new LightUpTheStageMayPlayEffect(); - effect.setTargetPointer(new FixedTarget(card.getId())); - game.addEffect(effect, source); - } - - return true; - } - return false; - } -} - -class LightUpTheStageMayPlayEffect extends AsThoughEffectImpl { - - private int castOnTurn = 0; - - LightUpTheStageMayPlayEffect() { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.Custom, Outcome.Benefit); - this.staticText = "Until the end of your next turn, you may play that card."; - } - - private LightUpTheStageMayPlayEffect(final LightUpTheStageMayPlayEffect effect) { - super(effect); - castOnTurn = effect.castOnTurn; - } - - @Override - public LightUpTheStageMayPlayEffect copy() { - return new LightUpTheStageMayPlayEffect(this); - } - - @Override - public void init(Ability source, Game game) { - super.init(source, game); - castOnTurn = game.getTurnNum(); - } - - @Override - public boolean isInactive(Ability source, Game game) { - if (castOnTurn != game.getTurnNum() && game.getPhase().getStep().getType() == PhaseStep.END_TURN) { - return game.isActivePlayer(source.getControllerId()); - } - return false; - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { - UUID objectIdToCast = CardUtil.getMainCardId(game, sourceId); - return source.isControlledBy(affectedControllerId) - && getTargetPointer().getTargets(game, source).contains(objectIdToCast); - } -} diff --git a/Mage.Sets/src/mage/cards/l/LightningRunner.java b/Mage.Sets/src/mage/cards/l/LightningRunner.java index c22a3a5e397..2a1aa9ee4b0 100644 --- a/Mage.Sets/src/mage/cards/l/LightningRunner.java +++ b/Mage.Sets/src/mage/cards/l/LightningRunner.java @@ -62,8 +62,8 @@ class LightningRunnerEffect extends OneShotEffect { LightningRunnerEffect() { super(Outcome.Benefit); - staticText = "you get {E}{E}, then you may pay {E}{E}{E}{E}{E}{E}{E}{E}. If you do, " - + "untap all creatures you control and after this phase, there is an additional combat phase"; + staticText = "you get {E}{E}, then you may pay {E}{E}{E}{E}{E}{E}{E}{E}. If you pay, " + + "untap all creatures you control, and after this phase, there is an additional combat phase"; } LightningRunnerEffect(final LightningRunnerEffect effect) { diff --git a/Mage.Sets/src/mage/cards/l/LightningWolf.java b/Mage.Sets/src/mage/cards/l/LightningWolf.java new file mode 100644 index 00000000000..ae907b4e6ab --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LightningWolf.java @@ -0,0 +1,42 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LightningWolf extends CardImpl { + + public LightningWolf(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); + + this.subtype.add(SubType.WOLF); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + + // {1}{R}: Lightning Wolf gains first strike until end of turn. Activate only as a sorcery. + this.addAbility(new ActivateAsSorceryActivatedAbility(new GainAbilitySourceEffect( + FirstStrikeAbility.getInstance(), Duration.EndOfTurn + ), new ManaCostsImpl<>("{1}{R}"))); + } + + private LightningWolf(final LightningWolf card) { + super(card); + } + + @Override + public LightningWolf copy() { + return new LightningWolf(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LikenessOfTheSeeker.java b/Mage.Sets/src/mage/cards/l/LikenessOfTheSeeker.java new file mode 100644 index 00000000000..a2fa4b8c3b0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LikenessOfTheSeeker.java @@ -0,0 +1,40 @@ +package mage.cards.l; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; +import mage.abilities.effects.common.UntapLandsEffect; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; + +/** + * + * @author weirddan455 + */ +public final class LikenessOfTheSeeker extends CardImpl { + + public LikenessOfTheSeeker(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, ""); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.MONK); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + this.color.setGreen(true); + this.nightCard = true; + + // Whenever Likeness of the Seeker becomes blocked, untap up to three lands you control. + this.addAbility(new BecomesBlockedSourceTriggeredAbility(new UntapLandsEffect(3, true, true), false)); + } + + private LikenessOfTheSeeker(final LikenessOfTheSeeker card) { + super(card); + } + + @Override + public LikenessOfTheSeeker copy() { + return new LikenessOfTheSeeker(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LilianaDeathMage.java b/Mage.Sets/src/mage/cards/l/LilianaDeathMage.java index 4c7d3bb390e..3942a1ec3dd 100644 --- a/Mage.Sets/src/mage/cards/l/LilianaDeathMage.java +++ b/Mage.Sets/src/mage/cards/l/LilianaDeathMage.java @@ -2,7 +2,6 @@ package mage.cards.l; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.LoseLifeTargetControllerEffect; @@ -34,7 +33,7 @@ public final class LilianaDeathMage extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.LILIANA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Return up to one target creature card from your graveyard to your hand. Ability plusAbility = new LoyaltyAbility(new LilianaDeathMagePlusEffect(), 1); diff --git a/Mage.Sets/src/mage/cards/l/LilianaDeathWielder.java b/Mage.Sets/src/mage/cards/l/LilianaDeathWielder.java index 45625088b15..9744b354e4c 100644 --- a/Mage.Sets/src/mage/cards/l/LilianaDeathWielder.java +++ b/Mage.Sets/src/mage/cards/l/LilianaDeathWielder.java @@ -4,7 +4,6 @@ package mage.cards.l; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.counter.AddCountersTargetEffect; @@ -39,7 +38,7 @@ public final class LilianaDeathWielder extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.LILIANA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +2: Put a -1/-1 counter on up to one target creature. LoyaltyAbility ability = new LoyaltyAbility(new AddCountersTargetEffect(CounterType.M1M1.createInstance(1)), 2); diff --git a/Mage.Sets/src/mage/cards/l/LilianaDeathsMajesty.java b/Mage.Sets/src/mage/cards/l/LilianaDeathsMajesty.java index e7433ed24d7..3c759a02eca 100644 --- a/Mage.Sets/src/mage/cards/l/LilianaDeathsMajesty.java +++ b/Mage.Sets/src/mage/cards/l/LilianaDeathsMajesty.java @@ -3,7 +3,6 @@ package mage.cards.l; import java.util.UUID; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.DestroyAllEffect; import mage.abilities.effects.common.MillCardsControllerEffect; @@ -38,7 +37,7 @@ public final class LilianaDeathsMajesty extends CardImpl { this.subtype.add(SubType.LILIANA); //Starting Loyalty: 5 - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +1: Create a 2/2 black Zombie creature token. Put the top two cards of your library into your graveyard. LoyaltyAbility ability = new LoyaltyAbility(new CreateTokenEffect(new ZombieToken()), 1); diff --git a/Mage.Sets/src/mage/cards/l/LilianaDefiantNecromancer.java b/Mage.Sets/src/mage/cards/l/LilianaDefiantNecromancer.java index 53587507f17..90f93f7558f 100644 --- a/Mage.Sets/src/mage/cards/l/LilianaDefiantNecromancer.java +++ b/Mage.Sets/src/mage/cards/l/LilianaDefiantNecromancer.java @@ -2,7 +2,6 @@ package mage.cards.l; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.costs.Cost; import mage.abilities.costs.common.PayVariableLoyaltyCost; import mage.abilities.effects.common.GetEmblemEffect; @@ -44,7 +43,7 @@ public final class LilianaDefiantNecromancer extends CardImpl { this.nightCard = true; - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +2: Each player discards a card. this.addAbility(new LoyaltyAbility(new DiscardEachPlayerEffect(1, false), 2)); diff --git a/Mage.Sets/src/mage/cards/l/LilianaDreadhordeGeneral.java b/Mage.Sets/src/mage/cards/l/LilianaDreadhordeGeneral.java index a3aae6bee5a..69fabb6d9a4 100644 --- a/Mage.Sets/src/mage/cards/l/LilianaDreadhordeGeneral.java +++ b/Mage.Sets/src/mage/cards/l/LilianaDreadhordeGeneral.java @@ -3,7 +3,6 @@ package mage.cards.l; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.common.DiesCreatureTriggeredAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; @@ -31,14 +30,14 @@ import java.util.UUID; */ public final class LilianaDreadhordeGeneral extends CardImpl { - private static final FilterControlledPermanent filter = new FilterControlledCreaturePermanent(" creatures"); + private static final FilterControlledPermanent filter = new FilterControlledCreaturePermanent("creatures"); public LilianaDreadhordeGeneral(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{4}{B}{B}"); this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.LILIANA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(6)); + this.setStartingLoyalty(6); // Whenever a creature you control dies, draw a card. this.addAbility(new DiesCreatureTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/l/LilianaHereticalHealer.java b/Mage.Sets/src/mage/cards/l/LilianaHereticalHealer.java index fd5b07b1b8b..20a173235ee 100644 --- a/Mage.Sets/src/mage/cards/l/LilianaHereticalHealer.java +++ b/Mage.Sets/src/mage/cards/l/LilianaHereticalHealer.java @@ -3,7 +3,7 @@ package mage.cards.l; import java.util.UUID; import mage.MageInt; -import mage.abilities.Gender; +import mage.abilities.Pronoun; import mage.abilities.common.DiesCreatureTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.ExileAndReturnTransformedSourceEffect; @@ -42,7 +42,6 @@ public final class LilianaHereticalHealer extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(3); - this.transformable = true; this.secondSideCardClazz = LilianaDefiantNecromancer.class; this.addAbility(new TransformAbility()); @@ -50,7 +49,7 @@ public final class LilianaHereticalHealer extends CardImpl { this.addAbility(LifelinkAbility.getInstance()); // Whenever another nontoken creature you control dies, exile Liliana Heretical Healer, then return her to the battlefield transformed under her owner's control. If you do, create a 2/2 black Zombie creature token. - this.addAbility(new DiesCreatureTriggeredAbility(new ExileAndReturnTransformedSourceEffect(Gender.FEMALE, + this.addAbility(new DiesCreatureTriggeredAbility(new ExileAndReturnTransformedSourceEffect(Pronoun.SHE, new CreateTokenEffect(new ZombieToken())), false, filter)); } diff --git a/Mage.Sets/src/mage/cards/l/LilianaOfTheDarkRealms.java b/Mage.Sets/src/mage/cards/l/LilianaOfTheDarkRealms.java index 59add5556bc..1eb3210e059 100644 --- a/Mage.Sets/src/mage/cards/l/LilianaOfTheDarkRealms.java +++ b/Mage.Sets/src/mage/cards/l/LilianaOfTheDarkRealms.java @@ -2,7 +2,6 @@ package mage.cards.l; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.GetEmblemEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; @@ -37,7 +36,7 @@ public final class LilianaOfTheDarkRealms extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.LILIANA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +1: Search your library for a Swamp card, reveal it, and put it into your hand. Then shuffle your library. this.addAbility(new LoyaltyAbility(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filter), true), 1)); diff --git a/Mage.Sets/src/mage/cards/l/LilianaOfTheVeil.java b/Mage.Sets/src/mage/cards/l/LilianaOfTheVeil.java index 0206587777a..bf41716944b 100644 --- a/Mage.Sets/src/mage/cards/l/LilianaOfTheVeil.java +++ b/Mage.Sets/src/mage/cards/l/LilianaOfTheVeil.java @@ -6,7 +6,6 @@ import java.util.List; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.SacrificeEffect; import mage.abilities.effects.common.discard.DiscardEachPlayerEffect; @@ -35,7 +34,7 @@ public final class LilianaOfTheVeil extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.LILIANA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +1: Each player discards a card. this.addAbility(new LoyaltyAbility(new DiscardEachPlayerEffect(), 1)); diff --git a/Mage.Sets/src/mage/cards/l/LilianaTheLastHope.java b/Mage.Sets/src/mage/cards/l/LilianaTheLastHope.java index 9d2d78f3b7b..fedfde8ecd5 100644 --- a/Mage.Sets/src/mage/cards/l/LilianaTheLastHope.java +++ b/Mage.Sets/src/mage/cards/l/LilianaTheLastHope.java @@ -4,7 +4,6 @@ package mage.cards.l; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.GetEmblemEffect; @@ -37,7 +36,7 @@ public final class LilianaTheLastHope extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.LILIANA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +1: Up to one target creature gets -2/-1 until your next turn. Effect effect = new BoostTargetEffect(-2, -1, Duration.UntilYourNextTurn); diff --git a/Mage.Sets/src/mage/cards/l/LilianaTheNecromancer.java b/Mage.Sets/src/mage/cards/l/LilianaTheNecromancer.java index 32fbb00af88..aa261351176 100644 --- a/Mage.Sets/src/mage/cards/l/LilianaTheNecromancer.java +++ b/Mage.Sets/src/mage/cards/l/LilianaTheNecromancer.java @@ -3,7 +3,6 @@ package mage.cards.l; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.LoseLifeTargetEffect; @@ -39,7 +38,7 @@ public final class LilianaTheNecromancer extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.LILIANA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Target player loses 2 life. Ability ability = new LoyaltyAbility(new LoseLifeTargetEffect(2), 1); diff --git a/Mage.Sets/src/mage/cards/l/LilianaUntouchedByDeath.java b/Mage.Sets/src/mage/cards/l/LilianaUntouchedByDeath.java index f15486a6883..d7915caf2f1 100644 --- a/Mage.Sets/src/mage/cards/l/LilianaUntouchedByDeath.java +++ b/Mage.Sets/src/mage/cards/l/LilianaUntouchedByDeath.java @@ -2,7 +2,6 @@ package mage.cards.l; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.AsThoughEffectImpl; @@ -33,7 +32,7 @@ public final class LilianaUntouchedByDeath extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.LILIANA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Put the top three cards of your library into your graveyard. If at least one of them is a Zombie card, each opponent loses 2 life and you gain 2 life. this.addAbility(new LoyaltyAbility(new LilianaUntouchedByDeathEffect(), 1)); diff --git a/Mage.Sets/src/mage/cards/l/LilianaVess.java b/Mage.Sets/src/mage/cards/l/LilianaVess.java index f5cc13bdc67..d378c51db85 100644 --- a/Mage.Sets/src/mage/cards/l/LilianaVess.java +++ b/Mage.Sets/src/mage/cards/l/LilianaVess.java @@ -5,7 +5,6 @@ import java.util.Set; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.discard.DiscardTargetEffect; import mage.abilities.effects.common.search.SearchLibraryPutOnLibraryEffect; @@ -34,7 +33,7 @@ public final class LilianaVess extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.LILIANA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +1: Target player discards a card. LoyaltyAbility ability1 = new LoyaltyAbility(new DiscardTargetEffect(1), 1); ability1.addTarget(new TargetPlayer()); diff --git a/Mage.Sets/src/mage/cards/l/LilianaWakerOfTheDead.java b/Mage.Sets/src/mage/cards/l/LilianaWakerOfTheDead.java index 81087aab8eb..4d59e896f13 100644 --- a/Mage.Sets/src/mage/cards/l/LilianaWakerOfTheDead.java +++ b/Mage.Sets/src/mage/cards/l/LilianaWakerOfTheDead.java @@ -2,7 +2,6 @@ package mage.cards.l; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; import mage.abilities.effects.OneShotEffect; @@ -40,7 +39,7 @@ public final class LilianaWakerOfTheDead extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.LILIANA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Each player discards a card. Each opponent who can't loses 3 life. this.addAbility(new LoyaltyAbility(new LilianaWakerOfTheDeadDiscardEffect(), 1)); diff --git a/Mage.Sets/src/mage/cards/l/LilianasSpoils.java b/Mage.Sets/src/mage/cards/l/LilianasSpoils.java index 10ba3bbeef0..623568c1822 100644 --- a/Mage.Sets/src/mage/cards/l/LilianasSpoils.java +++ b/Mage.Sets/src/mage/cards/l/LilianasSpoils.java @@ -36,7 +36,7 @@ public final class LilianasSpoils 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("
Look at the top five cards of your library. " + "You may reveal a black card from among them and put it into your hand. " + "Put the rest on the bottom of your library in a random order.") ); diff --git a/Mage.Sets/src/mage/cards/l/LilianasStandardBearer.java b/Mage.Sets/src/mage/cards/l/LilianasStandardBearer.java index af65fc92e7d..f505ea1140b 100644 --- a/Mage.Sets/src/mage/cards/l/LilianasStandardBearer.java +++ b/Mage.Sets/src/mage/cards/l/LilianasStandardBearer.java @@ -15,6 +15,7 @@ import mage.constants.WatcherScope; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; +import mage.util.CardUtil; import mage.watchers.Watcher; import java.util.HashMap; @@ -89,7 +90,7 @@ class LilianasStandardBearerWatcher extends Watcher { } ZoneChangeEvent zEvent = (ZoneChangeEvent) event; if (zEvent.isDiesEvent() && zEvent.getTarget().isCreature(game)) { - playerMap.compute(zEvent.getTarget().getControllerId(), (u, i) -> i == null ? 1 : Integer.sum(i, 1)); + playerMap.compute(zEvent.getTarget().getControllerId(), CardUtil::setOrIncrementValue); } } diff --git a/Mage.Sets/src/mage/cards/l/LimDulTheNecromancer.java b/Mage.Sets/src/mage/cards/l/LimDulTheNecromancer.java index 7a7c774461a..912aca2b988 100644 --- a/Mage.Sets/src/mage/cards/l/LimDulTheNecromancer.java +++ b/Mage.Sets/src/mage/cards/l/LimDulTheNecromancer.java @@ -15,6 +15,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; @@ -29,11 +30,9 @@ import java.util.UUID; */ public final class LimDulTheNecromancer extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature an opponent controls"); private static final FilterPermanent filter2 = new FilterPermanent("Zombie"); static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); filter2.add(SubType.ZOMBIE.getPredicate()); } @@ -46,7 +45,7 @@ public final class LimDulTheNecromancer extends CardImpl { this.toughness = new MageInt(4); // Whenever a creature an opponent controls dies, you may pay {1}{B}. If you do, return that card to the battlefield under your control. If it's a creature, it's a Zombie in addition to its other creature types. - this.addAbility(new DiesCreatureTriggeredAbility(new DoIfCostPaid(new LimDulTheNecromancerEffect(), new ManaCostsImpl("{1}{B}")), false, filter, true)); + this.addAbility(new DiesCreatureTriggeredAbility(new DoIfCostPaid(new LimDulTheNecromancerEffect(), new ManaCostsImpl("{1}{B}")), false, StaticFilters.FILTER_OPPONENTS_PERMANENT_A_CREATURE, true)); // {1}{B}: Regenerate target Zombie. Ability ability2 = new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateTargetEffect(), new ManaCostsImpl("{1}{B}")); @@ -91,7 +90,7 @@ class LimDulTheNecromancerEffect extends OneShotEffect { && card.isCreature(game)) { Permanent creature = game.getPermanent(card.getId()); ContinuousEffect effect = new AddCardSubTypeTargetEffect(SubType.ZOMBIE, Duration.WhileOnBattlefield); - effect.setTargetPointer(new FixedTarget(creature.getId())); + effect.setTargetPointer(new FixedTarget(creature.getId(), game)); game.addEffect(effect, source); } } diff --git a/Mage.Sets/src/mage/cards/l/LingeringDeath.java b/Mage.Sets/src/mage/cards/l/LingeringDeath.java index ee0dc66bd4a..b23aeeb4a21 100644 --- a/Mage.Sets/src/mage/cards/l/LingeringDeath.java +++ b/Mage.Sets/src/mage/cards/l/LingeringDeath.java @@ -1,4 +1,3 @@ - package mage.cards.l; import java.util.UUID; @@ -52,6 +51,7 @@ public final class LingeringDeath extends CardImpl { } class LingeringDeathAbility extends TriggeredAbilityImpl { + public LingeringDeathAbility() { super(Zone.BATTLEFIELD, new SacrificeTargetEffect()); } @@ -60,7 +60,6 @@ class LingeringDeathAbility extends TriggeredAbilityImpl { super(ability); } - @Override public LingeringDeathAbility copy() { return new LingeringDeathAbility(this); @@ -78,7 +77,7 @@ class LingeringDeathAbility extends TriggeredAbilityImpl { Permanent enchantedCreature = game.getPermanent(enchantment.getAttachedTo()); if (enchantedCreature != null) { if (event.getPlayerId().equals(enchantedCreature.getControllerId())) { - getEffects().get(0).setTargetPointer(new FixedTarget(enchantment.getAttachedTo())); + getEffects().get(0).setTargetPointer(new FixedTarget(enchantment.getAttachedTo(), game)); return true; } } diff --git a/Mage.Sets/src/mage/cards/l/LionSash.java b/Mage.Sets/src/mage/cards/l/LionSash.java new file mode 100644 index 00000000000..41425673a55 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LionSash.java @@ -0,0 +1,95 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.CountersSourceCount; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostEquippedEffect; +import mage.abilities.keyword.ReconfigureAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCardInGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LionSash extends CardImpl { + + private static final DynamicValue xValue = new CountersSourceCount(CounterType.P1P1); + + public LionSash(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{1}{W}"); + + this.subtype.add(SubType.EQUIPMENT); + this.subtype.add(SubType.CAT); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // {W}: Exile target card from a graveyard. If it was a permanent card, put a +1/+1 counter on Lion Sash. + Ability ability = new SimpleActivatedAbility(new LionSashEffect(), new ManaCostsImpl<>("{W}")); + ability.addTarget(new TargetCardInGraveyard()); + this.addAbility(ability); + + // Equipped creature gets +1/+1 for each +1/+1 counter on Lion Sash. + this.addAbility(new SimpleStaticAbility(new BoostEquippedEffect(xValue, xValue))); + + // Reconfigure {2} + this.addAbility(new ReconfigureAbility("{2}")); + } + + private LionSash(final LionSash card) { + super(card); + } + + @Override + public LionSash copy() { + return new LionSash(this); + } +} + +class LionSashEffect extends OneShotEffect { + + LionSashEffect() { + super(Outcome.Benefit); + staticText = "exile target card from a graveyard. If it was a permanent card, put a +1/+1 counter on {this}"; + } + + private LionSashEffect(final LionSashEffect effect) { + super(effect); + } + + @Override + public LionSashEffect copy() { + return new LionSashEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Card card = game.getCard(source.getFirstTarget()); + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (player == null || card == null) { + return false; + } + player.moveCards(card, Zone.EXILED, source, game); + if (card.isPermanent(game) && permanent != null) { + permanent.addCounters(CounterType.P1P1.createInstance(), source, game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/l/LittjaraMirrorlake.java b/Mage.Sets/src/mage/cards/l/LittjaraMirrorlake.java index cbe54ff2ab5..c42e0218b97 100644 --- a/Mage.Sets/src/mage/cards/l/LittjaraMirrorlake.java +++ b/Mage.Sets/src/mage/cards/l/LittjaraMirrorlake.java @@ -77,7 +77,7 @@ class LittjaraMirrorlakeEffect extends OneShotEffect { CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(); effect.setTargetPointer(new FixedTarget(source.getFirstTarget(), game)); effect.apply(game, source); - for (Permanent permanent : effect.getAddedPermanent()) { + for (Permanent permanent : effect.getAddedPermanents()) { if (permanent == null) { continue; } diff --git a/Mage.Sets/src/mage/cards/l/LivingBreakthrough.java b/Mage.Sets/src/mage/cards/l/LivingBreakthrough.java new file mode 100644 index 00000000000..ada98c582e5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LivingBreakthrough.java @@ -0,0 +1,107 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; +import mage.abilities.keyword.FlyingAbility; +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.game.Game; +import mage.game.events.GameEvent; +import mage.game.stack.Spell; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LivingBreakthrough extends CardImpl { + + public LivingBreakthrough(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, ""); + + this.subtype.add(SubType.MOONFOLK); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + this.color.setBlue(true); + this.nightCard = true; + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever you cast a spell, your opponents can't cast spells with the same mana value as that spell until your next turn. + this.addAbility(new SpellCastControllerTriggeredAbility(new LivingBreakthroughEffect(), false)); + } + + private LivingBreakthrough(final LivingBreakthrough card) { + super(card); + } + + @Override + public LivingBreakthrough copy() { + return new LivingBreakthrough(this); + } +} + +class LivingBreakthroughEffect extends ContinuousRuleModifyingEffectImpl { + + private int manaValue = -1; + + LivingBreakthroughEffect() { + super(Duration.UntilYourNextTurn, Outcome.Benefit); + staticText = "your opponents can't cast spells with the same mana value as that spell until your next turn"; + } + + private LivingBreakthroughEffect(final LivingBreakthroughEffect effect) { + super(effect); + } + + @Override + public LivingBreakthroughEffect copy() { + return new LivingBreakthroughEffect(this); + } + + @Override + public void init(Ability source, Game game) { + super.init(source, game); + Spell spell = (Spell) getValue("spellCast"); + if (spell != null) { + this.manaValue = spell.getManaValue(); + } + } + + @Override + public String getInfoMessage(Ability source, GameEvent event, Game game) { + MageObject mageObject = game.getObject(source.getSourceId()); + if (mageObject != null) { + return "You can't cast spells with mana value " + manaValue + + " this turn (" + mageObject.getIdName() + ")."; + } + return null; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.CAST_SPELL_LATE; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (!game.getOpponents(source.getControllerId()).contains(event.getPlayerId())) { + return false; + } + Spell spell = game.getStack().getSpell(event.getTargetId()); + return spell != null && spell.getManaValue() == this.manaValue; + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/l/LivingWish.java b/Mage.Sets/src/mage/cards/l/LivingWish.java index b6c62a9977d..908e848bd46 100644 --- a/Mage.Sets/src/mage/cards/l/LivingWish.java +++ b/Mage.Sets/src/mage/cards/l/LivingWish.java @@ -17,7 +17,7 @@ import mage.filter.predicate.Predicates; */ public final class LivingWish extends CardImpl { - private static final FilterCard filter = new FilterCard("a creature or land card"); + private static final FilterCard filter = new FilterCard("creature or land card"); static { filter.add(Predicates.or( @@ -28,7 +28,7 @@ public final class LivingWish extends CardImpl { public LivingWish(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{G}"); - // You may choose a creature or land card you own from outside the game, reveal that card, and put it into your hand. + // You may reveal a creature or land card you own from outside the game and put it into your hand. this.getSpellAbility().addEffect(new WishEffect(filter)); this.getSpellAbility().addHint(OpenSideboardHint.instance); diff --git a/Mage.Sets/src/mage/cards/l/LizardBlades.java b/Mage.Sets/src/mage/cards/l/LizardBlades.java new file mode 100644 index 00000000000..800d07411c8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LizardBlades.java @@ -0,0 +1,50 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.DoubleStrikeAbility; +import mage.abilities.keyword.ReconfigureAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LizardBlades extends CardImpl { + + public LizardBlades(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{1}{R}"); + + this.subtype.add(SubType.EQUIPMENT); + this.subtype.add(SubType.LIZARD); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Double strike + this.addAbility(DoubleStrikeAbility.getInstance()); + + // Equipped creature has double strike. + this.addAbility(new SimpleStaticAbility(new GainAbilityAttachedEffect( + DoubleStrikeAbility.getInstance(), AttachmentType.EQUIPMENT + ))); + + // Reconfigure {2} + this.addAbility(new ReconfigureAbility("{2}")); + + } + + private LizardBlades(final LizardBlades card) { + super(card); + } + + @Override + public LizardBlades copy() { + return new LizardBlades(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LoamDweller.java b/Mage.Sets/src/mage/cards/l/LoamDweller.java index 8702677de93..989ea0a8066 100644 --- a/Mage.Sets/src/mage/cards/l/LoamDweller.java +++ b/Mage.Sets/src/mage/cards/l/LoamDweller.java @@ -27,7 +27,7 @@ public final class LoamDweller extends CardImpl { // Whenever you cast a Spirit or Arcane spell, you may put a land card from your hand onto the battlefield tapped. this.addAbility(new SpellCastControllerTriggeredAbility( new PutCardFromHandOntoBattlefieldEffect(StaticFilters.FILTER_CARD_LAND_A, false, true), - StaticFilters.SPIRIT_OR_ARCANE_CARD, true)); + StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true)); } private LoamDweller(final LoamDweller card) { diff --git a/Mage.Sets/src/mage/cards/l/LoathsomeCatoblepas.java b/Mage.Sets/src/mage/cards/l/LoathsomeCatoblepas.java index 626ad2039d1..372d8773813 100644 --- a/Mage.Sets/src/mage/cards/l/LoathsomeCatoblepas.java +++ b/Mage.Sets/src/mage/cards/l/LoathsomeCatoblepas.java @@ -14,9 +14,8 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.TargetController; import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.Target; import mage.target.common.TargetCreaturePermanent; @@ -26,11 +25,6 @@ import mage.target.common.TargetCreaturePermanent; */ public final class LoathsomeCatoblepas extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public LoathsomeCatoblepas(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{5}{B}"); this.subtype.add(SubType.BEAST); @@ -42,7 +36,7 @@ public final class LoathsomeCatoblepas extends CardImpl { this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new MustBeBlockedByAtLeastOneSourceEffect(), new ManaCostsImpl("{2}{G}"))); // When Loathsome Catoblepas dies, target creature an opponent controls gets -3/-3 until end of turn. Ability ability = new DiesSourceTriggeredAbility(new BoostTargetEffect(-3,-3, Duration.EndOfTurn), false); - Target target = new TargetCreaturePermanent(filter); + Target target = new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE); ability.addTarget(target); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/l/LoathsomeCurator.java b/Mage.Sets/src/mage/cards/l/LoathsomeCurator.java index a8c5485f967..a21f1099a92 100644 --- a/Mage.Sets/src/mage/cards/l/LoathsomeCurator.java +++ b/Mage.Sets/src/mage/cards/l/LoathsomeCurator.java @@ -44,7 +44,7 @@ public final class LoathsomeCurator extends CardImpl { this.addAbility(new ExploitAbility()); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // When Loathsome Curator exploits a creature, destroy target creature you don't control with mana value 3 or less. Ability ability = new ExploitCreatureTriggeredAbility(new DestroyTargetEffect(), false); diff --git a/Mage.Sets/src/mage/cards/l/LolthSpiderQueen.java b/Mage.Sets/src/mage/cards/l/LolthSpiderQueen.java index 986ee74bd55..ed17d8a2a27 100644 --- a/Mage.Sets/src/mage/cards/l/LolthSpiderQueen.java +++ b/Mage.Sets/src/mage/cards/l/LolthSpiderQueen.java @@ -3,7 +3,6 @@ package mage.cards.l; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.common.DiesCreatureTriggeredAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.GetEmblemEffect; @@ -31,7 +30,7 @@ public final class LolthSpiderQueen extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.LOLTH); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // Whenever a creature you control dies, put a loyalty counter on Lolth, Spider Queen. this.addAbility(new DiesCreatureTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/l/LoneRider.java b/Mage.Sets/src/mage/cards/l/LoneRider.java index 8dc9205f81a..b87c872bc69 100644 --- a/Mage.Sets/src/mage/cards/l/LoneRider.java +++ b/Mage.Sets/src/mage/cards/l/LoneRider.java @@ -37,7 +37,6 @@ public final class LoneRider extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); - this.transformable = true; this.secondSideCardClazz = mage.cards.i.ItThatRidesAsOne.class; // First strike @@ -50,7 +49,7 @@ public final class LoneRider extends CardImpl { this.addAbility(new TransformAbility()); this.addAbility(new ConditionalInterveningIfTriggeredAbility( new BeginningOfEndStepTriggeredAbility( - new TransformSourceEffect(true), TargetController.NEXT, false + new TransformSourceEffect(), TargetController.NEXT, false ), condition, ruleText ).addHint(hint), new PlayerGainedLifeWatcher()); } diff --git a/Mage.Sets/src/mage/cards/l/LoneWolfOfTheNatterknolls.java b/Mage.Sets/src/mage/cards/l/LoneWolfOfTheNatterknolls.java index 1f6020f6f6e..7dd9ba8be11 100644 --- a/Mage.Sets/src/mage/cards/l/LoneWolfOfTheNatterknolls.java +++ b/Mage.Sets/src/mage/cards/l/LoneWolfOfTheNatterknolls.java @@ -11,7 +11,6 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.filter.FilterSpell; import mage.filter.StaticFilters; import java.util.UUID; @@ -29,7 +28,6 @@ public final class LoneWolfOfTheNatterknolls extends CardImpl { this.color.setGreen(true); this.nightCard = true; - this.transformable = true; // Whenever an opponent cast a spell during your turn, draw two cards. this.addAbility(new ConditionalTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/l/LongshotSquad.java b/Mage.Sets/src/mage/cards/l/LongshotSquad.java index 493534bbaa2..9c616c98e97 100644 --- a/Mage.Sets/src/mage/cards/l/LongshotSquad.java +++ b/Mage.Sets/src/mage/cards/l/LongshotSquad.java @@ -13,10 +13,8 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.TargetController; import mage.constants.Zone; -import mage.counters.CounterType; -import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; /** * @@ -24,14 +22,6 @@ import mage.filter.FilterPermanent; */ public final class LongshotSquad 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 LongshotSquad(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{G}"); this.subtype.add(SubType.DOG); @@ -43,9 +33,14 @@ public final class LongshotSquad extends CardImpl { // Outlast 1G this.addAbility(new OutlastAbility(new ManaCostsImpl("{1}{G}"))); // Each creature you control with a +1/+1 counter on it has reach. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(ReachAbility.getInstance(), Duration.WhileOnBattlefield, filter, - "Each creature you control with a +1/+1 counter on it has reach"))); - + this.addAbility(new SimpleStaticAbility( + Zone.BATTLEFIELD, + new GainAbilityAllEffect( + ReachAbility.getInstance(), + Duration.WhileOnBattlefield, + StaticFilters.FILTER_EACH_CONTROLLED_CREATURE_P1P1) + ) + ); } private LongshotSquad(final LongshotSquad card) { diff --git a/Mage.Sets/src/mage/cards/l/LordOfLineage.java b/Mage.Sets/src/mage/cards/l/LordOfLineage.java index b20e7d61509..1c8d6079993 100644 --- a/Mage.Sets/src/mage/cards/l/LordOfLineage.java +++ b/Mage.Sets/src/mage/cards/l/LordOfLineage.java @@ -40,7 +40,6 @@ public final class LordOfLineage extends CardImpl { // this card is the second face of double-faced card Bloodline Keeper this.nightCard = true; - this.transformable = true; this.addAbility(FlyingAbility.getInstance()); // Other Vampire creatures you control get +2/+2. diff --git a/Mage.Sets/src/mage/cards/l/LordOfTheUlvenwald.java b/Mage.Sets/src/mage/cards/l/LordOfTheUlvenwald.java index 05f56567b31..7fed8fc3c0f 100644 --- a/Mage.Sets/src/mage/cards/l/LordOfTheUlvenwald.java +++ b/Mage.Sets/src/mage/cards/l/LordOfTheUlvenwald.java @@ -45,7 +45,6 @@ public final class LordOfTheUlvenwald extends CardImpl { this.color.setRed(true); this.color.setGreen(true); this.nightCard = true; - this.transformable = true; // Other Wolves and Werewolves you control get +1/+1. this.addAbility(new SimpleStaticAbility(new BoostControlledEffect( diff --git a/Mage.Sets/src/mage/cards/l/LordWindgrace.java b/Mage.Sets/src/mage/cards/l/LordWindgrace.java index e57753a4106..b2308200308 100644 --- a/Mage.Sets/src/mage/cards/l/LordWindgrace.java +++ b/Mage.Sets/src/mage/cards/l/LordWindgrace.java @@ -4,7 +4,6 @@ import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.common.CanBeYourCommanderAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.DestroyTargetEffect; @@ -42,7 +41,7 @@ public final class LordWindgrace extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.WINDGRACE); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +2: Discard a card, then draw a card. If a land card is discarded this way, draw an additional card. this.addAbility(new LoyaltyAbility(new LordWindgraceEffect(), 2)); diff --git a/Mage.Sets/src/mage/cards/l/LoreholdCampus.java b/Mage.Sets/src/mage/cards/l/LoreholdCampus.java index b0630d256c5..9fb0004fd52 100644 --- a/Mage.Sets/src/mage/cards/l/LoreholdCampus.java +++ b/Mage.Sets/src/mage/cards/l/LoreholdCampus.java @@ -30,7 +30,7 @@ public final class LoreholdCampus extends CardImpl { this.addAbility(new WhiteManaAbility()); // {4}, {T}: Scry 1. - Ability ability = new SimpleActivatedAbility(new ScryEffect(1), new GenericManaCost(4)); + Ability ability = new SimpleActivatedAbility(new ScryEffect(1, false), new GenericManaCost(4)); ability.addCost(new TapSourceCost()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/l/LoseCalm.java b/Mage.Sets/src/mage/cards/l/LoseCalm.java index a007ceb5337..6b91f9006b2 100644 --- a/Mage.Sets/src/mage/cards/l/LoseCalm.java +++ b/Mage.Sets/src/mage/cards/l/LoseCalm.java @@ -31,7 +31,8 @@ public final class LoseCalm extends CardImpl { this.getSpellAbility().addEffect(new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn, "It gains haste")); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); effect = new GainAbilityTargetEffect(new MenaceAbility(), Duration.EndOfTurn); - effect.setText("and menace until end of turn"); + effect.setText("and menace until end of turn." + + "(A creature with menace can't be blocked except by two or more creatures.)"); this.getSpellAbility().addEffect(effect); } diff --git a/Mage.Sets/src/mage/cards/l/LostOrderOfJarkeld.java b/Mage.Sets/src/mage/cards/l/LostOrderOfJarkeld.java index b393e26c171..237a5619406 100644 --- a/Mage.Sets/src/mage/cards/l/LostOrderOfJarkeld.java +++ b/Mage.Sets/src/mage/cards/l/LostOrderOfJarkeld.java @@ -1,30 +1,29 @@ package mage.cards.l; import mage.MageInt; +import mage.abilities.Ability; import mage.abilities.common.AsEntersBattlefieldAbility; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.AdditiveDynamicValue; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.Effect; import mage.abilities.effects.common.ChooseOpponentEffect; import mage.abilities.effects.common.continuous.SetPowerToughnessSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.dynamicvalue.AdditiveDynamicValue; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.dynamicvalue.common.StaticValue; -import mage.abilities.effects.Effect; import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.players.Player; +import java.util.UUID; + /** * @author TheElk801 */ public final class LostOrderOfJarkeld extends CardImpl { - protected FilterCreaturePermanent filter = new FilterCreaturePermanent(); - public LostOrderOfJarkeld(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{W}"); @@ -39,8 +38,9 @@ public final class LostOrderOfJarkeld extends CardImpl { // Lost Order of Jarkeld's power and toughness are each equal to 1 plus the number of creatures the chosen player controls. this.addAbility(new SimpleStaticAbility( Zone.ALL, - new SetPowerToughnessSourceEffect( - new AdditiveDynamicValue(new CreaturesControlledByChosenPlayer(), StaticValue.get(1)), Duration.EndOfGame) + new SetPowerToughnessSourceEffect(new AdditiveDynamicValue( + CreaturesControlledByChosenPlayer.instance, StaticValue.get(1) + ), Duration.EndOfGame) )); } @@ -54,7 +54,8 @@ public final class LostOrderOfJarkeld extends CardImpl { } } -class CreaturesControlledByChosenPlayer implements DynamicValue { +enum CreaturesControlledByChosenPlayer implements DynamicValue { + instance; @Override public int calculate(Game game, Ability sourceAbility, Effect effect) { @@ -70,7 +71,7 @@ class CreaturesControlledByChosenPlayer implements DynamicValue { @Override public CreaturesControlledByChosenPlayer copy() { - return new CreaturesControlledByChosenPlayer(); + return this; } @Override diff --git a/Mage.Sets/src/mage/cards/l/LoxodonGatekeeper.java b/Mage.Sets/src/mage/cards/l/LoxodonGatekeeper.java index 33c57eb93c1..4c583bd2618 100644 --- a/Mage.Sets/src/mage/cards/l/LoxodonGatekeeper.java +++ b/Mage.Sets/src/mage/cards/l/LoxodonGatekeeper.java @@ -1,31 +1,38 @@ package mage.cards.l; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.PermanentsEnterBattlefieldTappedEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.EntersTheBattlefieldEvent; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicates; + +import java.util.UUID; /** - * * @author fireshoes */ public final class LoxodonGatekeeper extends CardImpl { + private static final FilterPermanent filter + = new FilterPermanent("artifacts, creatures, and lands your opponents control"); + + static { + filter.add(TargetController.OPPONENT.getControllerPredicate()); + filter.add(Predicates.or( + CardType.ARTIFACT.getPredicate(), + CardType.CREATURE.getPredicate(), + CardType.LAND.getPredicate() + )); + } + public LoxodonGatekeeper(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{W}"); this.subtype.add(SubType.ELEPHANT); this.subtype.add(SubType.SOLDIER); @@ -33,8 +40,7 @@ public final class LoxodonGatekeeper extends CardImpl { this.toughness = new MageInt(3); // Artifacts, creatures, and lands your opponents control enter the battlefield tapped. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new LoxodonGatekeeperTapEffect())); - + this.addAbility(new SimpleStaticAbility(new PermanentsEnterBattlefieldTappedEffect(filter))); } private LoxodonGatekeeper(final LoxodonGatekeeper card) { @@ -46,48 +52,3 @@ public final class LoxodonGatekeeper extends CardImpl { return new LoxodonGatekeeper(this); } } - -class LoxodonGatekeeperTapEffect extends ReplacementEffectImpl { - - LoxodonGatekeeperTapEffect() { - super(Duration.WhileOnBattlefield, Outcome.Tap); - staticText = "Artifacts, creatures, and lands your opponents control enter the battlefield tapped"; - } - - LoxodonGatekeeperTapEffect(final LoxodonGatekeeperTapEffect effect) { - super(effect); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Permanent target = ((EntersTheBattlefieldEvent) event).getTarget(); - if (target != null) { - target.setTapped(true); - } - return false; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (game.getOpponents(source.getControllerId()).contains(event.getPlayerId())) { - Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget(); - if (permanent != null - && (permanent.isCreature(game) - || permanent.isLand(game) - || permanent.isArtifact(game))) { - return true; - } - } - return false; - } - - @Override - public LoxodonGatekeeperTapEffect copy() { - return new LoxodonGatekeeperTapEffect(this); - } -} diff --git a/Mage.Sets/src/mage/cards/l/LoyalApprentice.java b/Mage.Sets/src/mage/cards/l/LoyalApprentice.java index eb21b5b9f44..19a9ef4f855 100644 --- a/Mage.Sets/src/mage/cards/l/LoyalApprentice.java +++ b/Mage.Sets/src/mage/cards/l/LoyalApprentice.java @@ -6,7 +6,6 @@ import mage.abilities.common.BeginningOfCombatTriggeredAbility; import mage.abilities.condition.common.CommanderInPlayCondition; import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; @@ -75,8 +74,6 @@ class LoyalApprenticeEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - CreateTokenEffect effect = new CreateTokenEffect(new ThopterColorlessToken()); - effect.apply(game, source); Token token = new ThopterColorlessToken(); token.putOntoBattlefield(1, game, source, source.getControllerId()); game.addEffect(new GainAbilityTargetEffect( diff --git a/Mage.Sets/src/mage/cards/l/LoyalCathar.java b/Mage.Sets/src/mage/cards/l/LoyalCathar.java index 656073481fb..eaa59129349 100644 --- a/Mage.Sets/src/mage/cards/l/LoyalCathar.java +++ b/Mage.Sets/src/mage/cards/l/LoyalCathar.java @@ -32,7 +32,6 @@ public final class LoyalCathar extends CardImpl { this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.SOLDIER); - this.transformable = true; this.secondSideCardClazz = mage.cards.u.UnhallowedCathar.class; this.power = new MageInt(2); diff --git a/Mage.Sets/src/mage/cards/l/LoyalSubordinate.java b/Mage.Sets/src/mage/cards/l/LoyalSubordinate.java index e6440424b0b..3dd69fcbe09 100644 --- a/Mage.Sets/src/mage/cards/l/LoyalSubordinate.java +++ b/Mage.Sets/src/mage/cards/l/LoyalSubordinate.java @@ -27,7 +27,7 @@ public final class LoyalSubordinate extends CardImpl { this.toughness = new MageInt(1); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // Lieutenant — At the beginning of combat on your turn, if you control your commander, each opponent loses 3 life. this.addAbility(new ConditionalTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/l/LuckyOffering.java b/Mage.Sets/src/mage/cards/l/LuckyOffering.java new file mode 100644 index 00000000000..6b2c78862dc --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LuckyOffering.java @@ -0,0 +1,45 @@ +package mage.cards.l; + +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterArtifactPermanent; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LuckyOffering extends CardImpl { + + private static final FilterPermanent filter + = new FilterArtifactPermanent("artifact with mana value 3 or less"); + + static { + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); + } + + public LuckyOffering(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{W}"); + + // Destroy target artifact with mana value 3 or less. You gain 3 life. + this.getSpellAbility().addEffect(new DestroyTargetEffect()); + this.getSpellAbility().addEffect(new GainLifeEffect(3)); + this.getSpellAbility().addTarget(new TargetPermanent(filter)); + } + + private LuckyOffering(final LuckyOffering card) { + super(card); + } + + @Override + public LuckyOffering copy() { + return new LuckyOffering(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LudevicNecrogenius.java b/Mage.Sets/src/mage/cards/l/LudevicNecrogenius.java new file mode 100644 index 00000000000..d7dab640a37 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LudevicNecrogenius.java @@ -0,0 +1,61 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.common.EntersBattlefieldOrAttacksSourceTriggeredAbility; +import mage.abilities.costs.common.ExileXFromYourGraveCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.costs.mana.VariableManaCost; +import mage.abilities.effects.common.InfoEffect; +import mage.abilities.effects.common.MillCardsControllerEffect; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.keyword.TransformAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.StaticFilters; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LudevicNecrogenius extends CardImpl { + + public LudevicNecrogenius(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{U}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + this.secondSideCardClazz = mage.cards.o.OlagLudevicsHubris.class; + + // Whenever Ludevic, Necrogenius enters the battlefield or attacks, mill a card. + this.addAbility(new EntersBattlefieldOrAttacksSourceTriggeredAbility(new MillCardsControllerEffect(1))); + + // {X}{U}{U}{B}{B}, Exile X creature cards from your graveyard: Transform Ludevic, Necrogenius. X can't be zero. Activate only as a sorcery. + this.addAbility(new TransformAbility()); + Ability ability = new ActivateAsSorceryActivatedAbility( + new TransformSourceEffect(), new ManaCostsImpl<>("{X}{U}{U}{B}{B}") + ); + ability.addEffect(new InfoEffect("X can't be 0")); + ability.addCost(new ExileXFromYourGraveCost(StaticFilters.FILTER_CARD_CREATURES_YOUR_GRAVEYARD)); + CardUtil.castStream(ability.getCosts().stream(), VariableManaCost.class).forEach(cost -> cost.setMinX(1)); + this.addAbility(ability); + } + + private LudevicNecrogenius(final LudevicNecrogenius card) { + super(card); + } + + @Override + public LudevicNecrogenius copy() { + return new LudevicNecrogenius(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LudevicsAbomination.java b/Mage.Sets/src/mage/cards/l/LudevicsAbomination.java index 4d9f5c33702..dcfe9517ef7 100644 --- a/Mage.Sets/src/mage/cards/l/LudevicsAbomination.java +++ b/Mage.Sets/src/mage/cards/l/LudevicsAbomination.java @@ -23,7 +23,6 @@ public final class LudevicsAbomination extends CardImpl { // this card is the second face of double-faced card this.nightCard = true; - this.transformable = true; this.power = new MageInt(13); this.toughness = new MageInt(13); diff --git a/Mage.Sets/src/mage/cards/l/LudevicsTestSubject.java b/Mage.Sets/src/mage/cards/l/LudevicsTestSubject.java index 6733f34f0a1..509e225b80a 100644 --- a/Mage.Sets/src/mage/cards/l/LudevicsTestSubject.java +++ b/Mage.Sets/src/mage/cards/l/LudevicsTestSubject.java @@ -34,7 +34,6 @@ public final class LudevicsTestSubject extends CardImpl { this.power = new MageInt(0); this.toughness = new MageInt(3); - this.transformable = true; this.secondSideCardClazz = LudevicsAbomination.class; this.addAbility(DefenderAbility.getInstance()); @@ -72,7 +71,7 @@ class LudevicsTestSubjectEffect extends OneShotEffect { if (p != null) { if (p.getCounters(game).getCount(CounterType.HATCHLING) >= 5) { p.removeCounters(CounterType.HATCHLING.getName(), p.getCounters(game).getCount(CounterType.HATCHLING), source, game); - TransformSourceEffect effect = new TransformSourceEffect(true); + TransformSourceEffect effect = new TransformSourceEffect(); return effect.apply(game, source); } } diff --git a/Mage.Sets/src/mage/cards/l/LukeSkywalkerTheLastJedi.java b/Mage.Sets/src/mage/cards/l/LukeSkywalkerTheLastJedi.java index 108eaadae30..610f5c67943 100644 --- a/Mage.Sets/src/mage/cards/l/LukeSkywalkerTheLastJedi.java +++ b/Mage.Sets/src/mage/cards/l/LukeSkywalkerTheLastJedi.java @@ -4,7 +4,6 @@ import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.common.ExileSourceEffect; import mage.abilities.effects.common.GetEmblemEffect; import mage.abilities.effects.common.PutOnLibraryTargetEffect; @@ -38,7 +37,7 @@ public final class LukeSkywalkerTheLastJedi extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.LUKE); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +1: Put two +1/+1 counters on up to one target creature. Ability ability1 = new LoyaltyAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance(2)), 2); diff --git a/Mage.Sets/src/mage/cards/l/LukkaCoppercoatOutcast.java b/Mage.Sets/src/mage/cards/l/LukkaCoppercoatOutcast.java index 0cfdb483c9b..b38b7b3e108 100644 --- a/Mage.Sets/src/mage/cards/l/LukkaCoppercoatOutcast.java +++ b/Mage.Sets/src/mage/cards/l/LukkaCoppercoatOutcast.java @@ -2,7 +2,6 @@ package mage.cards.l; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; @@ -33,7 +32,7 @@ public final class LukkaCoppercoatOutcast extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.LUKKA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +1: Exile the top three cards of your library. Creature cards exiled this way gain "You may cast this card from exile as long as you control a Lukka planeswalker." this.addAbility(new LoyaltyAbility(new LukkaCoppercoatOutcastExileEffect(), 1)); diff --git a/Mage.Sets/src/mage/cards/l/LuminousBroodmoth.java b/Mage.Sets/src/mage/cards/l/LuminousBroodmoth.java index 224178c0dcb..eadb7463755 100644 --- a/Mage.Sets/src/mage/cards/l/LuminousBroodmoth.java +++ b/Mage.Sets/src/mage/cards/l/LuminousBroodmoth.java @@ -74,7 +74,8 @@ class LuminousBroodmothTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - if (zEvent.getTarget() == null || zEvent.getTarget().getId().equals(this.getSourceId())) { + if (zEvent.getTarget() == null + || zEvent.getTarget().getId().equals(this.getSourceId())) { return false; } Permanent permanent = game.getPermanentOrLKIBattlefield(zEvent.getTarget().getId()); @@ -92,8 +93,8 @@ class LuminousBroodmothTriggeredAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "Whenever a creature you control without flying dies, " + - "return it to the battlefield under its owner's control with a flying counter on it."; + return "Whenever a creature you control without flying dies, " + + "return it to the battlefield under its owner's control with a flying counter on it."; } } diff --git a/Mage.Sets/src/mage/cards/l/LuminousPhantom.java b/Mage.Sets/src/mage/cards/l/LuminousPhantom.java index 577fd9493f3..052b41b86de 100644 --- a/Mage.Sets/src/mage/cards/l/LuminousPhantom.java +++ b/Mage.Sets/src/mage/cards/l/LuminousPhantom.java @@ -36,7 +36,6 @@ public final class LuminousPhantom extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); this.color.setWhite(true); - this.transformable = true; this.nightCard = true; // Flying diff --git a/Mage.Sets/src/mage/cards/l/LunarRejection.java b/Mage.Sets/src/mage/cards/l/LunarRejection.java new file mode 100644 index 00000000000..3ebe936d36d --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LunarRejection.java @@ -0,0 +1,59 @@ +package mage.cards.l; + +import mage.abilities.Ability; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.abilities.keyword.CleaveAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LunarRejection extends CardImpl { + + private static final FilterPermanent filter = new FilterCreaturePermanent("Wolf or Werewolf creature"); + + static { + filter.add(Predicates.or( + SubType.WEREWOLF.getPredicate(), + SubType.WOLF.getPredicate() + )); + } + + public LunarRejection(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}"); + + // Cleave {3}{U} + Ability ability = new CleaveAbility(this, new ReturnToHandTargetEffect(), "{3}{U}"); + ability.addEffect(new DrawCardSourceControllerEffect(1)); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + + // Return target [Wolf or Werewolf] creature to its owner's hand. + this.getSpellAbility().addEffect(new ReturnToHandTargetEffect() + .setText("return target [Wolf or Werewolf] creature to its owner's hand")); + this.getSpellAbility().addTarget(new TargetPermanent(filter)); + + // Draw a card. + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("
")); + } + + private LunarRejection(final LunarRejection card) { + super(card); + } + + @Override + public LunarRejection copy() { + return new LunarRejection(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LunarchInquisitors.java b/Mage.Sets/src/mage/cards/l/LunarchInquisitors.java index e0f7a3d5552..3385b5a8e6d 100644 --- a/Mage.Sets/src/mage/cards/l/LunarchInquisitors.java +++ b/Mage.Sets/src/mage/cards/l/LunarchInquisitors.java @@ -1,36 +1,35 @@ - package mage.cards.l; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.TransformIntoSourceTriggeredAbility; import mage.abilities.common.delayed.OnLeaveReturnExiledToBattlefieldAbility; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; -import mage.abilities.effects.common.ExileTargetEffect; +import mage.abilities.effects.common.ExileUntilSourceLeavesEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.target.common.TargetCreaturePermanent; -import mage.util.CardUtil; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.target.TargetPermanent; + +import java.util.UUID; /** - * * @author fireshoes */ public final class LunarchInquisitors extends CardImpl { - private static final String rule = "Whenever this creature transforms into Lunarch Inquisitors, you may exile another target creature until Lunarch Inquisitors leaves the battlefield"; + private static final FilterPermanent filter = new FilterCreaturePermanent("another target creature"); + + static { + filter.add(AnotherPredicate.instance); + } public LunarchInquisitors(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},""); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.CLERIC); this.power = new MageInt(4); @@ -41,8 +40,9 @@ public final class LunarchInquisitors extends CardImpl { this.nightCard = true; // When this creature transforms into Lunarch Inquisitors, you may exile another target creature until Lunarch Inquisitors leaves the battlefield. - Ability ability = new LunarchInquisitorsAbility(); - ability.addTarget(new TargetCreaturePermanent()); + Ability ability = new TransformIntoSourceTriggeredAbility(new ExileUntilSourceLeavesEffect("") + .setText("exile another target creature until {this} leaves the battlefield"), true); + ability.addTarget(new TargetPermanent(filter)); ability.addEffect(new CreateDelayedTriggeredAbilityEffect(new OnLeaveReturnExiledToBattlefieldAbility())); this.addAbility(ability); } @@ -56,70 +56,3 @@ public final class LunarchInquisitors extends CardImpl { return new LunarchInquisitors(this); } } - -class LunarchInquisitorsAbility extends TriggeredAbilityImpl { - - public LunarchInquisitorsAbility() { - super(Zone.BATTLEFIELD, new LunarchInquisitorsExileEffect(), true); - // Rule only shown on the night side - this.setRuleVisible(false); - } - - public LunarchInquisitorsAbility(final LunarchInquisitorsAbility ability) { - super(ability); - } - - @Override - public LunarchInquisitorsAbility copy() { - return new LunarchInquisitorsAbility(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 "Whenever this creature transforms into Lunarch Inquisitors, you may exile another target creature until Lunarch Inquisitors leaves the battlefield."; - } -} - -class LunarchInquisitorsExileEffect extends OneShotEffect { - - public LunarchInquisitorsExileEffect() { - super(Outcome.Benefit); - this.staticText = "exile target creature until {this} leaves the battlefield"; - } - - public LunarchInquisitorsExileEffect(final LunarchInquisitorsExileEffect effect) { - super(effect); - } - - @Override - public LunarchInquisitorsExileEffect copy() { - return new LunarchInquisitorsExileEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getSourceId()); - // If Lunarch Inquisitors leaves the battlefield before its triggered ability resolves, - // the target won't be exiled. - if (permanent != null) { - return new ExileTargetEffect(CardUtil.getCardExileZoneId(game, source), permanent.getIdName()).apply(game, source); - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/l/LunarchVeteran.java b/Mage.Sets/src/mage/cards/l/LunarchVeteran.java index 61596d263f3..439bef2eeaf 100644 --- a/Mage.Sets/src/mage/cards/l/LunarchVeteran.java +++ b/Mage.Sets/src/mage/cards/l/LunarchVeteran.java @@ -5,7 +5,6 @@ import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.keyword.DisturbAbility; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -26,7 +25,6 @@ public final class LunarchVeteran extends CardImpl { this.subtype.add(SubType.CLERIC); this.power = new MageInt(1); this.toughness = new MageInt(1); - this.transformable = true; this.secondSideCardClazz = mage.cards.l.LuminousPhantom.class; // Whenever another creature enters the battlefield under your control, you gain 1 life. @@ -35,8 +33,7 @@ public final class LunarchVeteran extends CardImpl { )); // Disturb {1}{W} - this.addAbility(new TransformAbility()); - this.addAbility(new DisturbAbility(new ManaCostsImpl<>("{1}{W}"))); + this.addAbility(new DisturbAbility(this, "{1}{W}")); } private LunarchVeteran(final LunarchVeteran card) { diff --git a/Mage.Sets/src/mage/cards/l/LunkErrant.java b/Mage.Sets/src/mage/cards/l/LunkErrant.java index 6083a138ef3..5f4a9a47cda 100644 --- a/Mage.Sets/src/mage/cards/l/LunkErrant.java +++ b/Mage.Sets/src/mage/cards/l/LunkErrant.java @@ -4,7 +4,7 @@ package mage.cards.l; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.AttacksAloneTriggeredAbility; +import mage.abilities.common.AttacksAloneSourceTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; @@ -31,7 +31,7 @@ public final class LunkErrant extends CardImpl { // Whenever Lunk Errant attacks alone, it gets +1/+1 and gains trample until end of turn. Effect effect = new BoostSourceEffect(1, 1, Duration.EndOfTurn); effect.setText("it gets +1/+1"); - Ability ability = new AttacksAloneTriggeredAbility(effect); + Ability ability = new AttacksAloneSourceTriggeredAbility(effect); effect = new GainAbilitySourceEffect(TrampleAbility.getInstance(), Duration.EndOfTurn); effect.setText("and gains trample until end of turn"); ability.addEffect(effect); diff --git a/Mage.Sets/src/mage/cards/l/LupinePrototype.java b/Mage.Sets/src/mage/cards/l/LupinePrototype.java index 093ac00fe7a..8e5a2c95a27 100644 --- a/Mage.Sets/src/mage/cards/l/LupinePrototype.java +++ b/Mage.Sets/src/mage/cards/l/LupinePrototype.java @@ -3,17 +3,17 @@ package mage.cards.l; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.RestrictionEffect; +import mage.abilities.condition.Condition; +import mage.abilities.effects.common.combat.CantAttackBlockUnlessConditionSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.Zone; import mage.game.Game; -import mage.game.permanent.Permanent; import mage.players.Player; +import java.util.Objects; +import java.util.Set; import java.util.UUID; /** @@ -29,7 +29,9 @@ public final class LupinePrototype extends CardImpl { this.toughness = new MageInt(5); // Lupine Prototype can't attack or block unless a player has no cards in hand. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new LupinePrototypeEffect())); + this.addAbility(new SimpleStaticAbility( + new CantAttackBlockUnlessConditionSourceEffect(LupinePrototypeCondition.instance) + )); } private LupinePrototype(final LupinePrototype card) { @@ -42,43 +44,23 @@ public final class LupinePrototype extends CardImpl { } } -class LupinePrototypeEffect extends RestrictionEffect { +enum LupinePrototypeCondition implements Condition { + instance; - public LupinePrototypeEffect() { - super(Duration.WhileOnBattlefield); - staticText = "{this} can't attack or block unless a player has no cards in hand"; - } - - public LupinePrototypeEffect(final LupinePrototypeEffect effect) { - super(effect); + @Override + public boolean apply(Game game, Ability source) { + return game + .getState() + .getPlayersInRange(source.getControllerId(), game) + .stream() + .map(game::getPlayer) + .filter(Objects::nonNull) + .map(Player::getHand) + .anyMatch(Set::isEmpty); } @Override - public boolean applies(Permanent permanent, Ability source, Game game) { - if (permanent.getId().equals(source.getSourceId())) { - for (Player player : game.getPlayers().values()) { - if (player != null && player.getHand().isEmpty()) { - return false; - } - } - return true; - } - // don't apply for all other creatures! - return false; + public String toString() { + return "a player has no cards in hand"; } - - @Override - public boolean canBlock(Permanent attacker, Permanent blocker, Ability source, Game game, boolean canUseChooseDialogs) { - return false; - } - - @Override - public boolean canAttack(Game game, boolean canUseChooseDialogs) { - return false; - } - - @Override - public LupinePrototypeEffect copy() { - return new LupinePrototypeEffect(this); - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/l/LurkingChupacabra.java b/Mage.Sets/src/mage/cards/l/LurkingChupacabra.java index 03853aa72db..3e993717b3a 100644 --- a/Mage.Sets/src/mage/cards/l/LurkingChupacabra.java +++ b/Mage.Sets/src/mage/cards/l/LurkingChupacabra.java @@ -11,8 +11,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; /** @@ -21,12 +20,6 @@ import mage.target.common.TargetCreaturePermanent; */ public final class LurkingChupacabra extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public LurkingChupacabra(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); @@ -37,7 +30,7 @@ public final class LurkingChupacabra extends CardImpl { // Whenever a creature you control explores, target creature an opponent controls gets -2/-2 until end of turn Ability ability = new CreatureExploresTriggeredAbility(new BoostTargetEffect(-2, -2, Duration.EndOfTurn)); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/l/LurkingSkirge.java b/Mage.Sets/src/mage/cards/l/LurkingSkirge.java index 9c8e474aa5a..26df79c78af 100644 --- a/Mage.Sets/src/mage/cards/l/LurkingSkirge.java +++ b/Mage.Sets/src/mage/cards/l/LurkingSkirge.java @@ -35,7 +35,7 @@ public final class LurkingSkirge extends CardImpl { // When a creature is put into an opponent's graveyard from the battlefield, if Lurking Skirge is an enchantment, Lurking Skirge becomes a 3/2 Imp creature with flying. TriggeredAbility ability = new PutIntoGraveFromBattlefieldAllTriggeredAbility(new BecomesCreatureSourceEffect(new LurkingSkirgeToken(), "", Duration.WhileOnBattlefield, true, false), false, filter, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_ENCHANTMENT_PERMANENT), + this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_PERMANENT_ENCHANTMENT), "When a creature is put into an opponent's graveyard from the battlefield, if {this} is an enchantment, {this} becomes a 3/2 Phyrexian Imp creature with flying.")); } diff --git a/Mage.Sets/src/mage/cards/l/LyndeCheerfulTormentor.java b/Mage.Sets/src/mage/cards/l/LyndeCheerfulTormentor.java new file mode 100644 index 00000000000..ea3908500d0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LyndeCheerfulTormentor.java @@ -0,0 +1,202 @@ +package mage.cards.l; + +import java.util.UUID; +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.cards.Card; +import mage.constants.*; +import mage.abilities.keyword.DeathtouchAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.filter.FilterPermanent; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.common.TargetOpponent; + +/** + * + * @author weirddan455 + */ +public final class LyndeCheerfulTormentor extends CardImpl { + + public LyndeCheerfulTormentor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{B}{R}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARLOCK); + this.power = new MageInt(2); + this.toughness = new MageInt(4); + + // Deathtouch + this.addAbility(DeathtouchAbility.getInstance()); + + // Whenever a Curse is put into your graveyard from the battlefield, return it to the battlefield attached to you at the beginning of the next end step. + this.addAbility(new LyndeCheerfulTormentorCurseDiesTriggeredAbility()); + + // At the beginning of your upkeep, you may attach a Curse attached to you to one of your opponents. If you do, draw two cards. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new LyndeCheerfulTormentorAttachCurseEffect(), TargetController.YOU, true)); + } + + private LyndeCheerfulTormentor(final LyndeCheerfulTormentor card) { + super(card); + } + + @Override + public LyndeCheerfulTormentor copy() { + return new LyndeCheerfulTormentor(this); + } +} + +class LyndeCheerfulTormentorCurseDiesTriggeredAbility extends TriggeredAbilityImpl { + + public LyndeCheerfulTormentorCurseDiesTriggeredAbility() { + super(Zone.BATTLEFIELD, new CreateDelayedTriggeredAbilityEffect( + new AtTheBeginOfNextEndStepDelayedTriggeredAbility( + new LyndeCheerfulTormentorReturnCurseEffect() + ) + )); + } + + private LyndeCheerfulTormentorCurseDiesTriggeredAbility(final LyndeCheerfulTormentorCurseDiesTriggeredAbility ability) { + super(ability); + } + + @Override + public LyndeCheerfulTormentorCurseDiesTriggeredAbility copy() { + return new LyndeCheerfulTormentorCurseDiesTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + if (zEvent.isDiesEvent()) { + Permanent permanent = zEvent.getTarget(); + if (permanent != null && permanent.hasSubtype(SubType.CURSE, game) && permanent.isOwnedBy(controllerId)) { + getEffects().setValue("curse", zEvent.getTargetId()); + return true; + } + } + return false; + } + + @Override + public boolean isInUseableZone(Game game, MageObject source, GameEvent event) { + return TriggeredAbilityImpl.isInUseableZoneDiesTrigger(this, event, game); + } + + @Override + public String getRule() { + return "Whenever a Curse is put into your graveyard from the battlefield, return it to the battlefield attached to you at the beginning of the next end step."; + } +} + +class LyndeCheerfulTormentorReturnCurseEffect extends OneShotEffect { + + public LyndeCheerfulTormentorReturnCurseEffect() { + super(Outcome.Benefit); + staticText = "return it to the battlefield attached to you at the beginning of the next end step"; + } + + private LyndeCheerfulTormentorReturnCurseEffect(final LyndeCheerfulTormentorReturnCurseEffect effect) { + super(effect); + } + + @Override + public LyndeCheerfulTormentorReturnCurseEffect copy() { + return new LyndeCheerfulTormentorReturnCurseEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Card curse = game.getCard((UUID)getValue("curse")); + if (controller != null && curse != null) { + game.getState().setValue("attachTo:" + curse.getId(), controller.getId()); + controller.moveCards(curse, Zone.BATTLEFIELD, source, game); + controller.addAttachment(curse.getId(), source, game); + return true; + } + return false; + } +} + +class LyndeCheerfulTormentorAttachCurseEffect extends OneShotEffect { + + private static final FilterPermanent filter = new FilterPermanent(SubType.CURSE, "Curse attached to you"); + + static { + filter.add(LyndeCheerfulTormentorPredicate.instance); + } + + public LyndeCheerfulTormentorAttachCurseEffect() { + super(Outcome.Benefit); + staticText = "attach a Curse attached to you to one of your opponents. If you do, draw two cards"; + } + + private LyndeCheerfulTormentorAttachCurseEffect(final LyndeCheerfulTormentorAttachCurseEffect effect) { + super(effect); + } + + @Override + public LyndeCheerfulTormentorAttachCurseEffect copy() { + return new LyndeCheerfulTormentorAttachCurseEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + TargetPermanent targetPermanent = new TargetPermanent(filter); + targetPermanent.setNotTarget(true); + if (controller.chooseTarget(outcome, targetPermanent, source, game)) { + Permanent curse = game.getPermanent(targetPermanent.getFirstTarget()); + if (curse != null) { + TargetOpponent targetOpponent = new TargetOpponent(true); + if (controller.chooseTarget(Outcome.Detriment, targetOpponent, source, game)) { + Player opponent = game.getPlayer(targetOpponent.getFirstTarget()); + if (opponent != null) { + controller.removeAttachment(curse, source, game); + opponent.addAttachment(curse.getId(), source, game); + controller.drawCards(2, source, game); + return true; + } + } + } + } + } + return false; + } +} + +enum LyndeCheerfulTormentorPredicate implements ObjectSourcePlayerPredicate { + instance; + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + Permanent permanent = input.getObject(); + return permanent != null && permanent.isAttachedTo(input.getPlayerId()); + } + + @Override + public String toString() { + return "attached to you"; + } +} diff --git a/Mage.Sets/src/mage/cards/m/MaChaoWesternWarrior.java b/Mage.Sets/src/mage/cards/m/MaChaoWesternWarrior.java index 034e9352eac..5dbbaf74463 100644 --- a/Mage.Sets/src/mage/cards/m/MaChaoWesternWarrior.java +++ b/Mage.Sets/src/mage/cards/m/MaChaoWesternWarrior.java @@ -3,7 +3,7 @@ package mage.cards.m; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.AttacksAloneTriggeredAbility; +import mage.abilities.common.AttacksAloneSourceTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.combat.CantBeBlockedSourceEffect; import mage.abilities.keyword.HorsemanshipAbility; @@ -34,7 +34,7 @@ public final class MaChaoWesternWarrior extends CardImpl { // Whenever Ma Chao, Western Warrior attacks alone, it can't be blocked this combat. Effect effect = new CantBeBlockedSourceEffect(Duration.EndOfCombat); effect.setText("it can't be blocked this combat"); - this.addAbility(new AttacksAloneTriggeredAbility(effect)); + this.addAbility(new AttacksAloneSourceTriggeredAbility(effect)); } private MaChaoWesternWarrior(final MaChaoWesternWarrior card) { diff --git a/Mage.Sets/src/mage/cards/m/MacabreWaltz.java b/Mage.Sets/src/mage/cards/m/MacabreWaltz.java index fe0cb2aa6e0..af85cacd153 100644 --- a/Mage.Sets/src/mage/cards/m/MacabreWaltz.java +++ b/Mage.Sets/src/mage/cards/m/MacabreWaltz.java @@ -1,14 +1,11 @@ - package mage.cards.m; - import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; import mage.abilities.effects.common.discard.DiscardControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.FilterCard; -import mage.filter.common.FilterCreatureCard; +import mage.filter.StaticFilters; import mage.target.common.TargetCardInYourGraveyard; import java.util.UUID; @@ -18,15 +15,13 @@ import java.util.UUID; */ public final class MacabreWaltz extends CardImpl { - private static final FilterCard filter = new FilterCreatureCard("creature cards from your graveyard"); - public MacabreWaltz(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}"); // Return up to two target creature cards from your graveyard to your hand, then discard a card. this.getSpellAbility().addEffect(new ReturnFromGraveyardToHandTargetEffect()); - this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, 2, filter)); - this.getSpellAbility().addEffect(new DiscardControllerEffect(1).setText(", then discard a card")); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, 2, StaticFilters.FILTER_CARD_CREATURES_YOUR_GRAVEYARD)); + this.getSpellAbility().addEffect(new DiscardControllerEffect(1).concatBy(", then")); } private MacabreWaltz(final MacabreWaltz card) { diff --git a/Mage.Sets/src/mage/cards/m/MadcapSkills.java b/Mage.Sets/src/mage/cards/m/MadcapSkills.java index f50194f7f84..c5cdde200a0 100644 --- a/Mage.Sets/src/mage/cards/m/MadcapSkills.java +++ b/Mage.Sets/src/mage/cards/m/MadcapSkills.java @@ -39,7 +39,7 @@ public final class MadcapSkills extends CardImpl { // Enchanted creature gets +3/+0 and and has menace. (It can't be blocked except by two or more creatures.) this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(3, 0))); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(new MenaceAbility(), AttachmentType.AURA))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(new MenaceAbility(false), AttachmentType.AURA))); } private MadcapSkills(final MadcapSkills card) { diff --git a/Mage.Sets/src/mage/cards/m/MaddeningImp.java b/Mage.Sets/src/mage/cards/m/MaddeningImp.java index 59d88458f8e..2fe231c94ee 100644 --- a/Mage.Sets/src/mage/cards/m/MaddeningImp.java +++ b/Mage.Sets/src/mage/cards/m/MaddeningImp.java @@ -118,7 +118,7 @@ class MaddeningImpCreateDelayedTriggeredAbilityEffect extends OneShotEffect { } } AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility - = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(Zone.ALL, new MaddeningImpDelayedDestroyEffect(activeCreatures), TargetController.ANY, new InvertCondition(TargetAttackedThisTurnCondition.instance)); + = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new MaddeningImpDelayedDestroyEffect(activeCreatures), TargetController.ANY, new InvertCondition(TargetAttackedThisTurnCondition.instance)); delayedAbility.getDuration(); game.addDelayedTriggeredAbility(delayedAbility, source); return true; diff --git a/Mage.Sets/src/mage/cards/m/MaelstromMuse.java b/Mage.Sets/src/mage/cards/m/MaelstromMuse.java index cdb2b600644..fbff34b0679 100644 --- a/Mage.Sets/src/mage/cards/m/MaelstromMuse.java +++ b/Mage.Sets/src/mage/cards/m/MaelstromMuse.java @@ -126,7 +126,7 @@ class MaelstromMuseWatcher extends Watcher { } Spell spell = game.getSpell(event.getSourceId()); if (spell != null && spell.isInstantOrSorcery(game)) { - playerMap.compute(event.getPlayerId(), (u, i) -> i == null ? 1 : Integer.sum(i, 1)); + playerMap.compute(event.getPlayerId(), CardUtil::setOrIncrementValue); } } diff --git a/Mage.Sets/src/mage/cards/m/MageDuel.java b/Mage.Sets/src/mage/cards/m/MageDuel.java index 521ed8d6359..d51a36f166e 100644 --- a/Mage.Sets/src/mage/cards/m/MageDuel.java +++ b/Mage.Sets/src/mage/cards/m/MageDuel.java @@ -37,12 +37,15 @@ public final class MageDuel extends CardImpl { // This spell costs {2} less to cast if you've cast another instant or sorcery spell this turn. this.addAbility(new SimpleStaticAbility( - Zone.ALL, new SpellCostReductionSourceEffect(2, MageDuelCondition.instance).setCanWorksOnStackOnly(true) + Zone.ALL, + new SpellCostReductionSourceEffect(2, MageDuelCondition.instance).setCanWorksOnStackOnly(true) ).setRuleAtTheTop(true), new SpellsCastWatcher()); // Target creature you control gets +1/+2 until end of turn. Then it fights target creature you don't control. this.getSpellAbility().addEffect(new BoostTargetEffect(1, 2)); - this.getSpellAbility().addEffect(new FightTargetsEffect().setText("Then it fights target creature you don't control")); + this.getSpellAbility().addEffect(new FightTargetsEffect().setText( + "Then it fights target creature you don't control. " + + "(Each deals damage equal to its power to the other.)")); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); this.getSpellAbility().addWatcher(new SpellsCastWatcher()); diff --git a/Mage.Sets/src/mage/cards/m/MageSlayer.java b/Mage.Sets/src/mage/cards/m/MageSlayer.java index 61b083e1595..9f416778e54 100644 --- a/Mage.Sets/src/mage/cards/m/MageSlayer.java +++ b/Mage.Sets/src/mage/cards/m/MageSlayer.java @@ -47,7 +47,7 @@ class MageSlayerEffect extends OneShotEffect { public MageSlayerEffect() { super(Outcome.Damage); - staticText = "it deals damage equal to the player or planeswalker it's attacking"; + staticText = "it deals damage equal to its power to the player or planeswalker it's attacking"; } public MageSlayerEffect(final MageSlayerEffect effect) { diff --git a/Mage.Sets/src/mage/cards/m/MagmaOpus.java b/Mage.Sets/src/mage/cards/m/MagmaOpus.java index 2391d6bcd5d..93f7c0d1d52 100644 --- a/Mage.Sets/src/mage/cards/m/MagmaOpus.java +++ b/Mage.Sets/src/mage/cards/m/MagmaOpus.java @@ -35,7 +35,7 @@ public final class MagmaOpus extends CardImpl { this.getSpellAbility().addEffect(new TapTargetEffect("tap two target permanents").setTargetPointer(new SecondTargetPointer())); this.getSpellAbility().addTarget(new TargetPermanent(2, StaticFilters.FILTER_PERMANENTS).withChooseHint("tap")); this.getSpellAbility().addEffect(new CreateTokenEffect(new Elemental44Token())); - this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(2).setText("Draw two cards")); + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(2)); // {U/R}{U/R}, Discard Magma Opus: Create a Treasure token. Ability ability = new SimpleActivatedAbility( diff --git a/Mage.Sets/src/mage/cards/m/MagmaPummeler.java b/Mage.Sets/src/mage/cards/m/MagmaPummeler.java new file mode 100644 index 00000000000..0c53646f66e --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MagmaPummeler.java @@ -0,0 +1,106 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.effects.PreventionEffectImpl; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.EntersBattlefieldWithXCountersEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.common.TargetAnyTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MagmaPummeler extends CardImpl { + + public MagmaPummeler(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{X}{R}{R}"); + + this.subtype.add(SubType.ELEMENTAL); + this.power = new MageInt(0); + this.toughness = new MageInt(0); + + // Magma Pummeler enters the battlefield with X +1/+1 counters on it. + this.addAbility(new EntersBattlefieldAbility(new EntersBattlefieldWithXCountersEffect(CounterType.P1P1.createInstance()))); + + // If damage would be dealt to Magma Pummeler while it has a +1/+1 counter on it, prevent that damage and remove that many +1/+1 counters from it. When one or more counters are removed from Magma Pummeler this way, it deals that much damage to any target. + this.addAbility(new SimpleStaticAbility(new MagmaPummelerEffect())); + } + + private MagmaPummeler(final MagmaPummeler card) { + super(card); + } + + @Override + public MagmaPummeler copy() { + return new MagmaPummeler(this); + } +} + +class MagmaPummelerEffect extends PreventionEffectImpl { + + public MagmaPummelerEffect() { + super(Duration.WhileOnBattlefield, Integer.MAX_VALUE, false, false); + staticText = "If damage would be dealt to {this} while it has a +1/+1 counter on it, " + + "prevent that damage and remove that many +1/+1 counters from it. " + + "When one or more counters are removed from {this} this way, it deals that much damage to any target."; + } + + private MagmaPummelerEffect(final MagmaPummelerEffect effect) { + super(effect); + } + + @Override + public MagmaPummelerEffect copy() { + return new MagmaPummelerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + int damage = event.getAmount(); + preventDamageAction(event, source, game); + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent == null) { + return false; + } + int beforeCounters = permanent.getCounters(game).getCount(CounterType.P1P1); + permanent.removeCounters(CounterType.P1P1.createInstance(damage), source, game); + int countersRemoved = beforeCounters - permanent.getCounters(game).getCount(CounterType.P1P1); + if (countersRemoved > 0) { + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new DamageTargetEffect(countersRemoved), false, + "{this} deals that much damage to any target" + ); + ability.addTarget(new TargetAnyTarget()); + game.fireReflexiveTriggeredAbility(ability, source); + } + return false; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + Permanent permanent = game.getPermanent(event.getTargetId()); + return super.applies(event, source, game) + && permanent != null + && event.getTargetId().equals(source.getSourceId()) + && permanent.getCounters(game).containsKey(CounterType.P1P1); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MagmaSliver.java b/Mage.Sets/src/mage/cards/m/MagmaSliver.java index 682c3140cdc..a281441a773 100644 --- a/Mage.Sets/src/mage/cards/m/MagmaSliver.java +++ b/Mage.Sets/src/mage/cards/m/MagmaSliver.java @@ -39,15 +39,15 @@ public final class MagmaSliver extends CardImpl { // where X is the number of Slivers on the battlefield." Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostTargetEffect(new PermanentsOnBattlefieldCount( - StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS), + StaticFilters.FILTER_PERMANENT_ALL_SLIVERS), StaticValue.get(0), Duration.EndOfTurn, true), new TapSourceCost()); Target target = new TargetCreaturePermanent( new FilterCreaturePermanent(SubType.SLIVER, "Sliver creature")); ability.addTarget(target); Effect effect = new GainAbilityAllEffect(ability, Duration.WhileOnBattlefield, - StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS); - effect.setText("All Slivers have \"{T}: Target Sliver creature gets +X/+0 until end of turn," + StaticFilters.FILTER_PERMANENT_ALL_SLIVERS); + effect.setText("All Slivers have \"{T}: Target Sliver creature gets +X/+0 until end of turn, " + "where X is the number of Slivers on the battlefield.\""); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); diff --git a/Mage.Sets/src/mage/cards/m/MagnigothTreefolk.java b/Mage.Sets/src/mage/cards/m/MagnigothTreefolk.java index 93b0353f422..422bad620d0 100644 --- a/Mage.Sets/src/mage/cards/m/MagnigothTreefolk.java +++ b/Mage.Sets/src/mage/cards/m/MagnigothTreefolk.java @@ -40,23 +40,23 @@ public final class MagnigothTreefolk extends CardImpl { Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect( new GainAbilitySourceEffect(new PlainswalkAbility()), - new PermanentsOnTheBattlefieldCondition(filterPlains, ComparisonType.MORE_THAN, 0, true), + new PermanentsOnTheBattlefieldCondition(filterPlains), "Domain — For each basic land type among lands you control, {this} has landwalk of that type.")); ability.addEffect(new ConditionalContinuousEffect( new GainAbilitySourceEffect(new IslandwalkAbility(), Duration.WhileOnBattlefield, false, true), - new PermanentsOnTheBattlefieldCondition(filterIsland, ComparisonType.MORE_THAN, 0, true), + new PermanentsOnTheBattlefieldCondition(filterIsland), "")); ability.addEffect(new ConditionalContinuousEffect( new GainAbilitySourceEffect(new SwampwalkAbility(), Duration.WhileOnBattlefield, false, true), - new PermanentsOnTheBattlefieldCondition(filterSwamp, ComparisonType.MORE_THAN, 0, true), + new PermanentsOnTheBattlefieldCondition(filterSwamp), "")); ability.addEffect(new ConditionalContinuousEffect( new GainAbilitySourceEffect(new MountainwalkAbility(), Duration.WhileOnBattlefield, false, true), - new PermanentsOnTheBattlefieldCondition(filterMountain, ComparisonType.MORE_THAN, 0, true), + new PermanentsOnTheBattlefieldCondition(filterMountain), "")); ability.addEffect(new ConditionalContinuousEffect( new GainAbilitySourceEffect(new ForestwalkAbility(), Duration.WhileOnBattlefield, false, true), - new PermanentsOnTheBattlefieldCondition(filterForest, ComparisonType.MORE_THAN, 0, true), + new PermanentsOnTheBattlefieldCondition(filterForest), "")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/m/MagusOfTheArena.java b/Mage.Sets/src/mage/cards/m/MagusOfTheArena.java index 4ffc5416ac1..b848d837108 100644 --- a/Mage.Sets/src/mage/cards/m/MagusOfTheArena.java +++ b/Mage.Sets/src/mage/cards/m/MagusOfTheArena.java @@ -56,7 +56,8 @@ class MagusOfTheArenaEffect extends OneShotEffect { MagusOfTheArenaEffect() { super(Outcome.Benefit); - this.staticText = "Tap target creature you control and target creature of an opponent's choice they control. Those creatures fight each other"; + this.staticText = "Tap target creature you control and target creature of an opponent's choice they control. " + + "Those creatures fight each other. (Each deals damage equal to its power to the other.)"; } MagusOfTheArenaEffect(final MagusOfTheArenaEffect effect) { diff --git a/Mage.Sets/src/mage/cards/m/MagusOfTheOrder.java b/Mage.Sets/src/mage/cards/m/MagusOfTheOrder.java index 6f88d216073..f343367e796 100644 --- a/Mage.Sets/src/mage/cards/m/MagusOfTheOrder.java +++ b/Mage.Sets/src/mage/cards/m/MagusOfTheOrder.java @@ -37,6 +37,7 @@ public final class MagusOfTheOrder extends CardImpl { filter.add(new ColorPredicate(ObjectColor.GREEN)); filter2.add(AnotherPredicate.instance); filter2.add(new ColorPredicate(ObjectColor.GREEN)); + filter2.add(CardType.CREATURE.getPredicate()); } public MagusOfTheOrder(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/m/MakeAWish.java b/Mage.Sets/src/mage/cards/m/MakeAWish.java index 6ccc5a56dd1..6f6ac29406c 100644 --- a/Mage.Sets/src/mage/cards/m/MakeAWish.java +++ b/Mage.Sets/src/mage/cards/m/MakeAWish.java @@ -63,7 +63,7 @@ class MakeAWishEffect extends OneShotEffect { TargetCard target = new TargetCardInYourGraveyard(Math.min(player.getGraveyard().size(), 2), StaticFilters.FILTER_CARD); target.setNotTarget(true); target.setRandom(true); - player.chooseTarget(outcome, target, source, game); + target.chooseTarget(outcome, player.getId(), source, game); return player.moveCards(new CardsImpl(target.getTargets()), Zone.HAND, source, game); } } diff --git a/Mage.Sets/src/mage/cards/m/MalakirCullblade.java b/Mage.Sets/src/mage/cards/m/MalakirCullblade.java index 028bcca5d44..e013dbb9342 100644 --- a/Mage.Sets/src/mage/cards/m/MalakirCullblade.java +++ b/Mage.Sets/src/mage/cards/m/MalakirCullblade.java @@ -9,21 +9,14 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; import mage.counters.CounterType; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; /** * * @author fireshoes */ public final class MalakirCullblade extends CardImpl { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } public MalakirCullblade(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}"); @@ -33,7 +26,7 @@ public final class MalakirCullblade extends CardImpl { this.toughness = new MageInt(1); // Whenever a creature an opponent controls dies, put a +1/+1 counter on Malakir Cullblade. - this.addAbility(new DiesCreatureTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false, filter)); + this.addAbility(new DiesCreatureTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false, StaticFilters.FILTER_OPPONENTS_PERMANENT_A_CREATURE)); } private MalakirCullblade(final MalakirCullblade card) { diff --git a/Mage.Sets/src/mage/cards/m/MalevolentHermit.java b/Mage.Sets/src/mage/cards/m/MalevolentHermit.java index 743e168f114..85c609a4651 100644 --- a/Mage.Sets/src/mage/cards/m/MalevolentHermit.java +++ b/Mage.Sets/src/mage/cards/m/MalevolentHermit.java @@ -8,7 +8,6 @@ import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.CounterUnlessPaysEffect; import mage.abilities.keyword.DisturbAbility; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -30,7 +29,6 @@ public final class MalevolentHermit extends CardImpl { this.subtype.add(SubType.WIZARD); this.power = new MageInt(2); this.toughness = new MageInt(1); - this.transformable = true; this.secondSideCardClazz = mage.cards.b.BenevolentGeist.class; // {U}, Sacrifice Malevolent Hermit: Counter target noncreature spell unless its controller pays {3}. @@ -42,8 +40,7 @@ public final class MalevolentHermit extends CardImpl { this.addAbility(ability); // Disturb {2}{U} - this.addAbility(new TransformAbility()); - this.addAbility(new DisturbAbility(new ManaCostsImpl<>("{2}{U}"))); + this.addAbility(new DisturbAbility(this, "{2}{U}")); } private MalevolentHermit(final MalevolentHermit card) { diff --git a/Mage.Sets/src/mage/cards/m/Malfunction.java b/Mage.Sets/src/mage/cards/m/Malfunction.java index 76e452d221c..0c124b02ede 100644 --- a/Mage.Sets/src/mage/cards/m/Malfunction.java +++ b/Mage.Sets/src/mage/cards/m/Malfunction.java @@ -1,7 +1,5 @@ - package mage.cards.m; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; @@ -12,15 +10,16 @@ import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.filter.predicate.Predicates; import mage.target.TargetPermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class Malfunction extends CardImpl { @@ -34,7 +33,7 @@ public final class Malfunction extends CardImpl { } public Malfunction(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{3}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{U}"); this.subtype.add(SubType.AURA); // Enchant artifact or creature. @@ -45,7 +44,7 @@ public final class Malfunction extends CardImpl { this.addAbility(ability); // When Malfunction enters the battlefield, tap enchanted permanent. - this.addAbility(new EntersBattlefieldTriggeredAbility(new TapEnchantedEffect())); + this.addAbility(new EntersBattlefieldTriggeredAbility(new TapEnchantedEffect().setText("tap enchanted permanent"))); // Enchanted permanent doesn't untap during its controller's untap step. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DontUntapInControllersUntapStepEnchantedEffect("permanent"))); diff --git a/Mage.Sets/src/mage/cards/m/MaliciousAffliction.java b/Mage.Sets/src/mage/cards/m/MaliciousAffliction.java index 7f1534ab42d..e537e4dd686 100644 --- a/Mage.Sets/src/mage/cards/m/MaliciousAffliction.java +++ b/Mage.Sets/src/mage/cards/m/MaliciousAffliction.java @@ -1,6 +1,5 @@ package mage.cards.m; -import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.condition.common.MorbidCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; @@ -11,9 +10,7 @@ import mage.abilities.hint.common.MorbidHint; 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; @@ -23,12 +20,6 @@ import java.util.UUID; */ public final class MaliciousAffliction extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creatures"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public MaliciousAffliction(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{B}{B}"); @@ -43,7 +34,7 @@ public final class MaliciousAffliction extends CardImpl { // Destroy target nonblack creature. this.getSpellAbility().addEffect(new DestroyTargetEffect()); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); } private MaliciousAffliction(final MaliciousAffliction card) { diff --git a/Mage.Sets/src/mage/cards/m/MaliciousInvader.java b/Mage.Sets/src/mage/cards/m/MaliciousInvader.java new file mode 100644 index 00000000000..95c36419cb9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MaliciousInvader.java @@ -0,0 +1,63 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MaliciousInvader extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent(SubType.HUMAN, ""); + + static { + filter.add(TargetController.OPPONENT.getControllerPredicate()); + } + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter); + private static final Hint hint = new ConditionHint(condition, "An opponent controls a Human"); + + public MaliciousInvader(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); + + this.subtype.add(SubType.VAMPIRE); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + this.color.setBlack(true); + this.nightCard = true; + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Malicious Invader gets +2/+0 as long as an opponent controls a Human. + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new BoostSourceEffect(2, 0, Duration.WhileOnBattlefield), + condition, "{this} gets +2/+0 as long as an opponent controls a Human" + )).addHint(hint)); + } + + private MaliciousInvader(final MaliciousInvader card) { + super(card); + } + + @Override + public MaliciousInvader copy() { + return new MaliciousInvader(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MaliciousMalfunction.java b/Mage.Sets/src/mage/cards/m/MaliciousMalfunction.java new file mode 100644 index 00000000000..97b963ae23d --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MaliciousMalfunction.java @@ -0,0 +1,84 @@ +package mage.cards.m; + +import java.util.UUID; + +import mage.abilities.Ability; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.continuous.BoostAllEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; + +/** + * + * @author weirddan455 + */ +public final class MaliciousMalfunction extends CardImpl { + + public MaliciousMalfunction(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}{B}"); + + // All creatures get -2/-2 until end of turn. If a creature would die this turn, exile it instead. + this.getSpellAbility().addEffect(new BoostAllEffect(-2, -2, Duration.EndOfTurn)); + this.getSpellAbility().addEffect(new MaliciousMalfunctionReplacementEffect()); + } + + private MaliciousMalfunction(final MaliciousMalfunction card) { + super(card); + } + + @Override + public MaliciousMalfunction copy() { + return new MaliciousMalfunction(this); + } +} + +class MaliciousMalfunctionReplacementEffect extends ReplacementEffectImpl { + + public MaliciousMalfunctionReplacementEffect() { + super(Duration.EndOfTurn, Outcome.Exile); + this.staticText = "If a creature would die this turn, exile it instead"; + } + + private MaliciousMalfunctionReplacementEffect(final MaliciousMalfunctionReplacementEffect effect) { + super(effect); + } + + @Override + public MaliciousMalfunctionReplacementEffect copy() { + return new MaliciousMalfunctionReplacementEffect(this); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Permanent permanent = ((ZoneChangeEvent) event).getTarget(); + if (permanent != null) { + Player player = game.getPlayer(permanent.getControllerId()); + if (player != null) { + return player.moveCards(permanent, Zone.EXILED, source, game); + } + } + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + return zEvent.getTarget() != null + && zEvent.getTarget().isCreature(game) + && zEvent.isDiesEvent(); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MammothHarness.java b/Mage.Sets/src/mage/cards/m/MammothHarness.java index 94b3d80f826..48bde41bbe4 100644 --- a/Mage.Sets/src/mage/cards/m/MammothHarness.java +++ b/Mage.Sets/src/mage/cards/m/MammothHarness.java @@ -1,4 +1,3 @@ - package mage.cards.m; import java.util.UUID; @@ -80,14 +79,14 @@ class MammothHarnessTriggeredAbility extends BlocksOrBecomesBlockedSourceTrigger if (event.getSourceId().equals(attachedTo.getId())) { Permanent blocked = game.getPermanent(event.getTargetId()); if (blocked != null && filter.match(blocked, game)) { - this.getEffects().setTargetPointer(new FixedTarget(event.getTargetId())); + this.getEffects().setTargetPointer(new FixedTarget(event.getTargetId(), game)); return true; } } if (event.getTargetId().equals(attachedTo.getId())) { Permanent blocker = game.getPermanent(event.getSourceId()); if (blocker != null) { - this.getEffects().setTargetPointer(new FixedTarget(event.getSourceId())); + this.getEffects().setTargetPointer(new FixedTarget(event.getSourceId(), game)); return true; } } diff --git a/Mage.Sets/src/mage/cards/m/ManaEchoes.java b/Mage.Sets/src/mage/cards/m/ManaEchoes.java index 958bfa42cb0..14e02df08bb 100644 --- a/Mage.Sets/src/mage/cards/m/ManaEchoes.java +++ b/Mage.Sets/src/mage/cards/m/ManaEchoes.java @@ -13,7 +13,6 @@ import mage.constants.Outcome; import mage.constants.SetTargetPointer; import mage.constants.Zone; import mage.filter.StaticFilters; -import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -29,7 +28,7 @@ public final class ManaEchoes extends CardImpl { // Whenever a creature enters the battlefield, you may add X mana of {C}, where X is the number of creatures you control that share a creature type with it. this.addAbility(new EntersBattlefieldAllTriggeredAbility(Zone.BATTLEFIELD, - new ManaEchoesEffect(), new FilterCreaturePermanent("a creature"), true, SetTargetPointer.PERMANENT, "")); + new ManaEchoesEffect(), StaticFilters.FILTER_PERMANENT_A_CREATURE, true, SetTargetPointer.PERMANENT, "")); } private ManaEchoes(final ManaEchoes card) { diff --git a/Mage.Sets/src/mage/cards/m/ManaGeode.java b/Mage.Sets/src/mage/cards/m/ManaGeode.java index 60edeadc52a..7fe926206c3 100644 --- a/Mage.Sets/src/mage/cards/m/ManaGeode.java +++ b/Mage.Sets/src/mage/cards/m/ManaGeode.java @@ -18,7 +18,7 @@ public final class ManaGeode extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); // When Mana Geode enters the battlefield, scry 1. - this.addAbility(new EntersBattlefieldTriggeredAbility(new ScryEffect(1))); + this.addAbility(new EntersBattlefieldTriggeredAbility(new ScryEffect(1, false))); // {T}: Add one mana of any color. this.addAbility(new AnyColorManaAbility()); diff --git a/Mage.Sets/src/mage/cards/m/ManaVault.java b/Mage.Sets/src/mage/cards/m/ManaVault.java index f08a986cbbe..517822b3f9e 100644 --- a/Mage.Sets/src/mage/cards/m/ManaVault.java +++ b/Mage.Sets/src/mage/cards/m/ManaVault.java @@ -41,7 +41,7 @@ public final class ManaVault extends CardImpl { // At the beginning of your draw step, if Mana Vault is tapped, it deals 1 damage to you. this.addAbility(new ConditionalInterveningIfTriggeredAbility( new BeginningOfDrawTriggeredAbility(Zone.BATTLEFIELD, new DamageControllerEffect(1), TargetController.YOU, false), - SourceTappedCondition.instance, + SourceTappedCondition.TAPPED, "At the beginning of your draw step, if {this} is tapped, it deals 1 damage to you.")); // {tap}: Add {C}{C}{C}. this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.ColorlessMana(3), new TapSourceCost())); diff --git a/Mage.Sets/src/mage/cards/m/ManaformHellkite.java b/Mage.Sets/src/mage/cards/m/ManaformHellkite.java new file mode 100644 index 00000000000..7df9d3d3e8b --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/ManaformHellkite.java @@ -0,0 +1,81 @@ +package mage.cards.m; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.token.DragonIllusionToken; +import mage.game.stack.Spell; +import mage.watchers.common.ManaPaidSourceWatcher; + +/** + * + * @author weirddan455 + */ +public final class ManaformHellkite extends CardImpl { + + public ManaformHellkite(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{R}"); + + this.subtype.add(SubType.DRAGON); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever you cast a noncreature spell, create an X/X red Dragon Illusion creature token with flying and haste, where X is the amount of mana spent to cast that spell. + // Exile that token at the beginning of the next end step. + this.addAbility(new SpellCastControllerTriggeredAbility(new ManaformHellkitEffect(), StaticFilters.FILTER_SPELL_A_NON_CREATURE, false, true)); + } + + private ManaformHellkite(final ManaformHellkite card) { + super(card); + } + + @Override + public ManaformHellkite copy() { + return new ManaformHellkite(this); + } +} + +class ManaformHellkitEffect extends OneShotEffect { + + public ManaformHellkitEffect() { + super(Outcome.PutCreatureInPlay); + staticText = "create an X/X red Dragon Illusion creature token with flying and haste, where X is the amount of mana spent to cast that spell. " + + "Exile that token at the beginning of the next end step"; + } + + private ManaformHellkitEffect(final ManaformHellkitEffect effect) { + super(effect); + } + + @Override + public ManaformHellkitEffect copy() { + return new ManaformHellkitEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Spell spell = (Spell) getValue("spellCast"); + if (spell != null) { + CreateTokenEffect effect = new CreateTokenEffect(new DragonIllusionToken(ManaPaidSourceWatcher.getTotalPaid(spell.getId(), game))); + if (effect.apply(game, source)) { + effect.exileTokensCreatedAtNextEndStep(game, source); + return true; + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/m/Manglehorn.java b/Mage.Sets/src/mage/cards/m/Manglehorn.java index 65beee2ad83..728edf76c43 100644 --- a/Mage.Sets/src/mage/cards/m/Manglehorn.java +++ b/Mage.Sets/src/mage/cards/m/Manglehorn.java @@ -1,32 +1,34 @@ package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.PermanentsEnterBattlefieldTappedEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.EntersTheBattlefieldEvent; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterArtifactPermanent; import mage.target.common.TargetArtifactPermanent; +import java.util.UUID; + /** - * * @author fireshoes */ public final class Manglehorn extends CardImpl { + private static final FilterPermanent filter = new FilterArtifactPermanent("artifacts your opponents control"); + + static { + filter.add(TargetController.OPPONENT.getControllerPredicate()); + } + public Manglehorn(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); @@ -40,7 +42,7 @@ public final class Manglehorn extends CardImpl { this.addAbility(ability); // Artifacts your opponents control enter the battlefield tapped. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ManglehornTapEffect())); + this.addAbility(new SimpleStaticAbility(new PermanentsEnterBattlefieldTappedEffect(filter))); } private Manglehorn(final Manglehorn card) { @@ -52,45 +54,3 @@ public final class Manglehorn extends CardImpl { return new Manglehorn(this); } } - -class ManglehornTapEffect extends ReplacementEffectImpl { - - ManglehornTapEffect() { - super(Duration.WhileOnBattlefield, Outcome.Tap); - staticText = "Artifacts your opponents control enter the battlefield tapped"; - } - - ManglehornTapEffect(final ManglehornTapEffect effect) { - super(effect); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Permanent target = ((EntersTheBattlefieldEvent) event).getTarget(); - if (target != null) { - target.setTapped(true); - } - return false; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (game.getOpponents(source.getControllerId()).contains(event.getPlayerId())) { - Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget(); - if (permanent != null && permanent.isArtifact(game)) { - return true; - } - } - return false; - } - - @Override - public ManglehornTapEffect copy() { - return new ManglehornTapEffect(this); - } -} diff --git a/Mage.Sets/src/mage/cards/m/MantleOfTheAncients.java b/Mage.Sets/src/mage/cards/m/MantleOfTheAncients.java index accb8af0396..f37062f168a 100644 --- a/Mage.Sets/src/mage/cards/m/MantleOfTheAncients.java +++ b/Mage.Sets/src/mage/cards/m/MantleOfTheAncients.java @@ -137,11 +137,12 @@ enum MantleOfTheAncientsValue implements DynamicValue { @Override public int calculate(Game game, Ability sourceAbility, Effect effect) { - Permanent permanent = sourceAbility.getSourcePermanentOrLKI(game); - if (permanent == null) { + Permanent sourcePermanent = sourceAbility.getSourcePermanentOrLKI(game); + if (sourcePermanent == null) { return 0; } - return permanent + Permanent permanent = game.getPermanent(sourcePermanent.getAttachedTo()); + return permanent == null ? 0 : permanent .getAttachments() .stream() .map(game::getPermanentOrLKIBattlefield) diff --git a/Mage.Sets/src/mage/cards/m/MaraudingLooter.java b/Mage.Sets/src/mage/cards/m/MaraudingLooter.java index 945b6d21e5c..18878bafd25 100644 --- a/Mage.Sets/src/mage/cards/m/MaraudingLooter.java +++ b/Mage.Sets/src/mage/cards/m/MaraudingLooter.java @@ -34,7 +34,7 @@ public final class MaraudingLooter extends CardImpl { Ability ability = new ConditionalInterveningIfTriggeredAbility( new BeginningOfEndStepTriggeredAbility(new DrawDiscardControllerEffect(1, 1, true), TargetController.YOU, false), RaidCondition.instance, - "Raid — At the beginning of your end step, " + "At the beginning of your end step, " + "if you attacked this turn, " + "you may draw a card. If you do, discard a card."); ability.setAbilityWord(AbilityWord.RAID); diff --git a/Mage.Sets/src/mage/cards/m/MarchOfBurgeoningLife.java b/Mage.Sets/src/mage/cards/m/MarchOfBurgeoningLife.java new file mode 100644 index 00000000000..784a9948af4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MarchOfBurgeoningLife.java @@ -0,0 +1,127 @@ +package mage.cards.m; + +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.costs.costadjusters.ExileCardsFromHandAdjuster; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreatureCard; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicate; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.common.TargetCardInLibrary; +import mage.target.targetadjustment.TargetAdjuster; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MarchOfBurgeoningLife extends CardImpl { + + private static final FilterCard filter = new FilterCard("green cards from your hand"); + + static { + filter.add(new ColorPredicate(ObjectColor.GREEN)); + } + + public MarchOfBurgeoningLife(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{X}{G}"); + + // As an additional cost to cast this spell, you may exile any number of green cards from your hand. This spell costs {2} less to cast for each card exiled this way. + ExileCardsFromHandAdjuster.addAdjusterAndMessage(this, filter); + + // Choose target creature with mana value less than X. Search your library for a creature card with the same name as that creature, put it onto the battlefield tapped, then shuffle. + this.getSpellAbility().addEffect(new MarchOfBurgeoningLifeEffect()); + this.getSpellAbility().setTargetAdjuster(MarchOfBurgeoningLifeAdjuster.instance); + } + + private MarchOfBurgeoningLife(final MarchOfBurgeoningLife card) { + super(card); + } + + @Override + public MarchOfBurgeoningLife copy() { + return new MarchOfBurgeoningLife(this); + } +} + +enum MarchOfBurgeoningLifeAdjuster implements TargetAdjuster { + instance; + + @Override + public void adjustTargets(Ability ability, Game game) { + int xValue = ability.getManaCostsToPay().getX(); + FilterPermanent filter = new FilterCreaturePermanent("creature with mana value less than " + xValue); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, xValue)); + ability.getTargets().clear(); + ability.addTarget(new TargetPermanent(filter)); + } +} + +class MarchOfBurgeoningLifeEffect extends OneShotEffect { + + private static class MarchOfBurgeoningLifePredicate implements Predicate { + private final Permanent permanent; + + private MarchOfBurgeoningLifePredicate(Permanent permanent) { + this.permanent = permanent; + } + + @Override + public boolean apply(Card input, Game game) { + return CardUtil.haveSameNames(permanent, input); + } + } + + MarchOfBurgeoningLifeEffect() { + super(Outcome.Benefit); + staticText = "choose target creature with mana value less than X. Search your library for a creature card " + + "with the same name as that creature, put it onto the battlefield tapped, then shuffle"; + } + + private MarchOfBurgeoningLifeEffect(final MarchOfBurgeoningLifeEffect effect) { + super(effect); + } + + @Override + public MarchOfBurgeoningLifeEffect copy() { + return new MarchOfBurgeoningLifeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (player == null || permanent == null) { + return false; + } + FilterCard filter = new FilterCreatureCard("creature card with the same name as " + permanent.getName()); + filter.add(new MarchOfBurgeoningLifePredicate(permanent)); + TargetCardInLibrary target = new TargetCardInLibrary(0, 1, filter); + player.searchLibrary(target, source, game); + Card card = player.getLibrary().getCard(target.getFirstTarget(), game); + if (card != null) { + player.moveCards( + card, Zone.BATTLEFIELD, source, game, true, + false, false, null + ); + } + player.shuffleLibrary(source, game); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/m/MarchOfOtherworldlyLight.java b/Mage.Sets/src/mage/cards/m/MarchOfOtherworldlyLight.java new file mode 100644 index 00000000000..5d4738b0380 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MarchOfOtherworldlyLight.java @@ -0,0 +1,74 @@ +package mage.cards.m; + +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.costs.costadjusters.ExileCardsFromHandAdjuster; +import mage.abilities.effects.common.ExileTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.filter.FilterCard; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.game.Game; +import mage.target.TargetPermanent; +import mage.target.targetadjustment.TargetAdjuster; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MarchOfOtherworldlyLight extends CardImpl { + + private static final FilterCard filter = new FilterCard("white cards from your hand"); + + static { + filter.add(new ColorPredicate(ObjectColor.WHITE)); + } + + public MarchOfOtherworldlyLight(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{X}{W}"); + + // As an additional cost to cast this spell, you may exile any number of white cards from your hand. This spell costs {2} less to cast for each card exiled this way. + ExileCardsFromHandAdjuster.addAdjusterAndMessage(this, filter); + + // Exile target artifact, creature, or enchantment with mana value X or less. + this.getSpellAbility().addEffect(new ExileTargetEffect( + "exile target artifact, creature, or enchantment with mana value X or less" + )); + this.getSpellAbility().setTargetAdjuster(MarchOfOtherworldlyLightAdjuster.instance); + } + + private MarchOfOtherworldlyLight(final MarchOfOtherworldlyLight card) { + super(card); + } + + @Override + public MarchOfOtherworldlyLight copy() { + return new MarchOfOtherworldlyLight(this); + } +} + +enum MarchOfOtherworldlyLightAdjuster implements TargetAdjuster { + instance; + + @Override + public void adjustTargets(Ability ability, Game game) { + int xValue = ability.getManaCostsToPay().getX(); + FilterPermanent filter = new FilterPermanent( + "artifact, creature, or enchantment with mana value " + xValue + " or less" + ); + filter.add(Predicates.or( + CardType.ARTIFACT.getPredicate(), + CardType.CREATURE.getPredicate(), + CardType.ENCHANTMENT.getPredicate() + )); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, xValue + 1)); + ability.getTargets().clear(); + ability.addTarget(new TargetPermanent(filter)); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MarchOfRecklessJoy.java b/Mage.Sets/src/mage/cards/m/MarchOfRecklessJoy.java new file mode 100644 index 00000000000..b3d06c514ef --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MarchOfRecklessJoy.java @@ -0,0 +1,137 @@ +package mage.cards.m; + +import mage.MageObjectReference; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.condition.Condition; +import mage.abilities.costs.costadjusters.ExileCardsFromHandAdjuster; +import mage.abilities.effects.OneShotEffect; +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.WatcherScope; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.players.Player; +import mage.util.CardUtil; +import mage.watchers.Watcher; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MarchOfRecklessJoy extends CardImpl { + + private static final FilterCard filter = new FilterCard("red cards from your hand"); + + static { + filter.add(new ColorPredicate(ObjectColor.RED)); + } + + public MarchOfRecklessJoy(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{X}{R}"); + + // As an additional cost to cast this spell, you may exile any number of red cards from your hand. This spell costs {2} less to cast for each card exiled this way. + ExileCardsFromHandAdjuster.addAdjusterAndMessage(this, filter); + + // Exile the top X cards of your library. You may play up to two of those cards until the end of your next turn. + this.getSpellAbility().addEffect(new MarchOfRecklessJoyEffect()); + this.getSpellAbility().addWatcher(new MarchOfRecklessJoyWatcher()); + } + + private MarchOfRecklessJoy(final MarchOfRecklessJoy card) { + super(card); + } + + @Override + public MarchOfRecklessJoy copy() { + return new MarchOfRecklessJoy(this); + } +} + +class MarchOfRecklessJoyEffect extends OneShotEffect { + + public MarchOfRecklessJoyEffect() { + super(Outcome.Benefit); + this.staticText = "exile the top X cards of your library. " + + "You may play up to two of those cards until the end of your next turn."; + } + + public MarchOfRecklessJoyEffect(final MarchOfRecklessJoyEffect effect) { + super(effect); + } + + @Override + public MarchOfRecklessJoyEffect copy() { + return new MarchOfRecklessJoyEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + int xValue = source.getManaCostsToPay().getX(); + if (player == null || xValue < 1 || player.getLibrary().size() < 1) { + return false; + } + Set cards = player.getLibrary().getTopCards(game, xValue); + player.moveCardsToExile( + cards, source, game, true, + CardUtil.getExileZoneId(game, source), + CardUtil.getSourceName(game, source) + ); + for (Card card : cards) { + CardUtil.makeCardPlayable( + game, source, card, Duration.UntilEndOfYourNextTurn, + false, null, MarchOfRecklessJoyCondition.instance + ); + } + return true; + } +} + +enum MarchOfRecklessJoyCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + return MarchOfRecklessJoyWatcher.check(source, game); + } +} + +class MarchOfRecklessJoyWatcher extends Watcher { + + private final Map morMap = new HashMap<>(); + + public MarchOfRecklessJoyWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() != GameEvent.EventType.SPELL_CAST || event.getAdditionalReference() == null) { + return; + } + morMap.compute(event + .getAdditionalReference() + .getApprovingMageObjectReference(), + CardUtil::setOrIncrementValue + ); + } + + static boolean check(Ability source, Game game) { + return game + .getState() + .getWatcher(MarchOfRecklessJoyWatcher.class) + .morMap + .getOrDefault(new MageObjectReference(source, 1), 0) < 2; + } +} diff --git a/Mage.Sets/src/mage/cards/m/MarchOfSwirlingMist.java b/Mage.Sets/src/mage/cards/m/MarchOfSwirlingMist.java new file mode 100644 index 00000000000..0e96217d801 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MarchOfSwirlingMist.java @@ -0,0 +1,58 @@ +package mage.cards.m; + +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.costs.costadjusters.ExileCardsFromHandAdjuster; +import mage.abilities.effects.common.PhaseOutTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.game.Game; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetadjustment.TargetAdjuster; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MarchOfSwirlingMist extends CardImpl { + + private static final FilterCard filter = new FilterCard("blue cards from your hand"); + + static { + filter.add(new ColorPredicate(ObjectColor.BLUE)); + } + + public MarchOfSwirlingMist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{X}{U}"); + + // As an additional cost to cast this spell, you may exile any number of blue cards from your hand. This spell costs {2} less to cast for each card exiled this way. + ExileCardsFromHandAdjuster.addAdjusterAndMessage(this, filter); + + // Up to X target creatures phase out. + this.getSpellAbility().addEffect(new PhaseOutTargetEffect().setText("up to X target creatures phase out")); + this.getSpellAbility().setTargetAdjuster(MarchOfSwirlingMistAdjuster.instance); + } + + private MarchOfSwirlingMist(final MarchOfSwirlingMist card) { + super(card); + } + + @Override + public MarchOfSwirlingMist copy() { + return new MarchOfSwirlingMist(this); + } +} + +enum MarchOfSwirlingMistAdjuster implements TargetAdjuster { + instance; + + @Override + public void adjustTargets(Ability ability, Game game) { + ability.getTargets().clear(); + ability.addTarget(new TargetCreaturePermanent(0, ability.getManaCostsToPay().getX())); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MarchOfTheReturned.java b/Mage.Sets/src/mage/cards/m/MarchOfTheReturned.java index cf98549e7ae..bd739cced9b 100644 --- a/Mage.Sets/src/mage/cards/m/MarchOfTheReturned.java +++ b/Mage.Sets/src/mage/cards/m/MarchOfTheReturned.java @@ -1,4 +1,3 @@ - package mage.cards.m; import java.util.UUID; @@ -6,7 +5,7 @@ import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterCreatureCard; +import mage.filter.StaticFilters; import mage.target.common.TargetCardInYourGraveyard; /** @@ -18,10 +17,9 @@ public final class MarchOfTheReturned extends CardImpl { public MarchOfTheReturned(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{B}"); - // Return up to two target creature cards from your graveyard to your hand. this.getSpellAbility().addEffect(new ReturnFromGraveyardToHandTargetEffect()); - this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0,2, new FilterCreatureCard("creature cards from your graveyard"))); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, 2, StaticFilters.FILTER_CARD_CREATURES_YOUR_GRAVEYARD)); } private MarchOfTheReturned(final MarchOfTheReturned card) { diff --git a/Mage.Sets/src/mage/cards/m/MarchOfWretchedSorrow.java b/Mage.Sets/src/mage/cards/m/MarchOfWretchedSorrow.java new file mode 100644 index 00000000000..3c61d72da59 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MarchOfWretchedSorrow.java @@ -0,0 +1,48 @@ +package mage.cards.m; + +import mage.ObjectColor; +import mage.abilities.costs.costadjusters.ExileCardsFromHandAdjuster; +import mage.abilities.dynamicvalue.common.ManacostVariableValue; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.target.common.TargetCreatureOrPlaneswalker; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MarchOfWretchedSorrow extends CardImpl { + + private static final FilterCard filter = new FilterCard("black cards from your hand"); + + static { + filter.add(new ColorPredicate(ObjectColor.BLACK)); + } + + public MarchOfWretchedSorrow(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{X}{B}"); + + // As an additional cost to cast this spell, you may exile any number of black cards from your hand. This spell costs {2} less to cast for each card exiled this way. + ExileCardsFromHandAdjuster.addAdjusterAndMessage(this, filter); + + // March of Wretched Sorrow deals X damage to target creature or planeswalker and you gain X life. + this.getSpellAbility().addEffect(new DamageTargetEffect(ManacostVariableValue.REGULAR)); + this.getSpellAbility().addEffect(new GainLifeEffect(ManacostVariableValue.REGULAR).concatBy("and")); + this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalker()); + } + + private MarchOfWretchedSorrow(final MarchOfWretchedSorrow card) { + super(card); + } + + @Override + public MarchOfWretchedSorrow copy() { + return new MarchOfWretchedSorrow(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MarisiBreakerOfTheCoil.java b/Mage.Sets/src/mage/cards/m/MarisiBreakerOfTheCoil.java index 4c0d98ee1eb..f11c5eda0b5 100644 --- a/Mage.Sets/src/mage/cards/m/MarisiBreakerOfTheCoil.java +++ b/Mage.Sets/src/mage/cards/m/MarisiBreakerOfTheCoil.java @@ -1,12 +1,10 @@ package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.DealsDamageToAPlayerAllTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; -import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.combat.GoadTargetEffect; import mage.cards.CardImpl; @@ -15,8 +13,11 @@ import mage.constants.*; import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** * @author TheElk801 */ @@ -86,8 +87,6 @@ class MarisiBreakerOfTheCoilSpellEffect extends ContinuousRuleModifyingEffectImp class MarisiBreakerOfTheCoilEffect extends OneShotEffect { - private static final Effect effect = new GoadTargetEffect(); - MarisiBreakerOfTheCoilEffect() { super(Outcome.Benefit); staticText = "goad each creature that player controls " @@ -105,13 +104,12 @@ class MarisiBreakerOfTheCoilEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - game.getBattlefield().getAllActivePermanents( + for (Permanent permanent : game.getBattlefield().getAllActivePermanents( StaticFilters.FILTER_PERMANENT_CREATURE, targetPointer.getFirst(game, source), game - ).stream().forEach(permanent -> { - effect.setTargetPointer(new FixedTarget(permanent, game)); - effect.apply(game, source); - }); + )) { + game.addEffect(new GoadTargetEffect().setTargetPointer(new FixedTarget(permanent, game)), source); + } return true; } } diff --git a/Mage.Sets/src/mage/cards/m/MaritLagesSlumber.java b/Mage.Sets/src/mage/cards/m/MaritLagesSlumber.java index 31a9579ff51..851bfdf2e64 100644 --- a/Mage.Sets/src/mage/cards/m/MaritLagesSlumber.java +++ b/Mage.Sets/src/mage/cards/m/MaritLagesSlumber.java @@ -47,7 +47,7 @@ public final class MaritLagesSlumber extends CardImpl { // Whenever Marit Lage's Slumber or another snow permanent enters the battlefield under your control, scry 1. this.addAbility(new EntersBattlefieldThisOrAnotherTriggeredAbility( - new ScryEffect(1), filter, false, true + new ScryEffect(1, false), filter, false, true )); // At the beginning of your upkeep, if you control ten or more snow permanents, sacrifice Marit Lage's Slumber. If you do, create Marit Lage, a legendary 20/20 black Avatar creature token with flying and indestructible. diff --git a/Mage.Sets/src/mage/cards/m/MarkForDeath.java b/Mage.Sets/src/mage/cards/m/MarkForDeath.java index 218a70375d0..dca7f939d2b 100644 --- a/Mage.Sets/src/mage/cards/m/MarkForDeath.java +++ b/Mage.Sets/src/mage/cards/m/MarkForDeath.java @@ -1,4 +1,3 @@ - package mage.cards.m; import java.util.UUID; @@ -12,7 +11,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; -import mage.constants.TargetController; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.CardIdPredicate; @@ -28,19 +27,12 @@ import mage.target.targetpointer.FixedTarget; */ public final class MarkForDeath extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public MarkForDeath(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{R}"); - // Target creature an opponent controls blocks this turn if able. Untap that creature. Other creatures that player controls can't block this turn. this.getSpellAbility().addEffect(new MarkForDeathEffect()); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); } private MarkForDeath(final MarkForDeath card) { @@ -89,4 +81,4 @@ class MarkForDeathEffect extends OneShotEffect { public MarkForDeathEffect copy() { return new MarkForDeathEffect(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/m/MarkovEnforcer.java b/Mage.Sets/src/mage/cards/m/MarkovEnforcer.java new file mode 100644 index 00000000000..8f4d494c6ca --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MarkovEnforcer.java @@ -0,0 +1,54 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DealtDamageAndDiedTriggeredAbility; +import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.FightTargetSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.game.permanent.token.BloodToken; +import mage.target.common.TargetOpponentsCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MarkovEnforcer extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.VAMPIRE, "Vampire"); + + public MarkovEnforcer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}{R}"); + + this.subtype.add(SubType.VAMPIRE); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(6); + this.toughness = new MageInt(6); + + // Whenever Markov Enforcer or another Vampire enters the battlefield under your control, Markov Enforcer fights up to one target creature an opponent controls. + Ability ability = new EntersBattlefieldThisOrAnotherTriggeredAbility( + new FightTargetSourceEffect(), filter, false, true + ); + ability.addTarget(new TargetOpponentsCreaturePermanent(0, 1)); + this.addAbility(ability); + + // Whenever a creature dealt damage by Markov Enforcer this turn dies, create a Blood token. + this.addAbility(new DealtDamageAndDiedTriggeredAbility(new CreateTokenEffect(new BloodToken()))); + } + + private MarkovEnforcer(final MarkovEnforcer card) { + super(card); + } + + @Override + public MarkovEnforcer copy() { + return new MarkovEnforcer(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MarkovPurifier.java b/Mage.Sets/src/mage/cards/m/MarkovPurifier.java new file mode 100644 index 00000000000..7d1b490efc7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MarkovPurifier.java @@ -0,0 +1,49 @@ +package mage.cards.m; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.condition.common.YouGainedLifeCondition; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.constants.*; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.watchers.common.PlayerGainedLifeWatcher; + +/** + * + * @author weirddan455 + */ +public final class MarkovPurifier extends CardImpl { + + public MarkovPurifier(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{B}"); + + this.subtype.add(SubType.VAMPIRE); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // At the beginning of your end step, if you gained life this turn, you may pay {2}. If you do, draw a card. + this.addAbility(new BeginningOfEndStepTriggeredAbility( + Zone.BATTLEFIELD, + new DoIfCostPaid(new DrawCardSourceControllerEffect(1), new GenericManaCost(2)), + TargetController.YOU, new YouGainedLifeCondition(ComparisonType.MORE_THAN, 0), false + ), new PlayerGainedLifeWatcher()); + } + + private MarkovPurifier(final MarkovPurifier card) { + super(card); + } + + @Override + public MarkovPurifier copy() { + return new MarkovPurifier(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MarkovRetribution.java b/Mage.Sets/src/mage/cards/m/MarkovRetribution.java new file mode 100644 index 00000000000..96d0d7d6946 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MarkovRetribution.java @@ -0,0 +1,60 @@ +package mage.cards.m; + +import mage.abilities.Mode; +import mage.abilities.effects.common.DamageWithPowerFromOneToAnotherTargetEffect; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.other.AnotherTargetPredicate; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MarkovRetribution extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.VAMPIRE); + private static final FilterPermanent filter2 = new FilterCreaturePermanent("another target creature"); + + static { + filter2.add(new AnotherTargetPredicate(2)); + } + + public MarkovRetribution(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{R}"); + + // Choose one or both — + this.getSpellAbility().getModes().setMinModes(1); + this.getSpellAbility().getModes().setMaxModes(2); + + // • Creatures you control get +1/+0 until end of turn. + this.getSpellAbility().addEffect(new BoostControlledEffect(1, 0, Duration.EndOfTurn)); + + // • Target Vampire you control deals damage equal to its power to another target creature. + Mode mode = new Mode(new DamageWithPowerFromOneToAnotherTargetEffect()); + TargetPermanent target = new TargetPermanent(filter); + target.setTargetTag(1); + mode.addTarget(target); + target = new TargetPermanent(filter2); + target.setTargetTag(2); + mode.addTarget(target); + this.getSpellAbility().addMode(mode); + } + + private MarkovRetribution(final MarkovRetribution card) { + super(card); + } + + @Override + public MarkovRetribution copy() { + return new MarkovRetribution(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MarkovWaltzer.java b/Mage.Sets/src/mage/cards/m/MarkovWaltzer.java new file mode 100644 index 00000000000..7ac421b32c3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MarkovWaltzer.java @@ -0,0 +1,54 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfCombatTriggeredAbility; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MarkovWaltzer extends CardImpl { + + public MarkovWaltzer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{W}"); + + this.subtype.add(SubType.VAMPIRE); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // At the beginning of combat on your turn, up to two target creatures you control each get +1/+0 until end of turn. + Ability ability = new BeginningOfCombatTriggeredAbility( + new BoostTargetEffect(1, 0) + .setText("up to two target creatures you control each get +1/+0 until end of turn"), + TargetController.YOU, false + ); + ability.addTarget(new TargetControlledCreaturePermanent(0, 2)); + this.addAbility(ability); + } + + private MarkovWaltzer(final MarkovWaltzer card) { + super(card); + } + + @Override + public MarkovWaltzer copy() { + return new MarkovWaltzer(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MarkovsServant.java b/Mage.Sets/src/mage/cards/m/MarkovsServant.java index 5b73ca58c87..81a62c53563 100644 --- a/Mage.Sets/src/mage/cards/m/MarkovsServant.java +++ b/Mage.Sets/src/mage/cards/m/MarkovsServant.java @@ -22,7 +22,6 @@ public final class MarkovsServant extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(4); - this.transformable = true; this.nightCard = true; } diff --git a/Mage.Sets/src/mage/cards/m/MarrowGnawer.java b/Mage.Sets/src/mage/cards/m/MarrowGnawer.java index 487d5f3e4cb..dd999aba8e4 100644 --- a/Mage.Sets/src/mage/cards/m/MarrowGnawer.java +++ b/Mage.Sets/src/mage/cards/m/MarrowGnawer.java @@ -1,5 +1,3 @@ - - package mage.cards.m; import mage.MageInt; @@ -8,6 +6,7 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.continuous.GainAbilityAllEffect; @@ -15,8 +14,8 @@ import mage.abilities.keyword.FearAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; import mage.game.permanent.token.RatToken; import mage.target.common.TargetControlledPermanent; @@ -28,15 +27,10 @@ import java.util.UUID; */ public final class MarrowGnawer extends CardImpl { - private static final FilterCreaturePermanent filterFear = new FilterCreaturePermanent("Rat creatures"); - private static final FilterControlledCreaturePermanent filterSacrifice = new FilterControlledCreaturePermanent("a Rat"); - private static final FilterControlledCreaturePermanent filter3 = new FilterControlledCreaturePermanent("the number of Rats you control"); - - static { - filterFear.add(SubType.RAT.getPredicate()); - filterSacrifice.add(SubType.RAT.getPredicate()); - filter3.add(SubType.RAT.getPredicate()); - } + private static final FilterPermanent filterFear = new FilterPermanent(SubType.RAT, "all Rats"); + private static final FilterControlledPermanent filterSacrifice = new FilterControlledPermanent(SubType.RAT, "a Rat"); + private static final FilterControlledPermanent filterX = new FilterControlledPermanent(SubType.RAT, "Rats you control"); + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filterX, null); public MarrowGnawer (UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}{B}"); @@ -47,14 +41,12 @@ public final class MarrowGnawer extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(3); - // Rat creatures have fear. (They can't be blocked except by artifact creatures and/or black creatures.) + // All Rats have fear. (They can't be blocked except by artifact creatures and/or black creatures.) this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(FearAbility.getInstance(), Duration.WhileOnBattlefield, filterFear))); // {T}, Sacrifice a Rat: create X 1/1 black Rat creature tokens, where X is the number of Rats you control. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, - new CreateTokenEffect(new RatToken(), new PermanentsOnBattlefieldCount(filter3, null)), - new TapSourceCost()); - ability.addCost( new SacrificeTargetCost(new TargetControlledPermanent(filterSacrifice))); + Ability ability = new SimpleActivatedAbility(new CreateTokenEffect(new RatToken(), xValue), new TapSourceCost()); + ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(filterSacrifice))); this.addAbility(ability); } @@ -66,5 +58,4 @@ public final class MarrowGnawer extends CardImpl { public MarrowGnawer copy() { return new MarrowGnawer(this); } - } diff --git a/Mage.Sets/src/mage/cards/m/MartialImpetus.java b/Mage.Sets/src/mage/cards/m/MartialImpetus.java index a57193f549a..7770b595339 100644 --- a/Mage.Sets/src/mage/cards/m/MartialImpetus.java +++ b/Mage.Sets/src/mage/cards/m/MartialImpetus.java @@ -2,8 +2,9 @@ package mage.cards.m; import mage.abilities.Ability; import mage.abilities.common.AttacksAttachedTriggeredAbility; -import mage.abilities.common.GoadAttachedAbility; +import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.combat.GoadAttachedEffect; import mage.abilities.effects.common.continuous.BoostAllEffect; import mage.abilities.effects.common.continuous.BoostEnchantedEffect; import mage.abilities.keyword.EnchantAbility; @@ -44,7 +45,9 @@ public final class MartialImpetus extends CardImpl { this.addAbility(ability); // Enchanted creature gets +1/+1 and is goaded. - this.addAbility(new GoadAttachedAbility(new BoostEnchantedEffect(1, 1))); + ability = new SimpleStaticAbility(new BoostEnchantedEffect(1, 1)); + ability.addEffect(new GoadAttachedEffect()); + this.addAbility(ability); // Whenever enchanted creature attacks, each other creature that's attacking one of your opponents gets +1/+1 until end of turn. this.addAbility(new AttacksAttachedTriggeredAbility( @@ -78,7 +81,7 @@ enum MartialImpetusPredicate implements ObjectSourcePlayerPredicate { && input.getObject() != attachedTo // must be other creature && input.getObject().isAttacking() // attacking && game.getOpponents(auraControllerId) // check for opponents of aura's controller - .contains(game.getCombat().getDefendingPlayerId(input.getObject().getId(), game))) { + .contains(game.getCombat().getDefendingPlayerId(input.getObject().getId(), game))) { return true; } } diff --git a/Mage.Sets/src/mage/cards/m/MartialLaw.java b/Mage.Sets/src/mage/cards/m/MartialLaw.java index 0ddad4628d3..5c7d02009e9 100644 --- a/Mage.Sets/src/mage/cards/m/MartialLaw.java +++ b/Mage.Sets/src/mage/cards/m/MartialLaw.java @@ -9,7 +9,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; /** @@ -17,12 +17,6 @@ import mage.target.common.TargetCreaturePermanent; */ public final class MartialLaw extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public MartialLaw(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{W}{W}"); @@ -30,7 +24,7 @@ public final class MartialLaw extends CardImpl { // At the beginning of your upkeep, detain target creature an opponent controls. // (Until your next turn, that creature can't attack or block and its activated abilities can't be activated.) Ability ability = new BeginningOfUpkeepTriggeredAbility(new DetainTargetEffect(), TargetController.YOU, false); - TargetCreaturePermanent target = new TargetCreaturePermanent(filter); + TargetCreaturePermanent target = new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE); ability.addTarget(target); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/m/MartonStromgald.java b/Mage.Sets/src/mage/cards/m/MartonStromgald.java index 3f8d0dd3396..bc40b4f10d5 100644 --- a/Mage.Sets/src/mage/cards/m/MartonStromgald.java +++ b/Mage.Sets/src/mage/cards/m/MartonStromgald.java @@ -5,6 +5,7 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.common.BlocksSourceTriggeredAbility; +import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.common.continuous.BoostAllEffect; import mage.cards.CardImpl; @@ -13,6 +14,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; import mage.constants.SuperType; +import mage.filter.StaticFilters; import mage.filter.common.FilterAttackingCreature; import mage.filter.common.FilterBlockingCreature; import mage.filter.predicate.mageobject.AnotherPredicate; @@ -30,7 +32,10 @@ public final class MartonStromgald extends CardImpl { attackingFilter.add(AnotherPredicate.instance); blockingFilter.add(AnotherPredicate.instance); } - + + private static final DynamicValue attackingValue = new PermanentsOnBattlefieldCount(attackingFilter); + private static final DynamicValue blockingValue = new PermanentsOnBattlefieldCount(blockingFilter); + public MartonStromgald(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{R}{R}"); addSuperType(SuperType.LEGENDARY); @@ -40,13 +45,10 @@ public final class MartonStromgald extends CardImpl { this.toughness = new MageInt(1); // Whenever Marton Stromgald attacks, other attacking creatures get +1/+1 until end of turn for each attacking creature other than Marton Stromgald. - PermanentsOnBattlefieldCount attackingValue = new PermanentsOnBattlefieldCount(attackingFilter); - this.addAbility(new AttacksTriggeredAbility(new BoostAllEffect(attackingValue, attackingValue, Duration.EndOfTurn, new FilterAttackingCreature(), true, null, true), false)); + this.addAbility(new AttacksTriggeredAbility(new BoostAllEffect(attackingValue, attackingValue, Duration.EndOfTurn, StaticFilters.FILTER_ATTACKING_CREATURES, true, null, true), false)); // Whenever Marton Stromgald blocks, other blocking creatures get +1/+1 until end of turn for each blocking creature other than Marton Stromgald. - PermanentsOnBattlefieldCount blockingValue = new PermanentsOnBattlefieldCount(blockingFilter); - this.addAbility(new BlocksSourceTriggeredAbility(new BoostAllEffect(blockingValue, blockingValue, Duration.EndOfTurn, new FilterBlockingCreature(), true, null, true), false)); - + this.addAbility(new BlocksSourceTriggeredAbility(new BoostAllEffect(blockingValue, blockingValue, Duration.EndOfTurn, StaticFilters.FILTER_BLOCKING_CREATURES, true, null, true), false)); } private MartonStromgald(final MartonStromgald card) { diff --git a/Mage.Sets/src/mage/cards/m/MartyrsOfKorlis.java b/Mage.Sets/src/mage/cards/m/MartyrsOfKorlis.java index e6078c65204..430753d8fc0 100644 --- a/Mage.Sets/src/mage/cards/m/MartyrsOfKorlis.java +++ b/Mage.Sets/src/mage/cards/m/MartyrsOfKorlis.java @@ -35,7 +35,7 @@ public final class MartyrsOfKorlis extends CardImpl { // As long as Martyrs of Korlis is untapped, all damage that would be dealt to you by artifacts is dealt to Martyrs of Korlis instead. Effect effect = new ConditionalReplacementEffect( new RedirectArtifactDamageFromPlayerToSourceEffect(Duration.WhileOnBattlefield), - new InvertCondition(SourceTappedCondition.instance), + SourceTappedCondition.UNTAPPED, null); effect.setText("{this} redirects artifact damage from controller as long as it's untapped"); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); diff --git a/Mage.Sets/src/mage/cards/m/MassMutiny.java b/Mage.Sets/src/mage/cards/m/MassMutiny.java index 7394d7fedd4..4ef9b39f30e 100644 --- a/Mage.Sets/src/mage/cards/m/MassMutiny.java +++ b/Mage.Sets/src/mage/cards/m/MassMutiny.java @@ -88,11 +88,11 @@ class MassMutinyEffect extends OneShotEffect { Permanent targetCreature = game.getPermanent(target.getFirstTarget()); if (targetCreature != null) { ContinuousEffect effect1 = new GainControlTargetEffect(Duration.EndOfTurn); - effect1.setTargetPointer(new FixedTarget(targetCreature.getId())); + effect1.setTargetPointer(new FixedTarget(targetCreature.getId(), game)); game.addEffect(effect1, source); ContinuousEffect effect2 = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn); - effect2.setTargetPointer(new FixedTarget(targetCreature.getId())); + effect2.setTargetPointer(new FixedTarget(targetCreature.getId(), game)); game.addEffect(effect2, source); targetCreature.untap(game); diff --git a/Mage.Sets/src/mage/cards/m/MassacreGirl.java b/Mage.Sets/src/mage/cards/m/MassacreGirl.java index 62197082b3e..e59aca6f533 100644 --- a/Mage.Sets/src/mage/cards/m/MassacreGirl.java +++ b/Mage.Sets/src/mage/cards/m/MassacreGirl.java @@ -31,7 +31,7 @@ public final class MassacreGirl extends CardImpl { this.toughness = new MageInt(4); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // When Massacre Girl enters the battlefield, each other creature gets -1/-1 until end of turn. Whenever a creature dies this turn, each creature other than Massacre Girl gets -1/-1 until end of turn. this.addAbility(new EntersBattlefieldTriggeredAbility(new MassacreGirlEffect())); diff --git a/Mage.Sets/src/mage/cards/m/MassiveMight.java b/Mage.Sets/src/mage/cards/m/MassiveMight.java new file mode 100644 index 00000000000..4c466aff4fe --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MassiveMight.java @@ -0,0 +1,39 @@ +package mage.cards.m; + +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MassiveMight extends CardImpl { + + public MassiveMight(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{G}"); + + // Target creature gets +2/+2 and gains trample until end of turn. + this.getSpellAbility().addEffect(new BoostTargetEffect(2, 2) + .setText("target creature gets +2/+2")); + this.getSpellAbility().addEffect(new GainAbilityTargetEffect( + TrampleAbility.getInstance(), Duration.EndOfTurn + ).setText("and gains trample until end of turn")); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + private MassiveMight(final MassiveMight card) { + super(card); + } + + @Override + public MassiveMight copy() { + return new MassiveMight(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MassiveRaid.java b/Mage.Sets/src/mage/cards/m/MassiveRaid.java index 72102824461..6e164905436 100644 --- a/Mage.Sets/src/mage/cards/m/MassiveRaid.java +++ b/Mage.Sets/src/mage/cards/m/MassiveRaid.java @@ -19,7 +19,7 @@ public final class MassiveRaid extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{R}{R}"); // Massive Raid deals damage to any target equal to the number of creatures you control. - this.getSpellAbility().addEffect(new DamageTargetEffect(CreaturesYouControlCount.instance)); + this.getSpellAbility().addEffect(new DamageTargetEffect(CreaturesYouControlCount.instance).setText("{this} deals damage to any target equal to the number of creatures you control")); this.getSpellAbility().addTarget(new TargetAnyTarget()); this.getSpellAbility().addHint(CreaturesYouControlHint.instance); } diff --git a/Mage.Sets/src/mage/cards/m/MasterOfTheHunt.java b/Mage.Sets/src/mage/cards/m/MasterOfTheHunt.java index 6a9bf75a11e..106776f83e6 100644 --- a/Mage.Sets/src/mage/cards/m/MasterOfTheHunt.java +++ b/Mage.Sets/src/mage/cards/m/MasterOfTheHunt.java @@ -1,7 +1,5 @@ - package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; @@ -10,22 +8,25 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; import mage.game.permanent.token.WolvesOfTheHuntToken; +import java.util.UUID; + /** * @author L_J */ public final class MasterOfTheHunt extends CardImpl { public MasterOfTheHunt(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{G}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{G}"); this.subtype.add(SubType.HUMAN); this.power = new MageInt(2); this.toughness = new MageInt(2); // Create a 1/1 green Wolf creature token named Wolves of the Hunt. It has “bands with other creatures named Wolves of the Hunt.” - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new WolvesOfTheHuntToken()), new ManaCostsImpl("{2}{G}{G}"))); + this.addAbility(new SimpleActivatedAbility( + new CreateTokenEffect(new WolvesOfTheHuntToken()), new ManaCostsImpl<>("{2}{G}{G}") + )); } private MasterOfTheHunt(final MasterOfTheHunt card) { diff --git a/Mage.Sets/src/mage/cards/m/MasterThief.java b/Mage.Sets/src/mage/cards/m/MasterThief.java index dfe7617e94b..011c3d7d49b 100644 --- a/Mage.Sets/src/mage/cards/m/MasterThief.java +++ b/Mage.Sets/src/mage/cards/m/MasterThief.java @@ -1,11 +1,8 @@ package mage.cards.m; -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.GainControlTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -13,7 +10,8 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; import mage.target.common.TargetArtifactPermanent; -import mage.watchers.common.LostControlWatcher; + +import java.util.UUID; /** * @author Loki, JayDi85 @@ -29,14 +27,8 @@ public final class MasterThief extends CardImpl { this.toughness = new MageInt(2); // When Master Thief enters the battlefield, gain control of target artifact for as long as you control Master Thief. - Ability ability = new EntersBattlefieldTriggeredAbility(new ConditionalContinuousEffect( - new GainControlTargetEffect(Duration.Custom), - new SourceOnBattlefieldControlUnchangedCondition(), - "gain control of target artifact for as long as you control {this}"), - false); - + Ability ability = new EntersBattlefieldTriggeredAbility(new GainControlTargetEffect(Duration.WhileControlled)); ability.addTarget(new TargetArtifactPermanent()); - ability.addWatcher(new LostControlWatcher()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/m/MasterTrinketeer.java b/Mage.Sets/src/mage/cards/m/MasterTrinketeer.java index 62dbdf42c70..ca21dfa78ab 100644 --- a/Mage.Sets/src/mage/cards/m/MasterTrinketeer.java +++ b/Mage.Sets/src/mage/cards/m/MasterTrinketeer.java @@ -1,7 +1,5 @@ - package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; @@ -13,36 +11,42 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; import mage.game.permanent.token.ServoToken; +import java.util.UUID; + /** - * * @author fireshoes */ public final class MasterTrinketeer extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Servo and Thopter creatures"); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Servos and Thopters"); static { - filter.add(Predicates.or(SubType.SERVO.getPredicate(), - SubType.THOPTER.getPredicate())); + filter.add(Predicates.or( + SubType.SERVO.getPredicate(), + SubType.THOPTER.getPredicate() + )); } public MasterTrinketeer(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); this.subtype.add(SubType.DWARF); this.subtype.add(SubType.ARTIFICER); this.power = new MageInt(3); this.toughness = new MageInt(2); // Servo and Thopter creatures you control get +1/+1. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield, filter, false))); + this.addAbility(new SimpleStaticAbility(new BoostControlledEffect( + 1, 1, Duration.WhileOnBattlefield, filter, false + ))); // {3}{W}: Create a 1/1 colorless Servo artifact creature token. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new ServoToken(), 1), new ManaCostsImpl("{3}{W}"))); + this.addAbility(new SimpleActivatedAbility( + new CreateTokenEffect(new ServoToken(), 1), new ManaCostsImpl<>("{3}{W}") + )); } private MasterTrinketeer(final MasterTrinketeer card) { diff --git a/Mage.Sets/src/mage/cards/m/MasterWarcraft.java b/Mage.Sets/src/mage/cards/m/MasterWarcraft.java index e739dbb994f..f8451295410 100644 --- a/Mage.Sets/src/mage/cards/m/MasterWarcraft.java +++ b/Mage.Sets/src/mage/cards/m/MasterWarcraft.java @@ -1,20 +1,20 @@ - package mage.cards.m; -import java.util.*; - import mage.abilities.Ability; import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility; import mage.abilities.condition.common.BeforeAttackersAreDeclaredCondition; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.RequirementEffect; import mage.abilities.effects.RestrictionEffect; import mage.abilities.effects.common.combat.AttacksIfAbleTargetEffect; import mage.abilities.effects.common.combat.CantAttackTargetEffect; +import mage.abilities.effects.common.combat.ChooseBlockersEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.TargetController; import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.events.GameEvent; @@ -23,11 +23,13 @@ import mage.players.Player; import mage.target.Target; import mage.target.common.TargetCreaturePermanent; import mage.target.targetpointer.FixedTarget; -import mage.watchers.Watcher; -import mage.watchers.common.ChooseBlockersRedundancyWatcher; +import mage.watchers.common.ControlCombatRedundancyWatcher; + +import java.util.Map; +import java.util.Set; +import java.util.UUID; /** - * * @author L_J */ public final class MasterWarcraft extends CardImpl { @@ -36,20 +38,19 @@ public final class MasterWarcraft extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{R/W}{R/W}"); // Cast Master Warcraft only before attackers are declared. - this.addAbility(new CastOnlyDuringPhaseStepSourceAbility(null, null, BeforeAttackersAreDeclaredCondition.instance, "Cast this spell only before attackers are declared")); + this.addAbility(new CastOnlyDuringPhaseStepSourceAbility( + null, null, BeforeAttackersAreDeclaredCondition.instance, + "Cast this spell only before attackers are declared" + )); // You choose which creatures attack this turn. this.getSpellAbility().addEffect(new MasterWarcraftChooseAttackersEffect()); // You choose which creatures block this turn and how those creatures block. - this.getSpellAbility().addEffect(new MasterWarcraftChooseBlockersEffect()); - + this.getSpellAbility().addEffect(new ChooseBlockersEffect(Duration.EndOfTurn).concatBy("
")); // (only the last resolved Master Warcraft spell's effects apply) - this.getSpellAbility().addWatcher(new MasterWarcraftCastWatcher()); - this.getSpellAbility().addEffect(new MasterWarcraftCastWatcherIncrementEffect()); - this.getSpellAbility().addWatcher(new ChooseBlockersRedundancyWatcher()); - this.getSpellAbility().addEffect(new ChooseBlockersRedundancyWatcherIncrementEffect()); + this.getSpellAbility().addWatcher(new ControlCombatRedundancyWatcher()); } private MasterWarcraft(final MasterWarcraft card) { @@ -60,73 +61,22 @@ public final class MasterWarcraft extends CardImpl { public MasterWarcraft copy() { return new MasterWarcraft(this); } - - private class MasterWarcraftCastWatcherIncrementEffect extends OneShotEffect { - - MasterWarcraftCastWatcherIncrementEffect() { - super(Outcome.Neutral); - } - - MasterWarcraftCastWatcherIncrementEffect(final MasterWarcraftCastWatcherIncrementEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - MasterWarcraftCastWatcher watcher = game.getState().getWatcher(MasterWarcraftCastWatcher.class); - if (watcher != null) { - watcher.increment(); - return true; - } - return false; - } - - @Override - public MasterWarcraftCastWatcherIncrementEffect copy() { - return new MasterWarcraftCastWatcherIncrementEffect(this); - } - } - - private class ChooseBlockersRedundancyWatcherIncrementEffect extends OneShotEffect { - - ChooseBlockersRedundancyWatcherIncrementEffect() { - super(Outcome.Neutral); - } - - ChooseBlockersRedundancyWatcherIncrementEffect(final ChooseBlockersRedundancyWatcherIncrementEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - ChooseBlockersRedundancyWatcher watcher = game.getState().getWatcher(ChooseBlockersRedundancyWatcher.class); - if (watcher != null) { - watcher.increment(); - return true; - } - return false; - } - - @Override - public ChooseBlockersRedundancyWatcherIncrementEffect copy() { - return new ChooseBlockersRedundancyWatcherIncrementEffect(this); - } - } } class MasterWarcraftChooseAttackersEffect extends ContinuousRuleModifyingEffectImpl { private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creatures that will attack this combat (creatures not chosen won't attack this combat)"); + static { filter.add(TargetController.ACTIVE.getControllerPredicate()); } - public MasterWarcraftChooseAttackersEffect() { + MasterWarcraftChooseAttackersEffect() { super(Duration.EndOfTurn, Outcome.Benefit, false, false); staticText = "You choose which creatures attack this turn"; } - public MasterWarcraftChooseAttackersEffect(final MasterWarcraftChooseAttackersEffect effect) { + private MasterWarcraftChooseAttackersEffect(final MasterWarcraftChooseAttackersEffect effect) { super(effect); } @@ -145,131 +95,54 @@ class MasterWarcraftChooseAttackersEffect extends ContinuousRuleModifyingEffectI return event.getType() == GameEvent.EventType.DECLARING_ATTACKERS; } + @Override + public void init(Ability source, Game game) { + super.init(source, game); + ControlCombatRedundancyWatcher.addAttackingController(source.getControllerId(), duration, game); + } + @Override public boolean applies(GameEvent event, Ability source, Game game) { - MasterWarcraftCastWatcher watcher = game.getState().getWatcher(MasterWarcraftCastWatcher.class); - if(watcher == null){ - return false; - } - watcher.decrement(); - if (watcher.copyCountApply > 0) { + if (!ControlCombatRedundancyWatcher.checkAttackingController(source.getControllerId(), game)) { game.informPlayers(source.getSourceObject(game).getIdName() + " didn't apply"); return false; } - watcher.copyCountApply = watcher.copyCount; Player controller = game.getPlayer(source.getControllerId()); Player attackingPlayer = game.getPlayer(game.getCombat().getAttackingPlayerId()); - if (controller != null && attackingPlayer != null && !attackingPlayer.getAvailableAttackers(game).isEmpty()) { - Target target = new TargetCreaturePermanent(0, Integer.MAX_VALUE, filter, true); - if (controller.chooseTarget(Outcome.Benefit, target, source, game)) { - for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterCreaturePermanent(), source.getControllerId(), source.getSourceId(), game)) { - - // Choose creatures that will be attacking this combat - if (target.getTargets().contains(permanent.getId())) { - RequirementEffect effect = new AttacksIfAbleTargetEffect(Duration.EndOfCombat); - effect.setText(""); - effect.setTargetPointer(new FixedTarget(permanent, game)); - game.addEffect(effect, source); - game.informPlayers(controller.getLogName() + " has decided that " + permanent.getLogName() + " attacks this combat if able"); - - // All other creatures can't attack (unless they must attack) - } else { - boolean hasToAttack = false; - for (Map.Entry> entry : game.getContinuousEffects().getApplicableRequirementEffects(permanent, false, game).entrySet()) { - RequirementEffect effect2 = entry.getKey(); - if (effect2.mustAttack(game)) { - hasToAttack = true; - } - } - if (!hasToAttack) { - RestrictionEffect effect = new CantAttackTargetEffect(Duration.EndOfCombat); - effect.setText(""); - effect.setTargetPointer(new FixedTarget(permanent, game)); - game.addEffect(effect, source); - } + if (controller == null || attackingPlayer == null || attackingPlayer.getAvailableAttackers(game).isEmpty()) { + return false; // the attack declaration resumes for the active player as normal + } + Target target = new TargetCreaturePermanent(0, Integer.MAX_VALUE, filter, true); + if (!controller.chooseTarget(Outcome.Benefit, target, source, game)) { + return false; // the attack declaration resumes for the active player as normal + } + for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterCreaturePermanent(), source.getControllerId(), source.getSourceId(), game)) { + + // Choose creatures that will be attacking this combat + if (target.getTargets().contains(permanent.getId())) { + RequirementEffect effect = new AttacksIfAbleTargetEffect(Duration.EndOfCombat); + effect.setText(""); + effect.setTargetPointer(new FixedTarget(permanent, game)); + game.addEffect(effect, source); + game.informPlayers(controller.getLogName() + " has decided that " + permanent.getLogName() + " attacks this combat if able"); + + // All other creatures can't attack (unless they must attack) + } else { + boolean hasToAttack = false; + for (Map.Entry> entry : game.getContinuousEffects().getApplicableRequirementEffects(permanent, false, game).entrySet()) { + RequirementEffect effect2 = entry.getKey(); + if (effect2.mustAttack(game)) { + hasToAttack = true; } } + if (!hasToAttack) { + RestrictionEffect effect = new CantAttackTargetEffect(Duration.EndOfCombat); + effect.setText(""); + effect.setTargetPointer(new FixedTarget(permanent, game)); + game.addEffect(effect, source); + } } } return false; // the attack declaration resumes for the active player as normal } } - -class MasterWarcraftChooseBlockersEffect extends ContinuousRuleModifyingEffectImpl { - - public MasterWarcraftChooseBlockersEffect() { - super(Duration.EndOfTurn, Outcome.Benefit, false, false); - staticText = "You choose which creatures block this turn and how those creatures block"; - } - - public MasterWarcraftChooseBlockersEffect(final MasterWarcraftChooseBlockersEffect effect) { - super(effect); - } - - @Override - public MasterWarcraftChooseBlockersEffect copy() { - return new MasterWarcraftChooseBlockersEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - return false; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DECLARING_BLOCKERS; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - ChooseBlockersRedundancyWatcher watcher = game.getState().getWatcher(ChooseBlockersRedundancyWatcher.class); - if(watcher == null){ - return false; - } - watcher.decrement(); - if (watcher.copyCountApply > 0) { - game.informPlayers(source.getSourceObject(game).getIdName() + " didn't apply"); - return false; - } - watcher.copyCountApply = watcher.copyCount; - Player blockController = game.getPlayer(source.getControllerId()); - if (blockController != null) { - game.getCombat().selectBlockers(blockController, source, game); - return true; - } - return false; - } -} - -class MasterWarcraftCastWatcher extends Watcher { - - public int copyCount = 0; - public int copyCountApply = 0; - - public MasterWarcraftCastWatcher() { - super(WatcherScope.GAME); - } - - - @Override - public void reset() { - copyCount = 0; - copyCountApply = 0; - } - - @Override - public void watch(GameEvent event, Game game) { - } - - public void increment() { - copyCount++; - copyCountApply = copyCount; - } - - public void decrement() { - if (copyCountApply > 0) { - copyCountApply--; - } - } -} diff --git a/Mage.Sets/src/mage/cards/m/MastermindsAcquisition.java b/Mage.Sets/src/mage/cards/m/MastermindsAcquisition.java index d1324c9b0c1..8957f7fea2f 100644 --- a/Mage.Sets/src/mage/cards/m/MastermindsAcquisition.java +++ b/Mage.Sets/src/mage/cards/m/MastermindsAcquisition.java @@ -7,7 +7,6 @@ import mage.abilities.hint.common.OpenSideboardHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.StaticFilters; import mage.target.common.TargetCardInLibrary; import java.util.UUID; @@ -24,9 +23,8 @@ public final class MastermindsAcquisition extends CardImpl { // Search your library for a card and put that card into your hand. Then shuffle your library. this.getSpellAbility().addEffect(new SearchLibraryPutInHandEffect(new TargetCardInLibrary())); - // Choose a card you own from outside the game and put it into your hand. - Mode mode = new Mode(new WishEffect(StaticFilters.FILTER_CARD_A, false) - .setText("Put a card you own from outside the game into your hand")); + // Put a card you own from outside the game into your hand. + Mode mode = new Mode(new WishEffect().setText("put a card you own from outside the game into your hand")); this.getSpellAbility().addMode(mode); this.getSpellAbility().addHint(OpenSideboardHint.instance); } diff --git a/Mage.Sets/src/mage/cards/m/MastersRebuke.java b/Mage.Sets/src/mage/cards/m/MastersRebuke.java new file mode 100644 index 00000000000..9a2f4323beb --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MastersRebuke.java @@ -0,0 +1,41 @@ +package mage.cards.m; + +import mage.abilities.effects.common.DamageWithPowerFromOneToAnotherTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent; +import mage.target.TargetPermanent; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author Alex-Vasile + */ +public class MastersRebuke extends CardImpl { + + private static final FilterPermanent filter = new FilterCreatureOrPlaneswalkerPermanent("creature or planeswalker you don't control"); + + static { + filter.add(TargetController.NOT_YOU.getControllerPredicate()); + } + + public MastersRebuke(UUID ownderId, CardSetInfo setInfo) { + super(ownderId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}"); + + // Target creature you control deals damage equal to its power to target creature or planeswalker you don’t control. + this.getSpellAbility().addEffect(new DamageWithPowerFromOneToAnotherTargetEffect()); + this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); + TargetPermanent targetForDamage = new TargetPermanent(filter); + targetForDamage.setTargetTag(2); + this.getSpellAbility().addTarget(targetForDamage); + } + + private MastersRebuke(final MastersRebuke card) { super(card); } + + @Override + public MastersRebuke copy() { return new MastersRebuke(this); } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/m/MathasFiendSeeker.java b/Mage.Sets/src/mage/cards/m/MathasFiendSeeker.java index 7353e0b17cb..117b4812a8c 100644 --- a/Mage.Sets/src/mage/cards/m/MathasFiendSeeker.java +++ b/Mage.Sets/src/mage/cards/m/MathasFiendSeeker.java @@ -21,7 +21,7 @@ import mage.constants.Outcome; import mage.constants.SuperType; import mage.constants.TargetController; import mage.counters.CounterType; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -34,11 +34,6 @@ import mage.target.common.TargetCreaturePermanent; public final class MathasFiendSeeker extends CardImpl { private static final String rule = "For as long as that creature has a bounty counter on it, it has \"When this creature dies, each opponent draws a card and gains 2 life.\""; - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } public MathasFiendSeeker(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}{W}{B}"); @@ -49,11 +44,11 @@ public final class MathasFiendSeeker extends CardImpl { this.toughness = new MageInt(3); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // At the beginning of your end step, put a bounty counter on target creature an opponent controls. For as long as that creature has a bounty counter on it, it has "When this creature dies, each opponent draws a card and gains 2 life." Ability ability = new BeginningOfYourEndStepTriggeredAbility(new AddCountersTargetEffect(CounterType.BOUNTY.createInstance()), false); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); Ability ability2 = new DiesSourceTriggeredAbility(new DrawCardAllEffect(1, TargetController.OPPONENT)); ability2.addEffect(new OpponentsGainLifeEffect()); Effect effect = new MathasFiendSeekerGainAbilityEffect(ability2, Duration.Custom, rule); diff --git a/Mage.Sets/src/mage/cards/m/MaulfistSquad.java b/Mage.Sets/src/mage/cards/m/MaulfistSquad.java index b710563d545..9c861c9e451 100644 --- a/Mage.Sets/src/mage/cards/m/MaulfistSquad.java +++ b/Mage.Sets/src/mage/cards/m/MaulfistSquad.java @@ -24,7 +24,7 @@ public final class MaulfistSquad extends CardImpl { this.toughness = new MageInt(1); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // Fabricate 1 this.addAbility(new FabricateAbility(1)); diff --git a/Mage.Sets/src/mage/cards/m/MayorOfAvabruck.java b/Mage.Sets/src/mage/cards/m/MayorOfAvabruck.java index d3a85d1084b..f9f11c93350 100644 --- a/Mage.Sets/src/mage/cards/m/MayorOfAvabruck.java +++ b/Mage.Sets/src/mage/cards/m/MayorOfAvabruck.java @@ -31,7 +31,6 @@ public final class MayorOfAvabruck extends CardImpl { this.subtype.add(SubType.ADVISOR); this.subtype.add(SubType.WEREWOLF); - this.transformable = true; this.secondSideCardClazz = mage.cards.h.HowlpackAlpha.class; this.power = new MageInt(1); diff --git a/Mage.Sets/src/mage/cards/m/MazemindTome.java b/Mage.Sets/src/mage/cards/m/MazemindTome.java index 282b220a4ee..2e5064d1606 100644 --- a/Mage.Sets/src/mage/cards/m/MazemindTome.java +++ b/Mage.Sets/src/mage/cards/m/MazemindTome.java @@ -31,7 +31,7 @@ public final class MazemindTome extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); // {T}, Put a page counter on Mazemind Tome: Scry 1. - Ability ability = new SimpleActivatedAbility(new ScryEffect(1), new TapSourceCost()); + Ability ability = new SimpleActivatedAbility(new ScryEffect(1, false), new TapSourceCost()); ability.addCost(new PutCountersSourceCost(CounterType.PAGE.createInstance())); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/m/MeanderingTowershell.java b/Mage.Sets/src/mage/cards/m/MeanderingTowershell.java index a70162003ab..1baeccec205 100644 --- a/Mage.Sets/src/mage/cards/m/MeanderingTowershell.java +++ b/Mage.Sets/src/mage/cards/m/MeanderingTowershell.java @@ -72,7 +72,7 @@ class MeanderingTowershellEffect extends OneShotEffect { public MeanderingTowershellEffect() { super(Outcome.Detriment); - this.staticText = "exile it. Return it to the battlefield under your control tapped and attacking at the beginning of the next declare attackers step on your next turn"; + this.staticText = "exile it. Return it to the battlefield under your control tapped and attacking at the beginning of the declare attackers step on your next turn"; } public MeanderingTowershellEffect(final MeanderingTowershellEffect effect) { diff --git a/Mage.Sets/src/mage/cards/m/MechHangar.java b/Mage.Sets/src/mage/cards/m/MechHangar.java new file mode 100644 index 00000000000..7bde7e2a278 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MechHangar.java @@ -0,0 +1,62 @@ + +package mage.cards.m; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.continuous.AddCardTypeTargetEffect; +import mage.abilities.mana.ColorlessManaAbility; +import mage.abilities.mana.ConditionalAnyColorManaAbility; +import mage.abilities.mana.conditional.ConditionalSpellManaBuilder; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.FilterSpell; +import mage.filter.predicate.Predicates; +import mage.target.TargetPermanent; + +/** + * + * @author Addictiveme + */ +public final class MechHangar extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("a Pilot or Vehicle spell"); + + static { + filter.add(Predicates.or(SubType.VEHICLE.getPredicate(), SubType.PILOT.getPredicate())); + } + + public MechHangar(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.LAND},""); + + // {T}: Add {C}. + this.addAbility(new ColorlessManaAbility()); + + // {T}: Add one mana of any color. Spend this mana only to cast a Pilot or Vehicle spell. + this.addAbility(new ConditionalAnyColorManaAbility(new TapSourceCost(), 1, new ConditionalSpellManaBuilder(filter), true)); + + // {3}, {T}, Target Vehicle becomes an artifact creature until end of turn. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCardTypeTargetEffect( + Duration.EndOfTurn, CardType.ARTIFACT, CardType.CREATURE) + .setText("Target Vehicle becomes an artifact creature until end of turn"), new GenericManaCost(3)); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetPermanent(new FilterPermanent(SubType.VEHICLE, "Vehicle"))); + this.addAbility(ability); + } + + private MechHangar(final MechHangar card) { + super(card); + } + + @Override + public MechHangar copy() { + return new MechHangar(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MechtitanCore.java b/Mage.Sets/src/mage/cards/m/MechtitanCore.java new file mode 100644 index 00000000000..f3d1d3bac9b --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MechtitanCore.java @@ -0,0 +1,183 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.CompositeCost; +import mage.abilities.costs.common.ExileSourceCost; +import mage.abilities.costs.common.ExileTargetCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.CrewAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.*; +import mage.filter.common.FilterControlledArtifactPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.token.MechtitanToken; +import mage.game.permanent.token.Token; +import mage.players.Player; +import mage.target.common.TargetControlledPermanent; +import mage.target.targetpointer.FixedTargets; +import mage.util.CardUtil; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MechtitanCore extends CardImpl { + + private static final FilterControlledPermanent filter + = new FilterControlledArtifactPermanent("other artifact creatures and/or Vehicles you control"); + + static { + filter.add(AnotherPredicate.instance); + filter.add(Predicates.or( + CardType.CREATURE.getPredicate(), + SubType.VEHICLE.getPredicate() + )); + } + + public MechtitanCore(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); + + this.subtype.add(SubType.VEHICLE); + this.power = new MageInt(2); + this.toughness = new MageInt(4); + + // {5}, Exile Mechtitan Core and four other artifact creatures and/or Vehicles you control: Create Mechtitan, a legendary 10/10 Construct artifact creature token with flying, vigilance, trample, lifelink, and haste that's all colors. When that token leaves the battlefield, return all cards exiled with Mechtitan Core except Mechtitan Core to the battlefield tapped under their owners' control. + Ability ability = new SimpleActivatedAbility(new MechtitanCoreTokenEffect(), new GenericManaCost(5)); + ability.addCost(new CompositeCost( + new ExileSourceCost(), new ExileTargetCost(new TargetControlledPermanent(4, filter)), + "exile {this} and four other artifact creatures and/or Vehicles you control")); + this.addAbility(ability); + + // Crew 2 + this.addAbility(new CrewAbility(2)); + } + + private MechtitanCore(final MechtitanCore card) { + super(card); + } + + @Override + public MechtitanCore copy() { + return new MechtitanCore(this); + } +} + +class MechtitanCoreTokenEffect extends OneShotEffect { + + MechtitanCoreTokenEffect() { + super(Outcome.Benefit); + staticText = "create Mechtitan, a legendary 10/10 Construct artifact creature token with flying, " + + "vigilance, trample, lifelink, and haste that's all colors. " + + "When that token leaves the battlefield, return all cards exiled with {this} except " + + "{this} to the battlefield tapped under their owners' control"; + } + + private MechtitanCoreTokenEffect(final MechtitanCoreTokenEffect effect) { + super(effect); + } + + @Override + public MechtitanCoreTokenEffect copy() { + return new MechtitanCoreTokenEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Token token = new MechtitanToken(); + token.putOntoBattlefield(1, game, source); + game.addDelayedTriggeredAbility(new MechtitanCoreTriggeredAbility(token, source, game), source); + return true; + } +} + +class MechtitanCoreTriggeredAbility extends DelayedTriggeredAbility { + + private final Set tokenIds = new HashSet<>(); + + MechtitanCoreTriggeredAbility(Token token, Ability source, Game game) { + super(new MechtitanCoreReturnEffect(), Duration.Custom, false, false); + this.getEffects().setTargetPointer(new FixedTargets(game.getExile().getExileZone(CardUtil.getExileZoneId(game, source)), game)); + tokenIds.addAll(token.getLastAddedTokenIds()); + } + + private MechtitanCoreTriggeredAbility(final MechtitanCoreTriggeredAbility ability) { + super(ability); + this.tokenIds.addAll(ability.tokenIds); + } + + @Override + public MechtitanCoreTriggeredAbility copy() { + return new MechtitanCoreTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return tokenIds.contains(event.getTargetId()) + && ((ZoneChangeEvent) event).getFromZone() == Zone.BATTLEFIELD; + } + + @Override + public boolean isInactive(Game game) { + return tokenIds + .stream() + .map(game::getPermanent) + .noneMatch(Objects::nonNull); + } + + @Override + public String getRule() { + return "When that token leaves the battlefield, return all cards exiled with {this} except " + + "{this} to the battlefield tapped under their owners' control"; + } +} + +class MechtitanCoreReturnEffect extends OneShotEffect { + + MechtitanCoreReturnEffect() { + super(Outcome.Benefit); + } + + private MechtitanCoreReturnEffect(final MechtitanCoreReturnEffect effect) { + super(effect); + } + + @Override + public MechtitanCoreReturnEffect copy() { + return new MechtitanCoreReturnEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Cards cards = new CardsImpl(getTargetPointer().getTargets(game, source)); + return player.moveCards( + cards.getCards(game), Zone.BATTLEFIELD, source, game, + true, false, true, null + ); + } +} +// and I'll form the head! diff --git a/Mage.Sets/src/mage/cards/m/MedomaisProphecy.java b/Mage.Sets/src/mage/cards/m/MedomaisProphecy.java index 85079d76fc0..4424a709475 100644 --- a/Mage.Sets/src/mage/cards/m/MedomaisProphecy.java +++ b/Mage.Sets/src/mage/cards/m/MedomaisProphecy.java @@ -61,7 +61,7 @@ class MedomaisProphecyTriggerEffect extends OneShotEffect { MedomaisProphecyTriggerEffect() { super(Outcome.Benefit); - staticText = "When you cast a spell with the chosen card name for the first time this turn, draw two cards."; + staticText = "When you cast a spell with the chosen name for the first time this turn, draw two cards."; } private MedomaisProphecyTriggerEffect(final MedomaisProphecyTriggerEffect effect) { diff --git a/Mage.Sets/src/mage/cards/m/MeganticSliver.java b/Mage.Sets/src/mage/cards/m/MeganticSliver.java index 2e0ae9898fa..069a4e564ea 100644 --- a/Mage.Sets/src/mage/cards/m/MeganticSliver.java +++ b/Mage.Sets/src/mage/cards/m/MeganticSliver.java @@ -1,20 +1,18 @@ - package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.continuous.BoostControlledEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.SubType; import mage.filter.StaticFilters; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class MeganticSliver extends CardImpl { @@ -27,7 +25,10 @@ public final class MeganticSliver extends CardImpl { this.toughness = new MageInt(3); // Sliver creatures you control get +3/+3. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(3, 3, Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS))); + this.addAbility(new SimpleStaticAbility(new BoostControlledEffect( + 3, 3, Duration.WhileOnBattlefield, + StaticFilters.FILTER_PERMANENT_SLIVERS + ))); } private MeganticSliver(final MeganticSliver card) { diff --git a/Mage.Sets/src/mage/cards/m/Melee.java b/Mage.Sets/src/mage/cards/m/Melee.java index 1d9cca9086f..3ff9968ce8c 100644 --- a/Mage.Sets/src/mage/cards/m/Melee.java +++ b/Mage.Sets/src/mage/cards/m/Melee.java @@ -1,6 +1,5 @@ package mage.cards.m; -import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility; import mage.abilities.condition.CompoundCondition; @@ -8,25 +7,23 @@ import mage.abilities.condition.Condition; import mage.abilities.condition.common.BeforeBlockersAreDeclaredCondition; import mage.abilities.condition.common.IsPhaseCondition; import mage.abilities.condition.common.MyTurnCondition; -import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; import mage.abilities.effects.common.RemoveFromCombatTargetEffect; import mage.abilities.effects.common.UntapTargetEffect; +import mage.abilities.effects.common.combat.ChooseBlockersEffect; import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Outcome; import mage.constants.TurnPhase; import mage.game.Game; import mage.game.combat.CombatGroup; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.targetpointer.FixedTarget; -import mage.watchers.common.ChooseBlockersRedundancyWatcher; +import mage.watchers.common.ControlCombatRedundancyWatcher; import java.util.UUID; @@ -35,21 +32,26 @@ import java.util.UUID; */ public final class Melee extends CardImpl { + private static final Condition condition = new CompoundCondition( + BeforeBlockersAreDeclaredCondition.instance, + new IsPhaseCondition(TurnPhase.COMBAT), + MyTurnCondition.instance + ); + private static final Hint hint = new ConditionHint(condition, "Can be cast"); + public Melee(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{4}{R}"); // Cast Melee only during your turn and only during combat before blockers are declared. - Condition condition = new CompoundCondition(BeforeBlockersAreDeclaredCondition.instance, - new IsPhaseCondition(TurnPhase.COMBAT), - MyTurnCondition.instance); - this.addAbility(new CastOnlyDuringPhaseStepSourceAbility(null, null, condition, "Cast this spell only during your turn and only during combat before blockers are declared") - .addHint(new ConditionHint(condition, "Can cast melee (it's combat phase on your turn)"))); + this.addAbility(new CastOnlyDuringPhaseStepSourceAbility( + null, null, condition, + "Cast this spell only during your turn and only during combat before blockers are declared" + ).addHint(hint)); // You choose which creatures block this combat and how those creatures block. // (only the last resolved Melee spell's blocking effect applies) - this.getSpellAbility().addEffect(new MeleeChooseBlockersEffect()); - this.getSpellAbility().addWatcher(new ChooseBlockersRedundancyWatcher()); - this.getSpellAbility().addEffect(new ChooseBlockersRedundancyWatcherIncrementEffect()); + this.getSpellAbility().addEffect(new ChooseBlockersEffect(Duration.EndOfCombat)); + this.getSpellAbility().addWatcher(new ControlCombatRedundancyWatcher()); // Whenever a creature attacks and isn't blocked this combat, untap it and remove it from combat. this.getSpellAbility().addEffect(new CreateDelayedTriggeredAbilityEffect(new MeleeTriggeredAbility())); @@ -63,82 +65,6 @@ public final class Melee extends CardImpl { public Melee copy() { return new Melee(this); } - - private class ChooseBlockersRedundancyWatcherIncrementEffect extends OneShotEffect { - - ChooseBlockersRedundancyWatcherIncrementEffect() { - super(Outcome.Neutral); - } - - ChooseBlockersRedundancyWatcherIncrementEffect(final ChooseBlockersRedundancyWatcherIncrementEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - ChooseBlockersRedundancyWatcher watcher = game.getState().getWatcher(ChooseBlockersRedundancyWatcher.class); - if (watcher != null) { - watcher.increment(); - return true; - } - return false; - } - - @Override - public ChooseBlockersRedundancyWatcherIncrementEffect copy() { - return new ChooseBlockersRedundancyWatcherIncrementEffect(this); - } - } -} - -class MeleeChooseBlockersEffect extends ContinuousRuleModifyingEffectImpl { - - public MeleeChooseBlockersEffect() { - super(Duration.EndOfCombat, Outcome.Benefit, false, false); - staticText = "You choose which creatures block this combat and how those creatures block"; - } - - public MeleeChooseBlockersEffect(final MeleeChooseBlockersEffect effect) { - super(effect); - } - - @Override - public MeleeChooseBlockersEffect copy() { - return new MeleeChooseBlockersEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - return false; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DECLARING_BLOCKERS; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - ChooseBlockersRedundancyWatcher watcher = game.getState().getWatcher(ChooseBlockersRedundancyWatcher.class); - if (watcher == null) { - return false; - } - watcher.decrement(); - watcher.copyCount--; - if (watcher.copyCountApply > 0) { - game.informPlayers(source.getSourceObject(game).getIdName() + " didn't apply"); - this.discard(); - return false; - } - watcher.copyCountApply = watcher.copyCount; - Player blockController = game.getPlayer(source.getControllerId()); - if (blockController != null) { - game.getCombat().selectBlockers(blockController, source, game); - return true; - } - this.discard(); - return false; - } } class MeleeTriggeredAbility extends DelayedTriggeredAbility { diff --git a/Mage.Sets/src/mage/cards/m/MemoryOfToshiro.java b/Mage.Sets/src/mage/cards/m/MemoryOfToshiro.java new file mode 100644 index 00000000000..31ad7b29957 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MemoryOfToshiro.java @@ -0,0 +1,45 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.costs.common.PayLifeCost; +import mage.abilities.mana.ConditionalColoredManaAbility; +import mage.abilities.mana.builder.common.InstantOrSorcerySpellManaBuilder; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MemoryOfToshiro extends CardImpl { + + public MemoryOfToshiro(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, ""); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SAMURAI); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + this.color.setBlack(true); + this.nightCard = true; + + // {T}, Pay 1 life: Add {B}. Spend this mana only to cast an instant or sorcery spell. + Ability ability = new ConditionalColoredManaAbility(Mana.BlackMana(1), new InstantOrSorcerySpellManaBuilder()); + ability.addCost(new PayLifeCost(1)); + this.addAbility(ability); + } + + private MemoryOfToshiro(final MemoryOfToshiro card) { + super(card); + } + + @Override + public MemoryOfToshiro copy() { + return new MemoryOfToshiro(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MemorySluice.java b/Mage.Sets/src/mage/cards/m/MemorySluice.java index a911e031f89..13ee4e0952f 100644 --- a/Mage.Sets/src/mage/cards/m/MemorySluice.java +++ b/Mage.Sets/src/mage/cards/m/MemorySluice.java @@ -23,7 +23,7 @@ public final class MemorySluice extends CardImpl { this.getSpellAbility().addTarget(new TargetPlayer()); // Conspire - this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.ONE)); + this.addAbility(new ConspireAbility(ConspireAbility.ConspireTargets.ONE)); } diff --git a/Mage.Sets/src/mage/cards/m/MenacingOgre.java b/Mage.Sets/src/mage/cards/m/MenacingOgre.java index f202df10796..4b8dfc0da5d 100644 --- a/Mage.Sets/src/mage/cards/m/MenacingOgre.java +++ b/Mage.Sets/src/mage/cards/m/MenacingOgre.java @@ -1,9 +1,5 @@ - package mage.cards.m; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -13,15 +9,18 @@ import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.counters.CounterType; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class MenacingOgre extends CardImpl { @@ -79,7 +78,8 @@ class MenacingOgreEffect extends OneShotEffect { Map numberChosen = new HashMap<>(); //players choose numbers - for (Player player : game.getPlayers().values()) { + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerId); if (player != null) { number = player.getAmount(0, 1000, message, game); numberChosen.put(player, number); @@ -92,7 +92,8 @@ class MenacingOgreEffect extends OneShotEffect { } } //reveal numbers to players and follow through with effect - for (Player player : game.getPlayers().values()) { + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerId); if (player != null) { game.informPlayers(player.getLogName() + " chose number " + numberChosen.get(player)); if (numberChosen.get(player) >= highestNumber) { diff --git a/Mage.Sets/src/mage/cards/m/MentorsGuidance.java b/Mage.Sets/src/mage/cards/m/MentorsGuidance.java index 47def4b42a5..e53de32e14a 100644 --- a/Mage.Sets/src/mage/cards/m/MentorsGuidance.java +++ b/Mage.Sets/src/mage/cards/m/MentorsGuidance.java @@ -41,10 +41,9 @@ public final class MentorsGuidance extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{U}"); // When you cast this spell, copy it if you control a planeswalker, Cleric, Druid, Shaman, Warlock, or Wizard. - new CastSourceTriggeredAbility(new ConditionalOneShotEffect( + this.addAbility(new CastSourceTriggeredAbility(new ConditionalOneShotEffect( new CopySourceSpellEffect(), condition, "copy it if you control " + - "a planeswalker, Cleric, Druid, Shaman, Warlock, or Wizard" - )); + "a planeswalker, Cleric, Druid, Shaman, Warlock, or Wizard"))); // Scry 1, then draw a card. this.getSpellAbility().addEffect(new ScryEffect(1, false)); diff --git a/Mage.Sets/src/mage/cards/m/MephiticVapors.java b/Mage.Sets/src/mage/cards/m/MephiticVapors.java index f327ae8d511..0698b320fe5 100644 --- a/Mage.Sets/src/mage/cards/m/MephiticVapors.java +++ b/Mage.Sets/src/mage/cards/m/MephiticVapors.java @@ -21,7 +21,7 @@ public final class MephiticVapors extends CardImpl { this.getSpellAbility().addEffect(new BoostAllEffect(-1, -1, Duration.EndOfTurn)); // Surveil 2. - this.getSpellAbility().addEffect(new SurveilEffect(2)); + this.getSpellAbility().addEffect(new SurveilEffect(2).concatBy("
")); } private MephiticVapors(final MephiticVapors card) { diff --git a/Mage.Sets/src/mage/cards/m/MerEkNightblade.java b/Mage.Sets/src/mage/cards/m/MerEkNightblade.java index b760ac4433d..5062d23e0ab 100644 --- a/Mage.Sets/src/mage/cards/m/MerEkNightblade.java +++ b/Mage.Sets/src/mage/cards/m/MerEkNightblade.java @@ -13,23 +13,14 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.TargetController; import mage.constants.Zone; -import mage.counters.CounterType; -import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; /** * * @author emerald000 */ public final class MerEkNightblade 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 MerEkNightblade(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}"); @@ -43,7 +34,14 @@ public final class MerEkNightblade extends CardImpl { this.addAbility(new OutlastAbility(new ManaCostsImpl<>("{B}"))); // Each creature you control with a +1/+1 counter on it has deathtouch. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(DeathtouchAbility.getInstance(), Duration.WhileOnBattlefield, filter, "Each creature you control with a +1/+1 counter on it has deathtouch"))); + this.addAbility(new SimpleStaticAbility( + Zone.BATTLEFIELD, + new GainAbilityAllEffect( + DeathtouchAbility.getInstance(), + Duration.WhileOnBattlefield, + StaticFilters.FILTER_EACH_CONTROLLED_CREATURE_P1P1) + ) + ); } private MerEkNightblade(final MerEkNightblade card) { diff --git a/Mage.Sets/src/mage/cards/m/MerchantRaiders.java b/Mage.Sets/src/mage/cards/m/MerchantRaiders.java index 9a9dbd385ca..d3bea90ebf0 100644 --- a/Mage.Sets/src/mage/cards/m/MerchantRaiders.java +++ b/Mage.Sets/src/mage/cards/m/MerchantRaiders.java @@ -1,21 +1,17 @@ package mage.cards.m; import mage.MageInt; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility; -import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; +import mage.abilities.effects.common.DontUntapInControllersUntapStepTargetEffect; import mage.abilities.effects.common.TapTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; import mage.filter.FilterPermanent; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.ZoneChangeEvent; -import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; -import mage.watchers.Watcher; import java.util.UUID; @@ -39,9 +35,9 @@ public final class MerchantRaiders extends CardImpl { new TapTargetEffect("tap up to one target creature"), filter, false, true ); - ability.addEffect(new MerchantRaidersEffect()); + ability.addEffect(new DontUntapInControllersUntapStepTargetEffect(Duration.WhileControlled)); ability.addTarget(new TargetCreaturePermanent(0, 1)); - this.addAbility(ability, new MerchantRaidersWatcher()); + this.addAbility(ability); } private MerchantRaiders(final MerchantRaiders card) { @@ -53,98 +49,3 @@ public final class MerchantRaiders extends CardImpl { return new MerchantRaiders(this); } } - -class MerchantRaidersEffect extends ContinuousRuleModifyingEffectImpl { - - public MerchantRaidersEffect() { - super(Duration.Custom, Outcome.Detriment, false, false); - this.staticText = "That creature doesn't untap during its controller's untap step for as long as you control {this}"; - } - - public MerchantRaidersEffect(final MerchantRaidersEffect effect) { - super(effect); - } - - @Override - public MerchantRaidersEffect copy() { - return new MerchantRaidersEffect(this); - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.UNTAP - || event.getType() == GameEvent.EventType.ZONE_CHANGE - || event.getType() == GameEvent.EventType.LOST_CONTROL; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - // Source must be on the battlefield (it's neccessary to check here because if as response to the enter - // the battlefield triggered ability the source dies (or will be exiled), then the ZONE_CHANGE or LOST_CONTROL - // event will happen before this effect is applied ever) - MageObject sourceObject = source.getSourceObjectIfItStillExists(game); - if (!(sourceObject instanceof Permanent) || !((Permanent) sourceObject).isControlledBy(source.getControllerId())) { - discard(); - return false; - } - switch (event.getType()) { - case ZONE_CHANGE: - // end effect if source does a zone move - if (!event.getTargetId().equals(source.getSourceId())) { - break; - } - ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - if (zEvent.getFromZone() == Zone.BATTLEFIELD) { - discard(); - return false; - } - break; - case UNTAP: - // prevent to untap the target creature - if (game.getTurn().getStepType() != PhaseStep.UNTAP - || !event.getTargetId().equals(targetPointer.getFirst(game, source))) { - break; - } - Permanent targetCreature = game.getPermanent(targetPointer.getFirst(game, source)); - if (targetCreature != null) { - return targetCreature.isControlledBy(game.getActivePlayerId()); - } else { - discard(); - return false; - } - case LOST_CONTROL: - // end effect if source control is changed - if (event.getTargetId().equals(source.getSourceId())) { - discard(); - return false; - } - break; - } - return false; - } -} - -class MerchantRaidersWatcher extends Watcher { - - MerchantRaidersWatcher() { - super(WatcherScope.CARD); - } - - @Override - public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.LOST_CONTROL - && event.getPlayerId().equals(controllerId) - && event.getTargetId().equals(sourceId)) { - condition = true; - game.replaceEvent(event); - return; - } else if (event.getType() == GameEvent.EventType.ZONE_CHANGE - && event.getTargetId().equals(sourceId)) { - ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - if (zEvent.getFromZone() == Zone.BATTLEFIELD) { - condition = true; - game.replaceEvent(event); - } - } - } -} diff --git a/Mage.Sets/src/mage/cards/m/MercilessPredator.java b/Mage.Sets/src/mage/cards/m/MercilessPredator.java index 579f9b79f7b..9ea43ac8cd2 100644 --- a/Mage.Sets/src/mage/cards/m/MercilessPredator.java +++ b/Mage.Sets/src/mage/cards/m/MercilessPredator.java @@ -21,7 +21,6 @@ public final class MercilessPredator extends CardImpl { // this card is the second face of double-faced card this.nightCard = true; - this.transformable = true; this.power = new MageInt(3); this.toughness = new MageInt(2); diff --git a/Mage.Sets/src/mage/cards/m/MeriekeRiBerit.java b/Mage.Sets/src/mage/cards/m/MeriekeRiBerit.java index 0be01c90a3b..98ff18706f3 100644 --- a/Mage.Sets/src/mage/cards/m/MeriekeRiBerit.java +++ b/Mage.Sets/src/mage/cards/m/MeriekeRiBerit.java @@ -1,14 +1,11 @@ package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.condition.common.SourceOnBattlefieldControlUnchangedCondition; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.DontUntapInControllersUntapStepSourceEffect; @@ -24,10 +21,10 @@ import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; import mage.target.targetpointer.FixedTarget; -import mage.watchers.common.LostControlWatcher; + +import java.util.UUID; /** - * * @author jeffwadsworth */ public final class MeriekeRiBerit extends CardImpl { @@ -43,16 +40,10 @@ public final class MeriekeRiBerit extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DontUntapInControllersUntapStepSourceEffect())); // {tap}: Gain control of target creature for as long as you control Merieke Ri Berit. When Merieke Ri Berit leaves the battlefield or becomes untapped, destroy that creature. It can't be regenerated. - ConditionalContinuousEffect MeriekeRiBeritGainControlEffect = new ConditionalContinuousEffect( - new GainControlTargetEffect(Duration.Custom), - new SourceOnBattlefieldControlUnchangedCondition(), - "Gain control of target creature for as long as you control {this}"); - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, MeriekeRiBeritGainControlEffect, new TapSourceCost()); + Ability ability = new SimpleActivatedAbility(new GainControlTargetEffect(Duration.WhileControlled), new TapSourceCost()); ability.addTarget(new TargetPermanent(new FilterCreaturePermanent("target creature"))); ability.addEffect(new MeriekeRiBeritCreateDelayedTriggerEffect()); - ability.addWatcher(new LostControlWatcher()); this.addAbility(ability); - } private MeriekeRiBerit(final MeriekeRiBerit card) { diff --git a/Mage.Sets/src/mage/cards/m/MetamorphicAlteration.java b/Mage.Sets/src/mage/cards/m/MetamorphicAlteration.java index 973849eea1d..0b7805593a6 100644 --- a/Mage.Sets/src/mage/cards/m/MetamorphicAlteration.java +++ b/Mage.Sets/src/mage/cards/m/MetamorphicAlteration.java @@ -139,8 +139,8 @@ class MetamorphicAlterationEffect extends ContinuousEffectImpl { for (Ability ability : copied.getAbilities()) { permanent.addAbility(ability, source.getSourceId(), game); } - permanent.getPower().setValue(copied.getPower().getBaseValue()); - permanent.getToughness().setValue(copied.getToughness().getBaseValue()); + permanent.getPower().setValue(copied.getPower().getBaseValueModified()); + permanent.getToughness().setValue(copied.getToughness().getBaseValueModified()); return true; } diff --git a/Mage.Sets/src/mage/cards/m/MeteorShower.java b/Mage.Sets/src/mage/cards/m/MeteorShower.java index 5d2955a5409..10598994c9d 100644 --- a/Mage.Sets/src/mage/cards/m/MeteorShower.java +++ b/Mage.Sets/src/mage/cards/m/MeteorShower.java @@ -20,7 +20,7 @@ public final class MeteorShower extends CardImpl { public MeteorShower(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{X}{R}"); - // Meteor Shower deals X plus 1 damage divided as you choose among any number of target creatures and/or players. + // Meteor Shower deals X plus 1 damage divided as you choose among any number of targets. DynamicValue xValue = new IntPlusDynamicValue(1, ManacostVariableValue.REGULAR); this.getSpellAbility().addEffect(new DamageMultiEffect(xValue)); this.getSpellAbility().addTarget(new TargetAnyTargetAmount(xValue)); diff --git a/Mage.Sets/src/mage/cards/m/MichikosReignOfTruth.java b/Mage.Sets/src/mage/cards/m/MichikosReignOfTruth.java new file mode 100644 index 00000000000..5bd75ba5f7c --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MichikosReignOfTruth.java @@ -0,0 +1,62 @@ +package mage.cards.m; + +import mage.abilities.common.SagaAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.ExileSagaAndReturnTransformedEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.keyword.TransformAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SagaChapter; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MichikosReignOfTruth extends CardImpl { + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount( + StaticFilters.FILTER_PERMANENT_CONTROLLED_ARTIFACT_OR_ENCHANTMENT + ); + + public MichikosReignOfTruth(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); + + this.subtype.add(SubType.SAGA); + this.secondSideCardClazz = mage.cards.p.PortraitOfMichiko.class; + + // (As this Saga enters and after your draw step, add a lore counter.) + SagaAbility sagaAbility = new SagaAbility(this); + + // I, II — Target creature gets +1/+1 until end of turn for each artifact and/or enchantment you control. + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_II, + new BoostTargetEffect(xValue, xValue, Duration.EndOfTurn) + .setText("target creature gets +1/+1 until end of turn " + + "for each artifact and/or enchantment you control"), + new TargetCreaturePermanent() + ); + + // III — Exile this Saga, then return it to the battlefield transformed under your control. + this.addAbility(new TransformAbility()); + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, new ExileSagaAndReturnTransformedEffect()); + + this.addAbility(sagaAbility); + } + + private MichikosReignOfTruth(final MichikosReignOfTruth card) { + super(card); + } + + @Override + public MichikosReignOfTruth copy() { + return new MichikosReignOfTruth(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MichonneRuthlessSurvivor.java b/Mage.Sets/src/mage/cards/m/MichonneRuthlessSurvivor.java index f418dae1ad6..75a02d7dccd 100644 --- a/Mage.Sets/src/mage/cards/m/MichonneRuthlessSurvivor.java +++ b/Mage.Sets/src/mage/cards/m/MichonneRuthlessSurvivor.java @@ -15,6 +15,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.game.Game; import mage.game.events.GameEvent; +import mage.game.permanent.token.WalkerToken; import mage.game.permanent.token.ZombieToken; import java.util.Objects; @@ -35,7 +36,7 @@ public final class MichonneRuthlessSurvivor extends CardImpl { this.toughness = new MageInt(3); // When Michonne enters the battlefield, create two Walker tokens. - this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new ZombieToken(), 2))); + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new WalkerToken(), 2))); // As long as Michonne is equipped, she must be blocked if able. this.addAbility(new SimpleStaticAbility(new ConditionalRequirementEffect( @@ -44,7 +45,7 @@ public final class MichonneRuthlessSurvivor extends CardImpl { ))); // Whenever Michonne and at least two Zombies attack, she gains indestructible until end of turn. - this.addAbility(new BattalionAbility()); + this.addAbility(new MichonneRuthlessSurvivorAbility()); } private MichonneRuthlessSurvivor(final MichonneRuthlessSurvivor card) { @@ -57,19 +58,19 @@ public final class MichonneRuthlessSurvivor extends CardImpl { } } -class BattalionAbility extends TriggeredAbilityImpl { +class MichonneRuthlessSurvivorAbility extends TriggeredAbilityImpl { - public BattalionAbility() { + public MichonneRuthlessSurvivorAbility() { super(Zone.BATTLEFIELD, new GainAbilitySourceEffect(IndestructibleAbility.getInstance(), Duration.EndOfTurn)); } - public BattalionAbility(final BattalionAbility ability) { + public MichonneRuthlessSurvivorAbility(final MichonneRuthlessSurvivorAbility ability) { super(ability); } @Override - public BattalionAbility copy() { - return new BattalionAbility(this); + public MichonneRuthlessSurvivorAbility copy() { + return new MichonneRuthlessSurvivorAbility(this); } @Override diff --git a/Mage.Sets/src/mage/cards/m/MidnightArsonist.java b/Mage.Sets/src/mage/cards/m/MidnightArsonist.java new file mode 100644 index 00000000000..f65ca30b814 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MidnightArsonist.java @@ -0,0 +1,85 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.mana.ManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterArtifactPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; +import mage.target.targetadjustment.TargetAdjuster; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MidnightArsonist extends CardImpl { + + public MidnightArsonist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); + + this.subtype.add(SubType.VAMPIRE); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // When Midnight Arsonist enters the battlefield, destroy up to X target artifacts without mana abilities, where X is the number of Vampires you control. + Ability ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect() + .setText("destroy up to X target artifacts without mana abilities, where X is the number of Vampires you control")); + ability.setTargetAdjuster(MidnightArsonistAdjuster.instance); + this.addAbility(ability.addHint(MidnightArsonistAdjuster.getHint())); + } + + private MidnightArsonist(final MidnightArsonist card) { + super(card); + } + + @Override + public MidnightArsonist copy() { + return new MidnightArsonist(this); + } +} + +enum MidnightArsonistAdjuster implements TargetAdjuster { + instance; + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.VAMPIRE); + private static final FilterPermanent filter2 = new FilterArtifactPermanent("artifacts without mana abilities"); + + static { + filter.add(MidnightArsonistPredicate.instance); + } + + private static final Hint hint = new ValueHint("Vampires you control", new PermanentsOnBattlefieldCount(filter)); + + @Override + public void adjustTargets(Ability ability, Game game) { + int vampires = game.getBattlefield().count(filter, ability.getSourceId(), ability.getControllerId(), game); + ability.getTargets().clear(); + ability.addTarget(new TargetPermanent(0, vampires, filter2)); + } + + public static Hint getHint() { + return hint; + } +} + +enum MidnightArsonistPredicate implements Predicate { + instance; + + @Override + public boolean apply(Permanent input, Game game) { + return input.getAbilities(game).stream().noneMatch(ManaAbility.class::isInstance); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MidnightBanshee.java b/Mage.Sets/src/mage/cards/m/MidnightBanshee.java index 690a9d24f9a..18ba566cf18 100644 --- a/Mage.Sets/src/mage/cards/m/MidnightBanshee.java +++ b/Mage.Sets/src/mage/cards/m/MidnightBanshee.java @@ -1,9 +1,7 @@ - package mage.cards.m; import java.util.UUID; import mage.MageInt; -import mage.ObjectColor; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.effects.common.counter.AddCountersAllEffect; import mage.abilities.keyword.WitherAbility; @@ -13,9 +11,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.TargetController; import mage.counters.CounterType; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ColorPredicate; +import mage.filter.StaticFilters; /** * @@ -23,12 +19,6 @@ import mage.filter.predicate.mageobject.ColorPredicate; */ public final class MidnightBanshee extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public MidnightBanshee(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}{B}{B}"); this.subtype.add(SubType.SPIRIT); @@ -38,7 +28,7 @@ public final class MidnightBanshee extends CardImpl { this.addAbility(WitherAbility.getInstance()); // At the beginning of your upkeep, put a -1/-1 counter on each nonblack creature. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(new AddCountersAllEffect(CounterType.M1M1.createInstance(), filter), TargetController.YOU, false)); + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new AddCountersAllEffect(CounterType.M1M1.createInstance(), StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK), TargetController.YOU, false)); } private MidnightBanshee(final MidnightBanshee card) { diff --git a/Mage.Sets/src/mage/cards/m/MidnightOil.java b/Mage.Sets/src/mage/cards/m/MidnightOil.java index 97280100aea..5e064df5f95 100644 --- a/Mage.Sets/src/mage/cards/m/MidnightOil.java +++ b/Mage.Sets/src/mage/cards/m/MidnightOil.java @@ -38,7 +38,8 @@ public final class MidnightOil extends CardImpl { // At the beginning of your draw step, draw an additional card and remove two hour counters from Midnight Oil. Ability ability = new BeginningOfDrawTriggeredAbility( - new DrawCardSourceControllerEffect(1), + new DrawCardSourceControllerEffect(1) + .setText("draw an additional card"), TargetController.YOU, false ); ability.addEffect(new RemoveCounterSourceEffect( diff --git a/Mage.Sets/src/mage/cards/m/MightMakesRight.java b/Mage.Sets/src/mage/cards/m/MightMakesRight.java index 7cc5e13b0ed..0e6054e7a38 100644 --- a/Mage.Sets/src/mage/cards/m/MightMakesRight.java +++ b/Mage.Sets/src/mage/cards/m/MightMakesRight.java @@ -17,6 +17,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.TargetController; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; @@ -30,12 +31,6 @@ public final class MightMakesRight extends CardImpl { private static final String ruleText = "At the beginning of combat on your turn, if you control each creature on the battlefield with the greatest power, " + "gain control of target creature an opponent controls until end of turn. Untap that creature. It gains haste until end of turn."; - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public MightMakesRight(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{5}{R}"); @@ -45,7 +40,7 @@ public final class MightMakesRight extends CardImpl { TriggeredAbility gainControlAbility = new BeginningOfCombatTriggeredAbility(new GainControlTargetEffect(Duration.EndOfTurn), TargetController.YOU, false); gainControlAbility.addEffect(new UntapTargetEffect()); gainControlAbility.addEffect(new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn)); - gainControlAbility.addTarget(new TargetCreaturePermanent(filter)); + gainControlAbility.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); Ability conditionalAbility = new ConditionalInterveningIfTriggeredAbility(gainControlAbility, ControlsEachCreatureWithGreatestPowerCondition.instance, ruleText); this.addAbility(conditionalAbility); } diff --git a/Mage.Sets/src/mage/cards/m/MightOfTheWild.java b/Mage.Sets/src/mage/cards/m/MightOfTheWild.java index d060db19df9..5cc3018bdb2 100644 --- a/Mage.Sets/src/mage/cards/m/MightOfTheWild.java +++ b/Mage.Sets/src/mage/cards/m/MightOfTheWild.java @@ -13,7 +13,7 @@ import mage.constants.CardType; import mage.constants.ComparisonType; import mage.constants.Duration; import mage.filter.FilterPermanent; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.PowerPredicate; @@ -47,7 +47,7 @@ public final class MightOfTheWild extends CardImpl { // Creatures you control gain indestructible this turn. mode = new Mode(); - mode.addEffect(new GainAbilityControlledEffect(IndestructibleAbility.getInstance(), Duration.EndOfTurn, new FilterControlledCreaturePermanent("creatures you control"))); + mode.addEffect(new GainAbilityControlledEffect(IndestructibleAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_CONTROLLED_CREATURES)); this.getSpellAbility().addMode(mode); } diff --git a/Mage.Sets/src/mage/cards/m/Mightstone.java b/Mage.Sets/src/mage/cards/m/Mightstone.java index 2bdfe3beadf..d22cefb0009 100644 --- a/Mage.Sets/src/mage/cards/m/Mightstone.java +++ b/Mage.Sets/src/mage/cards/m/Mightstone.java @@ -1,4 +1,3 @@ - package mage.cards.m; import java.util.UUID; @@ -8,9 +7,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AttackingPredicate; +import mage.filter.StaticFilters; /** * @@ -18,17 +15,11 @@ import mage.filter.predicate.permanent.AttackingPredicate; */ public final class Mightstone extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("attacking creatures"); - - static { - filter.add(AttackingPredicate.instance); - } - public Mightstone(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{4}"); // Attacking creatures get +1/+0. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostAllEffect( 1, 0, Duration.WhileOnBattlefield, filter, false))); + this.addAbility(new SimpleStaticAbility(new BoostAllEffect(1, 0, Duration.WhileOnBattlefield, StaticFilters.FILTER_ATTACKING_CREATURES, false))); } private Mightstone(final Mightstone card) { diff --git a/Mage.Sets/src/mage/cards/m/MilaCraftyCompanion.java b/Mage.Sets/src/mage/cards/m/MilaCraftyCompanion.java index 26e8c32f14f..37f1b436b97 100644 --- a/Mage.Sets/src/mage/cards/m/MilaCraftyCompanion.java +++ b/Mage.Sets/src/mage/cards/m/MilaCraftyCompanion.java @@ -4,7 +4,6 @@ import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.BecomesTargetControlledPermanentTriggeredAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.delayed.AtTheBeginOfYourNextUpkeepDelayedTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; @@ -64,7 +63,7 @@ public final class MilaCraftyCompanion extends ModalDoubleFacesCard { // Lukka, Wayward Bonder // Legendary Planeswalker - Lukka this.getRightHalfCard().addSuperType(SuperType.LEGENDARY); - this.getRightHalfCard().addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.getRightHalfCard().setStartingLoyalty(5); // +1: You may discard a card. If you do, draw a card. If a creature card was discarded this way, draw two cards instead. this.getRightHalfCard().addAbility(new LoyaltyAbility(new LukkaWaywardBonderDiscardEffect(), 1)); diff --git a/Mage.Sets/src/mage/cards/m/MilitiaRallier.java b/Mage.Sets/src/mage/cards/m/MilitiaRallier.java new file mode 100644 index 00000000000..a8d84fc63d9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MilitiaRallier.java @@ -0,0 +1,46 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.effects.common.UntapTargetEffect; +import mage.abilities.keyword.CantAttackAloneAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MilitiaRallier extends CardImpl { + + public MilitiaRallier(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Militia Rallier can't attack alone. + this.addAbility(new CantAttackAloneAbility()); + + // Whenever Militia Rallier attacks, untap target creature. + Ability ability = new AttacksTriggeredAbility(new UntapTargetEffect()); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + private MilitiaRallier(final MilitiaRallier card) { + super(card); + } + + @Override + public MilitiaRallier copy() { + return new MilitiaRallier(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MillicentRestlessRevenant.java b/Mage.Sets/src/mage/cards/m/MillicentRestlessRevenant.java new file mode 100644 index 00000000000..df218e63996 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MillicentRestlessRevenant.java @@ -0,0 +1,131 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.game.Game; +import mage.game.events.DamagedEvent; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.Permanent; +import mage.game.permanent.PermanentToken; +import mage.game.permanent.token.SpiritWhiteToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MillicentRestlessRevenant extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.SPIRIT); + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter); + private static final Hint hint = new ValueHint("Spirits you control", xValue); + + public MillicentRestlessRevenant(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{W}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.SPIRIT); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // This spell costs {1} less to cast for each Spirit you control. + this.addAbility(new SimpleStaticAbility( + Zone.ALL, new SpellCostReductionForEachSourceEffect(1, xValue) + ).addHint(hint)); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever Millicent, Restless Revenant or another nontoken Spirit you control dies or deals combat damage to a player, create a 1/1 white Spirit creature token with flying. + this.addAbility(new MillicentRestlessRevenantTriggeredAbility()); + } + + private MillicentRestlessRevenant(final MillicentRestlessRevenant card) { + super(card); + } + + @Override + public MillicentRestlessRevenant copy() { + return new MillicentRestlessRevenant(this); + } +} + +class MillicentRestlessRevenantTriggeredAbility extends TriggeredAbilityImpl { + + MillicentRestlessRevenantTriggeredAbility() { + super(Zone.BATTLEFIELD, new CreateTokenEffect(new SpiritWhiteToken())); + } + + private MillicentRestlessRevenantTriggeredAbility(final MillicentRestlessRevenantTriggeredAbility ability) { + super(ability); + } + + @Override + public MillicentRestlessRevenantTriggeredAbility copy() { + return new MillicentRestlessRevenantTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE + || event.getType() == GameEvent.EventType.DAMAGED_PLAYER; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent permanent; + switch (event.getType()) { + case ZONE_CHANGE: + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + if (!zEvent.isDiesEvent()) { + return false; + } + permanent = zEvent.getTarget(); + break; + case DAMAGED_PLAYER: + if (!((DamagedEvent) event).isCombatDamage()) { + return false; + } + permanent = game.getPermanentOrLKIBattlefield(event.getSourceId()); + break; + default: + return false; + } + if (permanent == null) { + return false; + } + return permanent.getId().equals(this.getSourceId()) + || (!(permanent instanceof PermanentToken) + && permanent.hasSubtype(SubType.SPIRIT, game)); + } + + @Override + public boolean isInUseableZone(Game game, MageObject source, GameEvent event) { + return TriggeredAbilityImpl.isInUseableZoneDiesTrigger(this, event, game); + } + + @Override + public String getRule() { + return "Whenever {this} or another nontoken Spirit you control dies or deals combat damage to a player, " + + "create a 1/1 white Spirit creature token with flying."; + } +} diff --git a/Mage.Sets/src/mage/cards/m/MimicVat.java b/Mage.Sets/src/mage/cards/m/MimicVat.java index a951e44e4b6..07d791714c8 100644 --- a/Mage.Sets/src/mage/cards/m/MimicVat.java +++ b/Mage.Sets/src/mage/cards/m/MimicVat.java @@ -182,7 +182,7 @@ class MimicVatCreateTokenEffect extends OneShotEffect { CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(source.getControllerId(), null, true); effect.setTargetPointer(new FixedTarget(card, game)); effect.apply(game, source); - for (Permanent addedToken : effect.getAddedPermanent()) { + for (Permanent addedToken : effect.getAddedPermanents()) { ExileTargetEffect exileEffect = new ExileTargetEffect(); exileEffect.setTargetPointer(new FixedTarget(addedToken, game)); DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); diff --git a/Mage.Sets/src/mage/cards/m/MindFlayer.java b/Mage.Sets/src/mage/cards/m/MindFlayer.java index 287fbd6097b..ceead1061f2 100644 --- a/Mage.Sets/src/mage/cards/m/MindFlayer.java +++ b/Mage.Sets/src/mage/cards/m/MindFlayer.java @@ -9,8 +9,6 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.game.Game; -import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; import java.util.UUID; @@ -28,7 +26,7 @@ public final class MindFlayer extends CardImpl { this.toughness = new MageInt(3); // Dominate Monster — When Mind Flayer enters the battlefield, gain control of target creature for as long as you control Mind Flayer. - Ability ability = new EntersBattlefieldTriggeredAbility(new MindFlayerEffect()); + Ability ability = new EntersBattlefieldTriggeredAbility(new GainControlTargetEffect(Duration.WhileControlled)); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability.withFlavorWord("Dominate Monster")); } @@ -42,33 +40,3 @@ public final class MindFlayer extends CardImpl { return new MindFlayer(this); } } - -class MindFlayerEffect extends GainControlTargetEffect { - - MindFlayerEffect() { - super(Duration.Custom, true); - staticText = "gain control of target creature for as long as you control {this}"; - } - - private MindFlayerEffect(final MindFlayerEffect effect) { - super(effect); - } - - @Override - public MindFlayerEffect copy() { - return new MindFlayerEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game); - Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (sourcePermanent == null - || permanent == null - || !sourcePermanent.isControlledBy(source.getControllerId())) { - discard(); - return false; - } - return super.apply(game, source); - } -} diff --git a/Mage.Sets/src/mage/cards/m/MindleechGhoul.java b/Mage.Sets/src/mage/cards/m/MindleechGhoul.java new file mode 100644 index 00000000000..5116b311c03 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MindleechGhoul.java @@ -0,0 +1,86 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.ExploitCreatureTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.ExploitAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInHand; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MindleechGhoul extends CardImpl { + + public MindleechGhoul(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}"); + + this.subtype.add(SubType.ZOMBIE); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Exploit + this.addAbility(new ExploitAbility()); + + // When Mindleech Ghoul exploits a creature, each opponent exiles a card from their hand. + this.addAbility(new ExploitCreatureTriggeredAbility(new MindleechGhoulEffect())); + } + + private MindleechGhoul(final MindleechGhoul card) { + super(card); + } + + @Override + public MindleechGhoul copy() { + return new MindleechGhoul(this); + } +} + +class MindleechGhoulEffect extends OneShotEffect { + + MindleechGhoulEffect() { + super(Outcome.Benefit); + staticText = "each opponent exiles a card from their hand"; + } + + private MindleechGhoulEffect(final MindleechGhoulEffect effect) { + super(effect); + } + + @Override + public MindleechGhoulEffect copy() { + return new MindleechGhoulEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + Cards cards = new CardsImpl(); + for (UUID playerId : game.getOpponents(source.getControllerId())) { + Player opponent = game.getPlayer(playerId); + if (opponent == null || opponent.getHand().isEmpty()) { + continue; + } + TargetCard target = new TargetCardInHand(); + opponent.choose(Outcome.Discard, opponent.getHand(), target, game); + cards.add(game.getCard(target.getFirstTarget())); + } + return controller.moveCards(cards, Zone.EXILED, source, game); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MindlinkMech.java b/Mage.Sets/src/mage/cards/m/MindlinkMech.java new file mode 100644 index 00000000000..38bd0955072 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MindlinkMech.java @@ -0,0 +1,215 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.MageObject; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.CrewAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.MageObjectReferencePredicate; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; +import mage.util.CardUtil; +import mage.util.functions.CopyApplier; +import mage.watchers.Watcher; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class MindlinkMech extends CardImpl { + + public MindlinkMech(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{U}"); + + this.subtype.add(SubType.VEHICLE); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever Mindlink Mech becomes crewed for the first time each turn, until end of turn, Mindlink Mech becomes a copy of target nonlegendary creature that crewed it this turn, except it's 4/3, it's a Vehicle artifact in addition to its other types, and it has flying. + this.addAbility(new MindlinkMechTriggeredAbility()); + + // Crew 1 + this.addAbility(new CrewAbility(1)); + } + + private MindlinkMech(final MindlinkMech card) { + super(card); + } + + @Override + public MindlinkMech copy() { + return new MindlinkMech(this); + } +} + +class MindlinkMechTriggeredAbility extends TriggeredAbilityImpl { + + MindlinkMechTriggeredAbility() { + super(Zone.BATTLEFIELD, new MindlinkMechEffect()); + this.addWatcher(new MindlinkMechWatcher()); + } + + private MindlinkMechTriggeredAbility(final MindlinkMechTriggeredAbility ability) { + super(ability); + } + + @Override + public MindlinkMechTriggeredAbility copy() { + return new MindlinkMechTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.VEHICLE_CREWED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (!event.getSourceId().equals(getSourceId()) || !MindlinkMechWatcher.checkVehicle(this, game)) { + return false; + } + this.getTargets().clear(); + this.addTarget(new TargetPermanent(MindlinkMechWatcher.makeFilter(this, game))); + return true; + } + + @Override + public String getRule() { + return "Whenever {this} becomes crewed for the first time each turn, until end of turn, " + + "{this} becomes a copy of target nonlegendary creature that crewed it this turn, " + + "except it's 4/3, it's a Vehicle artifact in addition to its other types, and it has flying."; + } +} + +class MindlinkMechWatcher extends Watcher { + + private final Map crewCount = new HashMap<>(); + private final Map> crewMap = new HashMap<>(); + private static final FilterPermanent invalidFilter = new FilterPermanent(); + + static { + invalidFilter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, -2)); + } + + MindlinkMechWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + Permanent vehicle; + Permanent crewer; + switch (event.getType()) { + case VEHICLE_CREWED: + vehicle = game.getPermanent(event.getTargetId()); + crewer = null; + break; + case CREWED_VEHICLE: + vehicle = game.getPermanent(event.getSourceId()); + crewer = game.getPermanent(event.getTargetId()); + break; + default: + return; + } + if (vehicle == null) { + return; + } + if (crewer == null) { + crewCount.compute(new MageObjectReference(vehicle, game), (m, i) -> i == null ? 1 : Integer.sum(i, 1)); + return; + } + crewMap.computeIfAbsent(new MageObjectReference(vehicle, game), x -> new HashSet<>()).add(new MageObjectReference(crewer, game)); + } + + @Override + public void reset() { + super.reset(); + crewCount.clear(); + crewMap.clear(); + } + + public static boolean checkVehicle(Ability source, Game game) { + return game + .getState() + .getWatcher(MindlinkMechWatcher.class) + .crewCount + .getOrDefault(new MageObjectReference(source), 0) < 2; + } + + public static FilterPermanent makeFilter(Ability source, Game game) { + Set predicates = game + .getState() + .getWatcher(MindlinkMechWatcher.class) + .crewMap + .computeIfAbsent(new MageObjectReference(source), x -> new HashSet<>()) + .stream() + .filter(mor -> { + Permanent permanent = mor.getPermanent(game); + return permanent != null && !permanent.isLegendary() && permanent.isCreature(game); + }).map(MageObjectReferencePredicate::new) + .collect(Collectors.toSet()); + if (predicates.isEmpty()) { + return invalidFilter; + } + FilterPermanent filterPermanent = new FilterPermanent( + "nonlegendary creature that crewed " + CardUtil.getSourceName(game, source) + " this turn" + ); + filterPermanent.add(Predicates.or(predicates)); + return filterPermanent; + } +} + +class MindlinkMechEffect extends OneShotEffect { + + MindlinkMechEffect() { + super(Outcome.Benefit); + } + + private MindlinkMechEffect(final MindlinkMechEffect effect) { + super(effect); + } + + @Override + public MindlinkMechEffect copy() { + return new MindlinkMechEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + Permanent creature = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (permanent == null || creature == null) { + return false; + } + game.copyPermanent(Duration.EndOfTurn, creature, permanent.getId(), source, new MindlinkMechApplier()); + return true; + } +} + +class MindlinkMechApplier extends CopyApplier { + @Override + public boolean apply(Game game, MageObject blueprint, Ability source, UUID targetObjectId) { + blueprint.getPower().modifyBaseValue(4); + blueprint.getToughness().modifyBaseValue(3); + blueprint.addCardType(game, CardType.ARTIFACT); + blueprint.addSubType(game, SubType.VEHICLE); + blueprint.getAbilities().add(FlyingAbility.getInstance()); + return true; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/m/Mindreaver.java b/Mage.Sets/src/mage/cards/m/Mindreaver.java index 24eb1383c66..ad7febd5e55 100644 --- a/Mage.Sets/src/mage/cards/m/Mindreaver.java +++ b/Mage.Sets/src/mage/cards/m/Mindreaver.java @@ -1,7 +1,6 @@ package mage.cards.m; import mage.MageInt; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; @@ -112,14 +111,13 @@ class MindreaverExileEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); Player player = game.getPlayer(source.getFirstTarget()); - MageObject mageObject = source.getSourceObject(game); - if (controller == null || player == null || mageObject == null) { + if (controller == null || player == null) { return false; } Cards cards = new CardsImpl(player.getLibrary().getTopCards(game, 3)); return controller.moveCardsToExile( cards.getCards(game), source, game, true, - CardUtil.getExileZoneId(game, source), mageObject.getIdName() + CardUtil.getExileZoneId(game, source), CardUtil.getSourceName(game, source) ); } } diff --git a/Mage.Sets/src/mage/cards/m/MineExcavation.java b/Mage.Sets/src/mage/cards/m/MineExcavation.java index 815f33043ef..6c533d70803 100644 --- a/Mage.Sets/src/mage/cards/m/MineExcavation.java +++ b/Mage.Sets/src/mage/cards/m/MineExcavation.java @@ -32,7 +32,7 @@ public final class MineExcavation extends CardImpl { this.getSpellAbility().addTarget(new TargetCardInGraveyard(filter)); // Conspire - this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.ONE)); + this.addAbility(new ConspireAbility(ConspireAbility.ConspireTargets.ONE)); } private MineExcavation(final MineExcavation card) { diff --git a/Mage.Sets/src/mage/cards/m/MinionOfTheMighty.java b/Mage.Sets/src/mage/cards/m/MinionOfTheMighty.java index a266101fac6..098e7f691d2 100644 --- a/Mage.Sets/src/mage/cards/m/MinionOfTheMighty.java +++ b/Mage.Sets/src/mage/cards/m/MinionOfTheMighty.java @@ -34,7 +34,7 @@ public final class MinionOfTheMighty extends CardImpl { this.toughness = new MageInt(1); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // Pack tactics — Whenever Minion of the Mighty attacks, if you attacked with creatures wih total power 6 or greater this combat, you may put a Dragon creature card from your hand onto the battlefield tapped and attacking. this.addAbility(new PackTacticsAbility(new MinionOfTheMightyEffect())); diff --git a/Mage.Sets/src/mage/cards/m/MinionReflector.java b/Mage.Sets/src/mage/cards/m/MinionReflector.java index a438b6e2000..8afbacbcc1d 100644 --- a/Mage.Sets/src/mage/cards/m/MinionReflector.java +++ b/Mage.Sets/src/mage/cards/m/MinionReflector.java @@ -83,7 +83,7 @@ class MinionReflectorEffect extends OneShotEffect { CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(source.getControllerId(), null, true); effect.setTargetPointer(new FixedTarget(permanent, game)); effect.apply(game, source); - for (Permanent addedToken : effect.getAddedPermanent()) { + for (Permanent addedToken : effect.getAddedPermanents()) { ContinuousEffect continuousEffect = new GainAbilityTargetEffect(new BeginningOfEndStepTriggeredAbility(new SacrificeSourceEffect(), TargetController.ANY, false), Duration.Custom); continuousEffect.setTargetPointer(new FixedTarget(addedToken.getId())); game.addEffect(continuousEffect, source); diff --git a/Mage.Sets/src/mage/cards/m/MiragePhalanx.java b/Mage.Sets/src/mage/cards/m/MiragePhalanx.java new file mode 100644 index 00000000000..f7391909b6c --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MiragePhalanx.java @@ -0,0 +1,91 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfCombatTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenCopyTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityPairedEffect; +import mage.abilities.keyword.SoulbondAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * @author Alex-Vasile + */ +public class MiragePhalanx extends CardImpl { + + private static final String ruleText = + "As long as {this} is paired with another creature, each of those creatures has " + + "\"At the beginning of combat on your turn, create a token that's a copy of this creature, " + + "except it has haste and loses soulbond. " + + "Exile it at end of combat.\""; + + public MiragePhalanx(UUID ownderId, CardSetInfo setInfo) { + super(ownderId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}{R}"); + + this.addSubType(SubType.HUMAN); + this.addSubType(SubType.SOLDIER); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Soulbond + this.addAbility(new SoulbondAbility()); + + // As long as Mirage Phalanx is paired with another creature, each of those creatures has + // “At the beginning of combat on your turn, create a token that's a copy of this creature, + // except it has haste and loses soulbond. + // Exile it at end of combat.” + Ability ability = new BeginningOfCombatTriggeredAbility(new MiragePhalanxEffect(), TargetController.YOU, false); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityPairedEffect(ability, ruleText))); + } + + private MiragePhalanx(final MiragePhalanx card) { + super(card); + } + + @Override + public MiragePhalanx copy() { + return new MiragePhalanx(this); + } +} + +class MiragePhalanxEffect extends OneShotEffect { + MiragePhalanxEffect() { + super(Outcome.PutCreatureInPlay); + this.staticText = "create a token that's a copy of this creature, " + + "except it has haste and loses soulbond. " + + "Exile it at end of combat."; + } + + private MiragePhalanxEffect(final MiragePhalanxEffect effect) { super(effect); } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = source.getSourcePermanentOrLKI(game); + if (permanent == null) { return false; } + + // It has haste + CreateTokenCopyTargetEffect tokenCopyEffect = new CreateTokenCopyTargetEffect(source.getControllerId(), null, true); + tokenCopyEffect.setTargetPointer(new FixedTarget(permanent, game)); + // It loses soulbond + tokenCopyEffect.addAbilityClassesToRemoveFromTokens(SoulbondAbility.class); + // Create the token(s) + tokenCopyEffect.apply(game, source); + // Exile it at the end of combat + tokenCopyEffect.exileTokensCreatedAtEndOfCombat(game, source); + + return !tokenCopyEffect.getAddedPermanents().isEmpty(); + } + + @Override + public Effect copy() { return new MiragePhalanxEffect(this); } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/m/MirriWeatherlightDuelist.java b/Mage.Sets/src/mage/cards/m/MirriWeatherlightDuelist.java index 7dc615f4747..6ce7d38f3e2 100644 --- a/Mage.Sets/src/mage/cards/m/MirriWeatherlightDuelist.java +++ b/Mage.Sets/src/mage/cards/m/MirriWeatherlightDuelist.java @@ -41,7 +41,7 @@ public final class MirriWeatherlightDuelist extends CardImpl { // As long as Mirri, Weatherlight Duelist is tapped, no more than one creature can attack you each combat. Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect( - new MirriWeatherlightDuelistAttackRestrictionEffect(1), SourceTappedCondition.instance, + new MirriWeatherlightDuelistAttackRestrictionEffect(1), SourceTappedCondition.TAPPED, "As long as {this} is tapped, no more than one creature can attack you each combat.")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/m/MirrorBox.java b/Mage.Sets/src/mage/cards/m/MirrorBox.java new file mode 100644 index 00000000000..791d4266279 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MirrorBox.java @@ -0,0 +1,135 @@ +package mage.cards.m; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.util.CardUtil; + +import java.util.List; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MirrorBox extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("each legendary creature"); + + static { + filter.add(SuperType.LEGENDARY.getPredicate()); + } + + public MirrorBox(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); + + // The "legend rule" doesn't apply to permanents you control. + this.addAbility(new SimpleStaticAbility(new MirrorBoxLegendEffect())); + + // Each legendary creature you control gets +1/+1. + this.addAbility(new SimpleStaticAbility(new BoostControlledEffect( + 1, 1, Duration.WhileOnBattlefield, filter + ))); + + // Each nontoken creature you control gets +1/+1 for each other creature you control with the same name as that creature. + this.addAbility(new SimpleStaticAbility(new MirrorBoxBoostEffect())); + } + + private MirrorBox(final MirrorBox card) { + super(card); + } + + @Override + public MirrorBox copy() { + return new MirrorBox(this); + } +} + +class MirrorBoxLegendEffect extends ContinuousRuleModifyingEffectImpl { + + MirrorBoxLegendEffect() { + super(Duration.WhileOnBattlefield, Outcome.Detriment, false, false); + staticText = "the \"legend rule\" doesn't apply to permanents you control"; + } + + private MirrorBoxLegendEffect(final MirrorBoxLegendEffect effect) { + super(effect); + } + + @Override + public MirrorBoxLegendEffect copy() { + return new MirrorBoxLegendEffect(this); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DESTROY_PERMANENT_BY_LEGENDARY_RULE; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + Permanent permanent = game.getPermanent(event.getTargetId()); + return permanent != null && permanent.isControlledBy(source.getControllerId()); + } +} + +class MirrorBoxBoostEffect extends ContinuousEffectImpl { + + private static final FilterPermanent filter = new FilterControlledCreaturePermanent(); + + static { + filter.add(TokenPredicate.FALSE); + } + + public MirrorBoxBoostEffect() { + super(Duration.WhileOnBattlefield, Layer.PTChangingEffects_7, SubLayer.ModifyPT_7c, Outcome.BoostCreature); + this.staticText = "each nontoken creature you control gets +1/+1 for " + + "each other creature you control with the same name as that creature"; + } + + public MirrorBoxBoostEffect(final MirrorBoxBoostEffect effect) { + super(effect); + } + + @Override + public MirrorBoxBoostEffect copy() { + return new MirrorBoxBoostEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + List permanents = game.getBattlefield().getActivePermanents( + StaticFilters.FILTER_CONTROLLED_CREATURE, + source.getSourceId(), source.getControllerId(), game + ); + for (Permanent permanent : game.getBattlefield().getActivePermanents( + filter, source.getControllerId(), source.getSourceId(), game + )) { + int amount = getAmount(permanents, permanent, game); + permanent.addPower(amount); + permanent.addToughness(amount); + } + return true; + } + + private int getAmount(List permanents, Permanent target, Game game) { + return permanents + .stream() + .filter(permanent -> !permanent.getId().equals(target.getId()) + && CardUtil.haveSameNames(permanent, target)) + .mapToInt(x -> 1) + .sum(); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MirrorMatch.java b/Mage.Sets/src/mage/cards/m/MirrorMatch.java index 33688df5163..7bc8fbc81d3 100644 --- a/Mage.Sets/src/mage/cards/m/MirrorMatch.java +++ b/Mage.Sets/src/mage/cards/m/MirrorMatch.java @@ -77,14 +77,14 @@ class MirrorMatchEffect extends OneShotEffect { CombatGroup group = game.getCombat().findGroup(attacker.getId()); boolean isCreature = false; if (group != null) { - for (Permanent addedToken : effect.getAddedPermanent()) { + for (Permanent addedToken : effect.getAddedPermanents()) { if (addedToken.isCreature(game)) { group.addBlockerToGroup(addedToken.getId(), attackerId, game); isCreature = true; } } ExileTargetEffect exileEffect = new ExileTargetEffect("Exile those tokens at end of combat"); - exileEffect.setTargetPointer(new FixedTargets(effect.getAddedPermanent(), game)); + exileEffect.setTargetPointer(new FixedTargets(effect.getAddedPermanents(), game)); game.addDelayedTriggeredAbility(new AtTheEndOfCombatDelayedTriggeredAbility(exileEffect), source); if (isCreature) { group.pickBlockerOrder(attacker.getControllerId(), game); diff --git a/Mage.Sets/src/mage/cards/m/MirrorMockery.java b/Mage.Sets/src/mage/cards/m/MirrorMockery.java index 9a31aabb6b5..c8fb636afbd 100644 --- a/Mage.Sets/src/mage/cards/m/MirrorMockery.java +++ b/Mage.Sets/src/mage/cards/m/MirrorMockery.java @@ -85,7 +85,7 @@ class MirrorMockeryEffect extends OneShotEffect { CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(); effect.setTargetPointer(new FixedTarget(enchanted, game)); effect.apply(game, source); - for (Permanent addedToken : effect.getAddedPermanent()) { + for (Permanent addedToken : effect.getAddedPermanents()) { if (addedToken != null) { ExileTargetEffect exileEffect = new ExileTargetEffect(); exileEffect.setTargetPointer(new FixedTarget(addedToken, game)); diff --git a/Mage.Sets/src/mage/cards/m/MirrorhallMimic.java b/Mage.Sets/src/mage/cards/m/MirrorhallMimic.java new file mode 100644 index 00000000000..15a77dbb34e --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MirrorhallMimic.java @@ -0,0 +1,62 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.CopyPermanentEffect; +import mage.abilities.keyword.DisturbAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.util.functions.CopyApplier; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MirrorhallMimic extends CardImpl { + + public MirrorhallMimic(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); + + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(0); + this.toughness = new MageInt(0); + this.secondSideCardClazz = mage.cards.g.GhastlyMimicry.class; + + // You may have Mirrorhall Mimic enter the battlefield as a copy of any creature on the battlefield, except it's a Spirit in addition to its other types. + this.addAbility(new EntersBattlefieldAbility(new CopyPermanentEffect( + StaticFilters.FILTER_PERMANENT_CREATURE, new MirrorhallMimicApplier() + ), true, null, "You may have {this} enter the battlefield as a copy of " + + "any creature on the battlefield, except it's a Spirit in addition to its other types.", null)); + + // Disturb {3}{U}{U} + this.addAbility(new DisturbAbility(this, "{3}{U}{U}")); + } + + private MirrorhallMimic(final MirrorhallMimic card) { + super(card); + } + + @Override + public MirrorhallMimic copy() { + return new MirrorhallMimic(this); + } +} + +class MirrorhallMimicApplier extends CopyApplier { + + @Override + public boolean apply(Game game, MageObject blueprint, Ability source, UUID targetObjectId) { + if (!blueprint.hasSubtype(SubType.SPIRIT, game)) { + blueprint.addSubType(game, SubType.SPIRIT); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/m/MirrorshellCrab.java b/Mage.Sets/src/mage/cards/m/MirrorshellCrab.java new file mode 100644 index 00000000000..5f699e5eb48 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MirrorshellCrab.java @@ -0,0 +1,48 @@ +package mage.cards.m; + +import java.util.UUID; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.CounterUnlessPaysEffect; +import mage.abilities.keyword.ChannelAbility; +import mage.abilities.keyword.WardAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.target.TargetStackObject; + +/** + * @author Addictiveme + */ +public final class MirrorshellCrab extends CardImpl { + + public MirrorshellCrab(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE,CardType.ARTIFACT}, "{5}{U}{U}"); + + this.subtype.add(SubType.CRAB); + this.power = new MageInt(5); + this.toughness = new MageInt(7); + + // Ward {3} + this.addAbility(new WardAbility(new GenericManaCost(3))); + + // Channel - {2}{U}, Discard Mirrorshell Crab: + // Counter target spell or ability unless its controller pays {3} + Ability channelAbility = new ChannelAbility("{2}{U}", new CounterUnlessPaysEffect(new GenericManaCost(3)) + .setText("Counter target spell or ability unless its controller pays {3}")); + channelAbility.addTarget(new TargetStackObject()); + this.addAbility(channelAbility); + } + + private MirrorshellCrab(final MirrorshellCrab card) { + super(card); + } + + @Override + public MirrorshellCrab copy() { + return new MirrorshellCrab(this); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/m/MischievousCatgeist.java b/Mage.Sets/src/mage/cards/m/MischievousCatgeist.java new file mode 100644 index 00000000000..2f3a7ac6a8f --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MischievousCatgeist.java @@ -0,0 +1,46 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.keyword.DisturbAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MischievousCatgeist extends CardImpl { + + public MischievousCatgeist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); + + this.subtype.add(SubType.CAT); + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + this.secondSideCardClazz = mage.cards.c.CatlikeCuriosity.class; + + // Whenever Mischievous Catgeist deals combat damage to a player, draw card. + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility( + new DrawCardSourceControllerEffect(1), false + )); + + // Disturb {2}{U} + this.addAbility(new DisturbAbility(this, "{2}{U}")); + } + + private MischievousCatgeist(final MischievousCatgeist card) { + super(card); + } + + @Override + public MischievousCatgeist copy() { + return new MischievousCatgeist(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/Misdirection.java b/Mage.Sets/src/mage/cards/m/Misdirection.java index 3e56791c854..15f7af28439 100644 --- a/Mage.Sets/src/mage/cards/m/Misdirection.java +++ b/Mage.Sets/src/mage/cards/m/Misdirection.java @@ -1,7 +1,5 @@ - package mage.cards.m; -import java.util.UUID; import mage.ObjectColor; import mage.abilities.costs.AlternativeCostSourceAbility; import mage.abilities.costs.common.ExileFromHandCost; @@ -11,34 +9,33 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.filter.FilterSpell; import mage.filter.common.FilterOwnedCard; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardIdPredicate; import mage.filter.predicate.mageobject.ColorPredicate; import mage.filter.predicate.other.NumberOfTargetsPredicate; import mage.target.TargetSpell; import mage.target.common.TargetCardInHand; +import java.util.UUID; + /** - * * @author jonubuu */ public final class Misdirection extends CardImpl { + private static final FilterOwnedCard filter + = new FilterOwnedCard("a blue card from your hand"); + private static final FilterSpell filter2 = new FilterSpell("spell with a single target"); + static { + filter.add(new ColorPredicate(ObjectColor.BLUE)); filter2.add(new NumberOfTargetsPredicate(1)); } public Misdirection(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{3}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{U}{U}"); // You may exile a blue card from your hand rather than pay Misdirection's mana cost. - FilterOwnedCard filterCardInHand = new FilterOwnedCard("a blue card from your hand"); - filterCardInHand.add(new ColorPredicate(ObjectColor.BLUE)); - - // the exile cost can never be paid with the card itself - filterCardInHand.add(Predicates.not(new CardIdPredicate(this.getId()))); - this.addAbility(new AlternativeCostSourceAbility(new ExileFromHandCost(new TargetCardInHand(filterCardInHand)))); + this.addAbility(new AlternativeCostSourceAbility(new ExileFromHandCost(new TargetCardInHand(filter)))); // Change the target of target spell with a single target. this.getSpellAbility().addEffect(new ChooseNewTargetsTargetEffect(true, true)); @@ -54,4 +51,3 @@ public final class Misdirection extends CardImpl { return new Misdirection(this); } } - diff --git a/Mage.Sets/src/mage/cards/m/MistSyndicateNaga.java b/Mage.Sets/src/mage/cards/m/MistSyndicateNaga.java index e9b87d8c898..9f186e82ed7 100644 --- a/Mage.Sets/src/mage/cards/m/MistSyndicateNaga.java +++ b/Mage.Sets/src/mage/cards/m/MistSyndicateNaga.java @@ -26,7 +26,7 @@ public final class MistSyndicateNaga extends CardImpl { this.toughness = new MageInt(1); // Ninjutsu {2}{U} - this.addAbility(new NinjutsuAbility(new ManaCostsImpl("{2}{U}"))); + this.addAbility(new NinjutsuAbility("{2}{U}")); // Whenever Mist-Syndicate Naga deals combat damage to a player, create a token that's a copy of Mist-Syndicate Naga. this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/m/MistbladeShinobi.java b/Mage.Sets/src/mage/cards/m/MistbladeShinobi.java index d35be64857d..c254e174ff7 100644 --- a/Mage.Sets/src/mage/cards/m/MistbladeShinobi.java +++ b/Mage.Sets/src/mage/cards/m/MistbladeShinobi.java @@ -36,7 +36,7 @@ public final class MistbladeShinobi extends CardImpl { this.toughness = new MageInt(1); // Ninjutsu {1}{U} ({1}{U}, Return an unblocked attacker you control to hand: Put this card onto the battlefield from your hand tapped and attacking.) - this.addAbility(new NinjutsuAbility(new ManaCostsImpl("{U}"))); + this.addAbility(new NinjutsuAbility("{U}")); // Whenever Mistblade Shinobi deals combat damage to a player, you may return target creature that player controls to its owner's hand. this.addAbility(new MistbladeShinobiTriggeredAbility()); diff --git a/Mage.Sets/src/mage/cards/m/MistformSliver.java b/Mage.Sets/src/mage/cards/m/MistformSliver.java index 524ef7f7b6e..70f2e7dc2dd 100644 --- a/Mage.Sets/src/mage/cards/m/MistformSliver.java +++ b/Mage.Sets/src/mage/cards/m/MistformSliver.java @@ -1,12 +1,11 @@ package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.AddCardSubTypeTargetEffect; @@ -15,18 +14,25 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.choices.Choice; import mage.choices.ChoiceCreatureType; -import mage.constants.*; -import mage.filter.StaticFilters; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.FilterPermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** * @author cbt33, Plopman (Engineered Plague) */ public final class MistformSliver extends CardImpl { + private static final FilterPermanent filter = new FilterPermanent(SubType.SLIVER, "all Slivers"); + public MistformSliver(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); this.subtype.add(SubType.ILLUSION); @@ -36,8 +42,11 @@ public final class MistformSliver extends CardImpl { this.toughness = new MageInt(1); // All Slivers have "{1}: This permanent becomes the creature type of your choice in addition to its other types until end of turn." - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new MistformSliverEffect(), new ManaCostsImpl("{1}")); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(ability, Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS))); + this.addAbility(new SimpleStaticAbility( + new GainAbilityAllEffect(new SimpleActivatedAbility( + new MistformSliverEffect(), new GenericManaCost(1) + ), Duration.WhileOnBattlefield, filter) + )); } private MistformSliver(final MistformSliver card) { diff --git a/Mage.Sets/src/mage/cards/m/MnemonicBetrayal.java b/Mage.Sets/src/mage/cards/m/MnemonicBetrayal.java index fa7f94a7c30..1a742997e97 100644 --- a/Mage.Sets/src/mage/cards/m/MnemonicBetrayal.java +++ b/Mage.Sets/src/mage/cards/m/MnemonicBetrayal.java @@ -53,7 +53,7 @@ class MnemonicBetrayalExileEffect extends OneShotEffect { public MnemonicBetrayalExileEffect() { super(Outcome.Benefit); - this.staticText = "exile all cards from all opponents' graveyards. " + + this.staticText = "exile all opponents' graveyards. " + "You may cast spells from among those cards this turn, " + "and you may spend mana as though it were mana of any type to cast those spells. " + "At the beginning of the next end step, if any of those cards remain exiled, " + @@ -85,7 +85,7 @@ class MnemonicBetrayalExileEffect extends OneShotEffect { .forEach(cards::addAll); controller.moveCardsToExile( cards.getCards(game), source, game, true, - source.getSourceId(), CardUtil.getSourceLogName(game, source) + source.getSourceId(), CardUtil.getSourceName(game, source) ); for (Card card : cards.getCards(game)) { if (card.isLand(game)) { diff --git a/Mage.Sets/src/mage/cards/m/MnemonicSliver.java b/Mage.Sets/src/mage/cards/m/MnemonicSliver.java index d49bc9e0dad..96304a3864d 100644 --- a/Mage.Sets/src/mage/cards/m/MnemonicSliver.java +++ b/Mage.Sets/src/mage/cards/m/MnemonicSliver.java @@ -4,7 +4,6 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.costs.Cost; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.common.DrawCardSourceControllerEffect; @@ -14,8 +13,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.StaticFilters; +import mage.filter.FilterPermanent; import java.util.UUID; @@ -24,18 +22,21 @@ import java.util.UUID; */ public final class MnemonicSliver extends CardImpl { + private static final FilterPermanent filter = new FilterPermanent(SubType.SLIVER, "all Slivers"); + public MnemonicSliver(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); this.subtype.add(SubType.SLIVER); this.power = new MageInt(2); this.toughness = new MageInt(2); - Cost cost = new SacrificeSourceCost(); - cost.setText("sacrifice this permanent"); - Ability gainedAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), new GenericManaCost(2)); - gainedAbility.addCost(cost); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, - new GainAbilityAllEffect(gainedAbility, Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS, false))); + Ability ability = new SimpleActivatedAbility( + new DrawCardSourceControllerEffect(1), new GenericManaCost(2) + ); + ability.addCost(new SacrificeSourceCost().setText("sacrifice this permanent")); + this.addAbility(new SimpleStaticAbility(new GainAbilityAllEffect( + ability, Duration.WhileOnBattlefield, filter, false + ))); } private MnemonicSliver(final MnemonicSliver card) { diff --git a/Mage.Sets/src/mage/cards/m/MnemonicSphere.java b/Mage.Sets/src/mage/cards/m/MnemonicSphere.java new file mode 100644 index 00000000000..6d7e3e3a7de --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MnemonicSphere.java @@ -0,0 +1,42 @@ +package mage.cards.m; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.keyword.ChannelAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MnemonicSphere extends CardImpl { + + public MnemonicSphere(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}{U}"); + + // {1}{U}, Sacrifice Mnemonic Sphere: Draw two cards. + Ability ability = new SimpleActivatedAbility( + new DrawCardSourceControllerEffect(2), new ManaCostsImpl<>("{1}{U}") + ); + ability.addCost(new SacrificeSourceCost()); + this.addAbility(ability); + + // Channel — {U}, Discard Mnemonic Sphere: Draw a card. + this.addAbility(new ChannelAbility("{U}", new DrawCardSourceControllerEffect(1))); + } + + private MnemonicSphere(final MnemonicSphere card) { + super(card); + } + + @Override + public MnemonicSphere copy() { + return new MnemonicSphere(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MobRule.java b/Mage.Sets/src/mage/cards/m/MobRule.java index 2dd3d2c1751..66cc3543041 100644 --- a/Mage.Sets/src/mage/cards/m/MobRule.java +++ b/Mage.Sets/src/mage/cards/m/MobRule.java @@ -1,4 +1,3 @@ - package mage.cards.m; import java.util.List; @@ -26,7 +25,7 @@ import mage.target.targetpointer.FixedTarget; public final class MobRule extends CardImpl { public MobRule(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{4}{R}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{R}{R}"); // Choose one // Gain control of all creatures with power 4 or greater until end of turn. Untap those creatures. They gain haste until end of turn. @@ -83,7 +82,7 @@ class MobRuleEffect extends OneShotEffect { List creatures = game.getBattlefield().getAllActivePermanents(filter, game); for (Permanent creature : creatures) { ContinuousEffect effect = new MobRuleControlAllEffect(source.getControllerId()); - effect.setTargetPointer(new FixedTarget(creature.getId())); + effect.setTargetPointer(new FixedTarget(creature.getId(), game)); game.addEffect(effect, source); applied = true; } @@ -93,7 +92,7 @@ class MobRuleEffect extends OneShotEffect { } for (Permanent creature : creatures) { ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(creature.getId())); + effect.setTargetPointer(new FixedTarget(creature.getId(), game)); game.addEffect(effect, source); applied = true; } diff --git a/Mage.Sets/src/mage/cards/m/MobileFort.java b/Mage.Sets/src/mage/cards/m/MobileFort.java index deab260e391..52b731cd97b 100644 --- a/Mage.Sets/src/mage/cards/m/MobileFort.java +++ b/Mage.Sets/src/mage/cards/m/MobileFort.java @@ -1,12 +1,10 @@ - package mage.cards.m; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.LimitedTimesPerTurnActivatedAbility; -import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.Effect; +import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.common.combat.CanAttackAsThoughItDidntHaveDefenderSourceEffect; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.keyword.DefenderAbility; @@ -33,10 +31,8 @@ public final class MobileFort extends CardImpl { this.addAbility(DefenderAbility.getInstance()); // {3}: Mobile Fort gets +3/-1 until end of turn and can attack this turn as though it didn't have defender. Activate this ability only once each turn. - Effect effect = new CanAttackAsThoughItDidntHaveDefenderSourceEffect(Duration.EndOfTurn); - effect.setText("and can attack this turn as though it didn't have defender"); - Ability ability = new LimitedTimesPerTurnActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(3, -1, Duration.EndOfTurn), new ManaCostsImpl("{3}")); - ability.addEffect(effect); + Ability ability = new LimitedTimesPerTurnActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(3, -1, Duration.EndOfTurn), new GenericManaCost(3)); + ability.addEffect(new CanAttackAsThoughItDidntHaveDefenderSourceEffect(Duration.EndOfTurn, "and")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/m/MobilizerMech.java b/Mage.Sets/src/mage/cards/m/MobilizerMech.java new file mode 100644 index 00000000000..199f37bab0e --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MobilizerMech.java @@ -0,0 +1,94 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.common.continuous.AddCardTypeTargetEffect; +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.Duration; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MobilizerMech extends CardImpl { + + public MobilizerMech(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}{U}"); + + this.subtype.add(SubType.VEHICLE); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever Mobilizer Mech becomes crewed, up to one other target Vehicle you control becomes an artifact creature until end of turn. + this.addAbility(new MobilizerMechTriggeredAbility()); + + // Crew 3 + this.addAbility(new CrewAbility(3)); + } + + private MobilizerMech(final MobilizerMech card) { + super(card); + } + + @Override + public MobilizerMech copy() { + return new MobilizerMech(this); + } +} + +class MobilizerMechTriggeredAbility extends TriggeredAbilityImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent( + SubType.VEHICLE, "up to one other Vehicle you control" + ); + + static { + filter.add(AnotherPredicate.instance); + } + + MobilizerMechTriggeredAbility() { + super(Zone.BATTLEFIELD, new AddCardTypeTargetEffect(Duration.EndOfTurn, CardType.ARTIFACT, CardType.CREATURE)); + this.addTarget(new TargetPermanent(0, 1, filter)); + } + + private MobilizerMechTriggeredAbility(final MobilizerMechTriggeredAbility ability) { + super(ability); + } + + @Override + public MobilizerMechTriggeredAbility copy() { + return new MobilizerMechTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.VEHICLE_CREWED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return event.getSourceId().equals(getSourceId()); + } + + @Override + public String getRule() { + return "Whenever {this} becomes crewed, up to one other target Vehicle " + + "you control becomes an artifact creature until end of turn."; + } +} diff --git a/Mage.Sets/src/mage/cards/m/MoggInfestation.java b/Mage.Sets/src/mage/cards/m/MoggInfestation.java index 87ec162f9f3..26665315aef 100644 --- a/Mage.Sets/src/mage/cards/m/MoggInfestation.java +++ b/Mage.Sets/src/mage/cards/m/MoggInfestation.java @@ -1,4 +1,3 @@ - package mage.cards.m; import java.util.UUID; @@ -6,8 +5,11 @@ import mage.abilities.Ability; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenTargetEffect; +import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; @@ -62,16 +64,27 @@ class MoggInfestationEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null && getTargetPointer().getFirst(game, source) != null) { + Cards creaturesDied = new CardsImpl(); + if (controller != null + && getTargetPointer().getFirst(game, source) != null) { for (Permanent permanent : game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, getTargetPointer().getFirst(game, source), game)) { if (permanent.destroy(source, game, false)) { if (game.getState().getZone(permanent.getId()) == Zone.GRAVEYARD) { // If a commander is replaced to command zone, the creature does not die - Effect effect = new CreateTokenTargetEffect(new GoblinToken(), 2); - effect.setTargetPointer(getTargetPointer()); - effect.apply(game, source); + creaturesDied.add(permanent); } } } + game.getState().processAction(game); // Bug #8548 + if (creaturesDied.isEmpty()) { + return true; + } + for (Card c : creaturesDied.getCards(game)) { + if (game.getState().getZone(c.getId()) == Zone.GRAVEYARD) { + Effect effect = new CreateTokenTargetEffect(new GoblinToken(), 2); + effect.setTargetPointer(getTargetPointer()); + effect.apply(game, source); + } + } return true; } return false; diff --git a/Mage.Sets/src/mage/cards/m/MoggManiac.java b/Mage.Sets/src/mage/cards/m/MoggManiac.java index 2e8bb092569..a94a09fa628 100644 --- a/Mage.Sets/src/mage/cards/m/MoggManiac.java +++ b/Mage.Sets/src/mage/cards/m/MoggManiac.java @@ -27,7 +27,7 @@ public final class MoggManiac extends CardImpl { this.toughness = new MageInt(1); // Whenever Mogg Maniac is dealt damage, it deals that much damage to target opponent. - Ability ability = new DealtDamageToSourceTriggeredAbility(new MoggManiacDealDamageEffect(), false, false, true); + Ability ability = new DealtDamageToSourceTriggeredAbility(new MoggManiacDealDamageEffect(), false, false); ability.addTarget(new TargetOpponentOrPlaneswalker()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/m/MoldgrafMillipede.java b/Mage.Sets/src/mage/cards/m/MoldgrafMillipede.java new file mode 100644 index 00000000000..7b3041b3af3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MoldgrafMillipede.java @@ -0,0 +1,45 @@ +package mage.cards.m; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; +import mage.abilities.effects.common.MillCardsControllerEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.counters.CounterType; +import mage.filter.StaticFilters; + +/** + * + * @author weirddan455 + */ +public final class MoldgrafMillipede extends CardImpl { + + public MoldgrafMillipede(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}"); + + this.subtype.add(SubType.INSECT); + this.subtype.add(SubType.HORROR); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // When Moldgraf Millipede enters the battlefield, mill three cards, then put a +1/+1 counter on Moldgraf Millipede for each creature card in your graveyard. + Ability ability = new EntersBattlefieldTriggeredAbility(new MillCardsControllerEffect(3)); + ability.addEffect(new AddCountersSourceEffect(CounterType.P1P1.createInstance(), new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_CREATURE), false).concatBy(", then")); + this.addAbility(ability); + } + + private MoldgrafMillipede(final MoldgrafMillipede card) { + super(card); + } + + @Override + public MoldgrafMillipede copy() { + return new MoldgrafMillipede(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MoltenBirth.java b/Mage.Sets/src/mage/cards/m/MoltenBirth.java index b6499b04e56..3400c0b7edc 100644 --- a/Mage.Sets/src/mage/cards/m/MoltenBirth.java +++ b/Mage.Sets/src/mage/cards/m/MoltenBirth.java @@ -11,7 +11,7 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; -import mage.game.permanent.token.MoltenBirthElementalToken; +import mage.game.permanent.token.RedElementalToken; import mage.players.Player; import java.util.UUID; @@ -25,7 +25,7 @@ public final class MoltenBirth extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{R}{R}"); // Create two 1/1 red Elemental creature tokens. Then flip a coin. If you win the flip, return Molten Birth to its owner's hand. - this.getSpellAbility().addEffect(new CreateTokenEffect(new MoltenBirthElementalToken(), 2)); + this.getSpellAbility().addEffect(new CreateTokenEffect(new RedElementalToken(), 2)); this.getSpellAbility().addEffect(new MoltenBirthEffect()); } diff --git a/Mage.Sets/src/mage/cards/m/MoltenEchoes.java b/Mage.Sets/src/mage/cards/m/MoltenEchoes.java index 6125a7f4c79..46f9a9a048f 100644 --- a/Mage.Sets/src/mage/cards/m/MoltenEchoes.java +++ b/Mage.Sets/src/mage/cards/m/MoltenEchoes.java @@ -82,7 +82,7 @@ class MoltenEchoesEffect extends OneShotEffect { CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(null, null, true); effect.setTargetPointer(getTargetPointer()); if (effect.apply(game, source)) { - for (Permanent tokenPermanent : effect.getAddedPermanent()) { + for (Permanent tokenPermanent : effect.getAddedPermanents()) { ExileTargetEffect exileEffect = new ExileTargetEffect(); exileEffect.setTargetPointer(new FixedTarget(tokenPermanent, game)); DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); diff --git a/Mage.Sets/src/mage/cards/m/MoltenPrimordial.java b/Mage.Sets/src/mage/cards/m/MoltenPrimordial.java index f3426606bb8..09fa9b18417 100644 --- a/Mage.Sets/src/mage/cards/m/MoltenPrimordial.java +++ b/Mage.Sets/src/mage/cards/m/MoltenPrimordial.java @@ -100,11 +100,11 @@ class MoltenPrimordialEffect extends OneShotEffect { Permanent targetCreature = game.getPermanent(target.getFirstTarget()); if (targetCreature != null) { ContinuousEffect effect1 = new GainControlTargetEffect(Duration.EndOfTurn); - effect1.setTargetPointer(new FixedTarget(targetCreature.getId())); + effect1.setTargetPointer(new FixedTarget(targetCreature.getId(), game)); game.addEffect(effect1, source); ContinuousEffect effect2 = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn); - effect2.setTargetPointer(new FixedTarget(targetCreature.getId())); + effect2.setTargetPointer(new FixedTarget(targetCreature.getId(), game)); game.addEffect(effect2, source); targetCreature.untap(game); diff --git a/Mage.Sets/src/mage/cards/m/MoltensteelDragon.java b/Mage.Sets/src/mage/cards/m/MoltensteelDragon.java index ad9430c080a..141964fa61e 100644 --- a/Mage.Sets/src/mage/cards/m/MoltensteelDragon.java +++ b/Mage.Sets/src/mage/cards/m/MoltensteelDragon.java @@ -1,28 +1,25 @@ - package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.mana.PhyrexianManaCost; +import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.ColoredManaSymbol; import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.SubType; + +import java.util.UUID; /** - * * @author North */ public final class MoltensteelDragon extends CardImpl { public MoltensteelDragon(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{4}{R/P}{R/P}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{4}{R/P}{R/P}"); this.subtype.add(SubType.PHYREXIAN); this.subtype.add(SubType.DRAGON); @@ -32,9 +29,9 @@ public final class MoltensteelDragon extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, - new BoostSourceEffect(1, 0, Duration.EndOfTurn), - new PhyrexianManaCost(ColoredManaSymbol.R))); + this.addAbility(new SimpleActivatedAbility(new BoostSourceEffect( + 1, 0, Duration.EndOfTurn + ), new ManaCostsImpl<>("{R/P}"))); } private MoltensteelDragon(final MoltensteelDragon card) { diff --git a/Mage.Sets/src/mage/cards/m/MondronenShaman.java b/Mage.Sets/src/mage/cards/m/MondronenShaman.java index 2bedb848b58..8683524fe7d 100644 --- a/Mage.Sets/src/mage/cards/m/MondronenShaman.java +++ b/Mage.Sets/src/mage/cards/m/MondronenShaman.java @@ -24,7 +24,6 @@ public final class MondronenShaman extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(2); - this.transformable = true; this.secondSideCardClazz = mage.cards.t.TovolarsMagehunter.class; // At the beginning of each upkeep, if no spells were cast last turn, transform Mondronen Shaman. diff --git a/Mage.Sets/src/mage/cards/m/MonkeyCage.java b/Mage.Sets/src/mage/cards/m/MonkeyCage.java index f146d6141e6..206b4325c6d 100644 --- a/Mage.Sets/src/mage/cards/m/MonkeyCage.java +++ b/Mage.Sets/src/mage/cards/m/MonkeyCage.java @@ -13,7 +13,7 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SetTargetPointer; import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.token.ApeToken; @@ -29,7 +29,7 @@ public final class MonkeyCage extends CardImpl { // When a creature enters the battlefield, sacrifice Monkey Cage and create X 2/2 green Ape creature tokens, where X is that creature's converted mana cost. Ability ability = new EntersBattlefieldAllTriggeredAbility(Zone.BATTLEFIELD, new SacrificeSourceEffect(), - new FilterCreaturePermanent("a creature"), false, SetTargetPointer.PERMANENT, ""); + StaticFilters.FILTER_PERMANENT_A_CREATURE, false, SetTargetPointer.PERMANENT, ""); ability.addEffect(new MonkeyCageEffect()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/m/MoodmarkPainter.java b/Mage.Sets/src/mage/cards/m/MoodmarkPainter.java index 802d68d6ea4..e850d17c7f5 100644 --- a/Mage.Sets/src/mage/cards/m/MoodmarkPainter.java +++ b/Mage.Sets/src/mage/cards/m/MoodmarkPainter.java @@ -32,22 +32,22 @@ public final class MoodmarkPainter extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(3); - // Undergrowth — When Moodmark Painter enters the battlefield, target creature gains menace and gets +X/+0 until end of turn, where X is the number of creature cards in your graveyard. - DynamicValue xValue = new CardsInControllerGraveyardCount( - StaticFilters.FILTER_CARD_CREATURE - ); + // Undergrowth — When Moodmark Painter enters the battlefield, Ability ability = new EntersBattlefieldTriggeredAbility( new GainAbilityTargetEffect( - new MenaceAbility(), + new MenaceAbility(false), Duration.EndOfTurn - ).setText("target creature gains menace"), + ).setText("target creature gains menace."), false); + // target creature gains menace and gets +X/+0 until end of turn, + // where X is the number of creature cards in your graveyard. + DynamicValue xValue = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_CREATURE); ability.addEffect(new BoostTargetEffect( xValue, StaticValue.get(0), Duration.EndOfTurn, true ).setText("and gets +X/+0 until end of turn, " - + "where X is the number of creature cards in your graveyard") - ); + + "where X is the number of creature cards in your graveyard. " + + "(It can't be blocked except by two or more creatures.)")); // Must be here to match Oracle text ability.addTarget(new TargetCreaturePermanent()); ability.withFlavorWord("Undergrowth"); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/m/MoonCircuitHacker.java b/Mage.Sets/src/mage/cards/m/MoonCircuitHacker.java new file mode 100644 index 00000000000..793733e416d --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MoonCircuitHacker.java @@ -0,0 +1,65 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.discard.DiscardControllerEffect; +import mage.abilities.keyword.NinjutsuAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.Game; +import mage.game.permanent.Permanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MoonCircuitHacker extends CardImpl { + + public MoonCircuitHacker(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{1}{U}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.NINJA); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Ninjutsu {U} + this.addAbility(new NinjutsuAbility("{U}")); + + // Whenever Moon-Circuit Hacker deals combat damage to a player, you may draw a card. If you do, discard a card unless Moon-Circuit Hacker entered the battlefield this turn. + Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility( + new DrawCardSourceControllerEffect(1), true + ); + ability.addEffect(new ConditionalOneShotEffect( + new DiscardControllerEffect(1), MoonCircuitHackerCondition.instance, + "If you do, discard a card unless {this} entered the battlefield this turn" + )); + this.addAbility(ability); + } + + private MoonCircuitHacker(final MoonCircuitHacker card) { + super(card); + } + + @Override + public MoonCircuitHacker copy() { + return new MoonCircuitHacker(this); + } +} + +enum MoonCircuitHackerCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = source.getSourcePermanentOrLKI(game); + return permanent == null || permanent.getTurnsOnBattlefield() > 0; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/m/MoonbladeShinobi.java b/Mage.Sets/src/mage/cards/m/MoonbladeShinobi.java index 20a2877ab33..a59ad34096d 100644 --- a/Mage.Sets/src/mage/cards/m/MoonbladeShinobi.java +++ b/Mage.Sets/src/mage/cards/m/MoonbladeShinobi.java @@ -27,7 +27,7 @@ public final class MoonbladeShinobi extends CardImpl { this.toughness = new MageInt(2); // Ninjutsu {2}{U} - this.addAbility(new NinjutsuAbility(new ManaCostsImpl("{2}{U}"))); + this.addAbility(new NinjutsuAbility("{2}{U}")); // Whenever Moonblade Shinobi deals combat damage to a player, create a 1/1 blue Illusion creature token with flying. this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/m/MoonfolkPuzzlemaker.java b/Mage.Sets/src/mage/cards/m/MoonfolkPuzzlemaker.java new file mode 100644 index 00000000000..8a15a7f6ead --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MoonfolkPuzzlemaker.java @@ -0,0 +1,42 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.common.BecomesTappedSourceTriggeredAbility; +import mage.abilities.effects.keyword.ScryEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MoonfolkPuzzlemaker extends CardImpl { + + public MoonfolkPuzzlemaker(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{2}{U}"); + + this.subtype.add(SubType.MOONFOLK); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(1); + this.toughness = new MageInt(4); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever Moonfolk Puzzlemaker becomes tapped, scry 1. + this.addAbility(new BecomesTappedSourceTriggeredAbility(new ScryEffect(1, false))); + } + + private MoonfolkPuzzlemaker(final MoonfolkPuzzlemaker card) { + super(card); + } + + @Override + public MoonfolkPuzzlemaker copy() { + return new MoonfolkPuzzlemaker(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MoonlitAmbusher.java b/Mage.Sets/src/mage/cards/m/MoonlitAmbusher.java new file mode 100644 index 00000000000..ee33eed54c2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MoonlitAmbusher.java @@ -0,0 +1,38 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.keyword.NightboundAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MoonlitAmbusher extends CardImpl { + + public MoonlitAmbusher(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); + + this.subtype.add(SubType.WEREWOLF); + this.power = new MageInt(6); + this.toughness = new MageInt(3); + this.color.setGreen(true); + this.nightCard = true; + + // Nightbound + this.addAbility(new NightboundAbility()); + } + + private MoonlitAmbusher(final MoonlitAmbusher card) { + super(card); + } + + @Override + public MoonlitAmbusher copy() { + return new MoonlitAmbusher(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/Moonmist.java b/Mage.Sets/src/mage/cards/m/Moonmist.java index e039f421f2d..a952836ca18 100644 --- a/Mage.Sets/src/mage/cards/m/Moonmist.java +++ b/Mage.Sets/src/mage/cards/m/Moonmist.java @@ -1,7 +1,5 @@ - package mage.cards.m; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.PreventAllDamageByAllPermanentsEffect; @@ -16,13 +14,15 @@ import mage.filter.predicate.Predicates; import mage.game.Game; import mage.game.permanent.Permanent; +import java.util.UUID; + /** - * * @author BetaSteward */ public final class Moonmist extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creatures other than Werewolves and Wolves"); + private static final FilterCreaturePermanent filter + = new FilterCreaturePermanent("creatures other than Werewolves and Wolves"); static { filter.add(Predicates.not(SubType.WEREWOLF.getPredicate())); @@ -30,8 +30,7 @@ public final class Moonmist extends CardImpl { } public Moonmist(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{G}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}"); // Transform all Humans. Prevent all combat damage that would be dealt this turn by creatures other than Werewolves and Wolves. this.getSpellAbility().addEffect(new MoonmistEffect()); @@ -67,11 +66,8 @@ class MoonmistEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - for (Permanent permanent: game.getBattlefield().getActivePermanents(filter, source.getControllerId(), game)) { - if (permanent.isTransformable()) { - permanent.transform(game); - game.informPlayers(permanent.getName() + " transforms into " + permanent.getSecondCardFace().getName()); - } + for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), game)) { + permanent.transform(source, game); } return true; } @@ -80,5 +76,4 @@ class MoonmistEffect extends OneShotEffect { public MoonmistEffect copy() { return new MoonmistEffect(this); } - } diff --git a/Mage.Sets/src/mage/cards/m/MoonrageBrute.java b/Mage.Sets/src/mage/cards/m/MoonrageBrute.java index bbd90c57b43..eb754cd791e 100644 --- a/Mage.Sets/src/mage/cards/m/MoonrageBrute.java +++ b/Mage.Sets/src/mage/cards/m/MoonrageBrute.java @@ -24,7 +24,6 @@ public final class MoonrageBrute extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(3); this.color.setRed(true); - this.transformable = true; this.nightCard = true; // First strike diff --git a/Mage.Sets/src/mage/cards/m/MoonragersSlash.java b/Mage.Sets/src/mage/cards/m/MoonragersSlash.java index 68b745297e6..a9167956ced 100644 --- a/Mage.Sets/src/mage/cards/m/MoonragersSlash.java +++ b/Mage.Sets/src/mage/cards/m/MoonragersSlash.java @@ -4,7 +4,7 @@ import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.NightCondition; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; -import mage.abilities.hint.common.NightHint; +import mage.abilities.hint.common.DayNightHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -24,7 +24,7 @@ public final class MoonragersSlash extends CardImpl { // This spell costs {2} less to cast if it's night. this.addAbility(new SimpleStaticAbility( Zone.ALL, new SpellCostReductionSourceEffect(2, NightCondition.instance) - ).addHint(NightHint.instance)); + ).addHint(DayNightHint.instance).setRuleAtTheTop(true)); // Moonrager's Slash deals 3 damage to any target. this.getSpellAbility().addEffect(new DamageTargetEffect(3)); diff --git a/Mage.Sets/src/mage/cards/m/MoonriseIntruder.java b/Mage.Sets/src/mage/cards/m/MoonriseIntruder.java index b765b98d6af..0d53062f18e 100644 --- a/Mage.Sets/src/mage/cards/m/MoonriseIntruder.java +++ b/Mage.Sets/src/mage/cards/m/MoonriseIntruder.java @@ -24,7 +24,6 @@ public final class MoonriseIntruder extends CardImpl { // this card is the second face of double-faced card this.nightCard = true; - this.transformable = true; // Menace this.addAbility(new MenaceAbility()); diff --git a/Mage.Sets/src/mage/cards/m/MoonscarredWerewolf.java b/Mage.Sets/src/mage/cards/m/MoonscarredWerewolf.java index c5ee197498b..7bc6a02be36 100644 --- a/Mage.Sets/src/mage/cards/m/MoonscarredWerewolf.java +++ b/Mage.Sets/src/mage/cards/m/MoonscarredWerewolf.java @@ -29,7 +29,6 @@ public final class MoonscarredWerewolf extends CardImpl { // this card is the second face of double-faced card this.nightCard = true; - this.transformable = true; this.addAbility(VigilanceAbility.getInstance()); diff --git a/Mage.Sets/src/mage/cards/m/MoonsilverSpear.java b/Mage.Sets/src/mage/cards/m/MoonsilverSpear.java index fab99b3941e..c32773fc6a2 100644 --- a/Mage.Sets/src/mage/cards/m/MoonsilverSpear.java +++ b/Mage.Sets/src/mage/cards/m/MoonsilverSpear.java @@ -1,4 +1,3 @@ - package mage.cards.m; import java.util.UUID; @@ -26,8 +25,10 @@ public final class MoonsilverSpear extends CardImpl { // Equipped creature has first strike. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(FirstStrikeAbility.getInstance(), AttachmentType.EQUIPMENT))); + // Whenever equipped creature attacks, create a 4/4 white Angel creature token with flying. this.addAbility(new AttacksAttachedTriggeredAbility(new CreateTokenEffect(new AngelToken()))); + // Equip {4} this.addAbility(new EquipAbility(Outcome.AddAbility, new GenericManaCost(4))); } diff --git a/Mage.Sets/src/mage/cards/m/MoonsnarePrototype.java b/Mage.Sets/src/mage/cards/m/MoonsnarePrototype.java new file mode 100644 index 00000000000..741687662af --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MoonsnarePrototype.java @@ -0,0 +1,91 @@ +package mage.cards.m; + +import mage.abilities.Ability; +import mage.abilities.costs.common.TapTargetCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.PutOnLibraryTargetEffect; +import mage.abilities.keyword.ChannelAbility; +import mage.abilities.mana.ColorlessManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.TappedPredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetControlledPermanent; +import mage.target.common.TargetNonlandPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MoonsnarePrototype extends CardImpl { + + private static final FilterControlledPermanent filter + = new FilterControlledPermanent("untapped artifact or creature you control"); + + static { + filter.add(TappedPredicate.UNTAPPED); + filter.add(Predicates.or( + CardType.ARTIFACT.getPredicate(), + CardType.CREATURE.getPredicate() + )); + } + + public MoonsnarePrototype(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{U}"); + + // {T}, Tap an untapped artifact or creature you control: Add {C}. + Ability ability = new ColorlessManaAbility(); + ability.addCost(new TapTargetCost(new TargetControlledPermanent(filter))); + this.addAbility(ability); + + // Channel — {4}{U}, Discard Moonsnare Prototype: The owner of target nonland permanent puts it on the top or bottom of their library. + ability = new ChannelAbility("{4}{U}", new MoonsnarePrototypeEffect()); + ability.addTarget(new TargetNonlandPermanent()); + this.addAbility(ability); + } + + private MoonsnarePrototype(final MoonsnarePrototype card) { + super(card); + } + + @Override + public MoonsnarePrototype copy() { + return new MoonsnarePrototype(this); + } +} + +class MoonsnarePrototypeEffect extends OneShotEffect { + + MoonsnarePrototypeEffect() { + super(Outcome.Benefit); + staticText = "the owner of target nonland permanent puts it on the top or bottom of their library"; + } + + private MoonsnarePrototypeEffect(final MoonsnarePrototypeEffect effect) { + super(effect); + } + + @Override + public MoonsnarePrototypeEffect copy() { + return new MoonsnarePrototypeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(game.getOwnerId(source.getFirstTarget())); + if (player == null) { + return false; + } + if (player.chooseUse(Outcome.Detriment, "Put the targeted object on the top or bottom of your library?", + "", "Top", "Bottom", source, game)) { + return new PutOnLibraryTargetEffect(true).apply(game, source); + } + return new PutOnLibraryTargetEffect(false).apply(game, source); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MoonsnareSpecialist.java b/Mage.Sets/src/mage/cards/m/MoonsnareSpecialist.java new file mode 100644 index 00000000000..8f4ed5ce2d7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MoonsnareSpecialist.java @@ -0,0 +1,46 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.abilities.keyword.NinjutsuAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MoonsnareSpecialist extends CardImpl { + + public MoonsnareSpecialist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.NINJA); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Ninjutsu {2}{U} + this.addAbility(new NinjutsuAbility("{2}{U}")); + + // When Moonsnare Specialist enters the battlefield, return up to one target creature to its owner's hand. + Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnToHandTargetEffect()); + ability.addTarget(new TargetCreaturePermanent(0, 1)); + this.addAbility(ability); + } + + private MoonsnareSpecialist(final MoonsnareSpecialist card) { + super(card); + } + + @Override + public MoonsnareSpecialist copy() { + return new MoonsnareSpecialist(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MoonveilRegent.java b/Mage.Sets/src/mage/cards/m/MoonveilRegent.java index b0e147769ec..8bc8b41f998 100644 --- a/Mage.Sets/src/mage/cards/m/MoonveilRegent.java +++ b/Mage.Sets/src/mage/cards/m/MoonveilRegent.java @@ -46,7 +46,7 @@ public final class MoonveilRegent extends CardImpl { new DrawCardSourceControllerEffect(MoonveilRegentSpellValue.instance) .setText("draw a card for each of that spell's colors"), new DiscardHandCost() - ), false)); + ), StaticFilters.FILTER_SPELL_A, false, true)); // When Moonveil Regent dies, it deals X damage to any target, where X is the number of colors among permanents you control. Ability ability = new DiesSourceTriggeredAbility(new DamageTargetEffect( diff --git a/Mage.Sets/src/mage/cards/m/Morale.java b/Mage.Sets/src/mage/cards/m/Morale.java index d3d668d01f6..e53d2b59d55 100644 --- a/Mage.Sets/src/mage/cards/m/Morale.java +++ b/Mage.Sets/src/mage/cards/m/Morale.java @@ -1,4 +1,3 @@ - package mage.cards.m; import java.util.UUID; @@ -7,7 +6,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.common.FilterAttackingCreature; +import mage.filter.StaticFilters; /** * @@ -18,10 +17,8 @@ public final class Morale extends CardImpl { public Morale(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}{W}"); - // Attacking creatures get +1/+1 until end of turn. - this.getSpellAbility().addEffect(new BoostAllEffect(1, 1, Duration.EndOfTurn, new FilterAttackingCreature("Attacking creatures"), false)); - + this.getSpellAbility().addEffect(new BoostAllEffect(1, 1, Duration.EndOfTurn, StaticFilters.FILTER_ATTACKING_CREATURES, false)); } private Morale(final Morale card) { diff --git a/Mage.Sets/src/mage/cards/m/MorbidPlunder.java b/Mage.Sets/src/mage/cards/m/MorbidPlunder.java index d36a0a1220e..6a151cdcc6c 100644 --- a/Mage.Sets/src/mage/cards/m/MorbidPlunder.java +++ b/Mage.Sets/src/mage/cards/m/MorbidPlunder.java @@ -1,4 +1,3 @@ - package mage.cards.m; import java.util.UUID; @@ -6,7 +5,7 @@ import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterCreatureCard; +import mage.filter.StaticFilters; import mage.target.common.TargetCardInYourGraveyard; /** @@ -21,7 +20,7 @@ public final class MorbidPlunder extends CardImpl { // Return up to two target creature cards from your graveyard to your hand. this.getSpellAbility().addEffect(new ReturnFromGraveyardToHandTargetEffect()); - this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, 2, new FilterCreatureCard("creature cards from your graveyard"))); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, 2, StaticFilters.FILTER_CARD_CREATURES_YOUR_GRAVEYARD)); } private MorbidPlunder(final MorbidPlunder card) { diff --git a/Mage.Sets/src/mage/cards/m/Mordenkainen.java b/Mage.Sets/src/mage/cards/m/Mordenkainen.java index e55f74548f2..76b69d4501f 100644 --- a/Mage.Sets/src/mage/cards/m/Mordenkainen.java +++ b/Mage.Sets/src/mage/cards/m/Mordenkainen.java @@ -4,7 +4,6 @@ import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.GetEmblemEffect; @@ -27,7 +26,7 @@ public final class Mordenkainen extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.MORDENKAINEN); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +2: Draw two cards, then put a card from your hand on the bottom of your library. this.addAbility(new LoyaltyAbility(new MordenkainenDrawEffect(), 2)); diff --git a/Mage.Sets/src/mage/cards/m/MorningApparition.java b/Mage.Sets/src/mage/cards/m/MorningApparition.java index 2ab7a6c2582..3b04cf354d8 100644 --- a/Mage.Sets/src/mage/cards/m/MorningApparition.java +++ b/Mage.Sets/src/mage/cards/m/MorningApparition.java @@ -25,7 +25,6 @@ public final class MorningApparition extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(1); this.color.setWhite(true); - this.transformable = true; this.nightCard = true; // Flying diff --git a/Mage.Sets/src/mage/cards/m/MothriderPatrol.java b/Mage.Sets/src/mage/cards/m/MothriderPatrol.java new file mode 100644 index 00000000000..7b42217efbb --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MothriderPatrol.java @@ -0,0 +1,49 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MothriderPatrol extends CardImpl { + + public MothriderPatrol(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}"); + + this.subtype.add(SubType.FOX); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // {3}{W}, {T}: Tap target creature. + Ability ability = new SimpleActivatedAbility(new TapTargetEffect(), new ManaCostsImpl<>("{3}{W}")); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + private MothriderPatrol(final MothriderPatrol card) { + super(card); + } + + @Override + public MothriderPatrol copy() { + return new MothriderPatrol(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MourningPatrol.java b/Mage.Sets/src/mage/cards/m/MourningPatrol.java index 8065c06f1f8..d04ce198bd0 100644 --- a/Mage.Sets/src/mage/cards/m/MourningPatrol.java +++ b/Mage.Sets/src/mage/cards/m/MourningPatrol.java @@ -3,7 +3,6 @@ package mage.cards.m; import mage.MageInt; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.keyword.DisturbAbility; -import mage.abilities.keyword.TransformAbility; import mage.abilities.keyword.VigilanceAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -24,15 +23,13 @@ public final class MourningPatrol extends CardImpl { this.subtype.add(SubType.SOLDIER); this.power = new MageInt(2); this.toughness = new MageInt(3); - this.transformable = true; this.secondSideCardClazz = mage.cards.m.MorningApparition.class; // Vigilance this.addAbility(VigilanceAbility.getInstance()); // Disturb {3}{W} - this.addAbility(new TransformAbility()); - this.addAbility(new DisturbAbility(new ManaCostsImpl<>("{3}{W}"))); + this.addAbility(new DisturbAbility(this, "{3}{W}")); } private MourningPatrol(final MourningPatrol card) { diff --git a/Mage.Sets/src/mage/cards/m/MuYanling.java b/Mage.Sets/src/mage/cards/m/MuYanling.java index 0745a98712c..60f0889275b 100644 --- a/Mage.Sets/src/mage/cards/m/MuYanling.java +++ b/Mage.Sets/src/mage/cards/m/MuYanling.java @@ -4,7 +4,6 @@ package mage.cards.m; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.combat.CantBeBlockedTargetEffect; @@ -15,8 +14,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -33,7 +31,7 @@ public final class MuYanling extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.YANLING); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +2: Target creature can't be blocked this turn. LoyaltyAbility ability = new LoyaltyAbility(new CantBeBlockedTargetEffect(), 2); @@ -59,12 +57,6 @@ public final class MuYanling extends CardImpl { class MuYanlingEffect extends OneShotEffect { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public MuYanlingEffect() { super(Outcome.Tap); staticText = "tap all creatures your opponents control. You take an extra turn after this one."; @@ -80,7 +72,7 @@ class MuYanlingEffect extends OneShotEffect { if (player == null) { return false; } - for (Permanent creature : game.getBattlefield().getActivePermanents(filter, player.getId(), source.getSourceId(), game)) { + for (Permanent creature : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE, player.getId(), source.getSourceId(), game)) { creature.tap(source, game); } return new AddExtraTurnControllerEffect().apply(game, source); diff --git a/Mage.Sets/src/mage/cards/m/MuYanlingCelestialWind.java b/Mage.Sets/src/mage/cards/m/MuYanlingCelestialWind.java index 709b92c774a..1b9abc9ce02 100644 --- a/Mage.Sets/src/mage/cards/m/MuYanlingCelestialWind.java +++ b/Mage.Sets/src/mage/cards/m/MuYanlingCelestialWind.java @@ -2,7 +2,6 @@ package mage.cards.m; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.abilities.effects.common.continuous.BoostAllEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; @@ -33,7 +32,7 @@ public final class MuYanlingCelestialWind extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.YANLING); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +1: Until your next turn, up to one target creature gets -5/-0. Ability ability = new LoyaltyAbility(new BoostTargetEffect( diff --git a/Mage.Sets/src/mage/cards/m/MuYanlingSkyDancer.java b/Mage.Sets/src/mage/cards/m/MuYanlingSkyDancer.java index b137052617c..25c122b8709 100644 --- a/Mage.Sets/src/mage/cards/m/MuYanlingSkyDancer.java +++ b/Mage.Sets/src/mage/cards/m/MuYanlingSkyDancer.java @@ -2,7 +2,6 @@ package mage.cards.m; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.GetEmblemEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; @@ -30,7 +29,7 @@ public final class MuYanlingSkyDancer extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.YANLING); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(2)); + this.setStartingLoyalty(2); // +2: Until your next turn, up to one target creature gets -2/-0 and loses flying. Ability ability = new LoyaltyAbility(new BoostTargetEffect( diff --git a/Mage.Sets/src/mage/cards/m/MukotaiAmbusher.java b/Mage.Sets/src/mage/cards/m/MukotaiAmbusher.java new file mode 100644 index 00000000000..c9096a73673 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MukotaiAmbusher.java @@ -0,0 +1,41 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.keyword.LifelinkAbility; +import mage.abilities.keyword.NinjutsuAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MukotaiAmbusher extends CardImpl { + + public MukotaiAmbusher(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}{B}"); + + this.subtype.add(SubType.RAT); + this.subtype.add(SubType.NINJA); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // Ninjutsu {1}{B} + this.addAbility(new NinjutsuAbility("{1}{B}")); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + } + + private MukotaiAmbusher(final MukotaiAmbusher card) { + super(card); + } + + @Override + public MukotaiAmbusher copy() { + return new MukotaiAmbusher(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MukotaiSoulripper.java b/Mage.Sets/src/mage/cards/m/MukotaiSoulripper.java new file mode 100644 index 00000000000..3c4f0a92fd7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MukotaiSoulripper.java @@ -0,0 +1,65 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.CrewAbility; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MukotaiSoulripper extends CardImpl { + + private static final FilterControlledPermanent filter + = new FilterControlledPermanent("another artifact or creature"); + + static { + filter.add(Predicates.or( + CardType.ARTIFACT.getPredicate(), + CardType.CREATURE.getPredicate() + )); + } + + public MukotaiSoulripper(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}{B}"); + + this.subtype.add(SubType.VEHICLE); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + + // Whenever Mukotai Soulripper attacks, you may sacrifice another artifact or creature. If you do, put a +1/+1 counter on Mukotai Soulripper and it gains menace until end of turn. + this.addAbility(new AttacksTriggeredAbility(new DoIfCostPaid( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), + new SacrificeTargetCost(new TargetControlledPermanent(filter)) + ).addEffect(new GainAbilitySourceEffect( + new MenaceAbility(false), Duration.EndOfTurn + ).setText("and it gains menace until end of turn")))); + + // Crew 2 + this.addAbility(new CrewAbility(2)); + } + + private MukotaiSoulripper(final MukotaiSoulripper card) { + super(card); + } + + @Override + public MukotaiSoulripper copy() { + return new MukotaiSoulripper(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MultanisDecree.java b/Mage.Sets/src/mage/cards/m/MultanisDecree.java index ea4a5a81af7..62c174b36c2 100644 --- a/Mage.Sets/src/mage/cards/m/MultanisDecree.java +++ b/Mage.Sets/src/mage/cards/m/MultanisDecree.java @@ -57,7 +57,7 @@ class MultanisDecreeDestroyEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); int enchantmentsDestoyed = 0; - for (Permanent permanent: game.getState().getBattlefield().getActivePermanents(StaticFilters.FILTER_ENCHANTMENT_PERMANENT, source.getControllerId(), source.getSourceId(), game)) { + for (Permanent permanent: game.getState().getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_ENCHANTMENT, source.getControllerId(), source.getSourceId(), game)) { if (permanent.destroy(source, game, false)) { enchantmentsDestoyed++; } diff --git a/Mage.Sets/src/mage/cards/m/MurasaBehemoth.java b/Mage.Sets/src/mage/cards/m/MurasaBehemoth.java index bcac144b857..d3303cbecd1 100644 --- a/Mage.Sets/src/mage/cards/m/MurasaBehemoth.java +++ b/Mage.Sets/src/mage/cards/m/MurasaBehemoth.java @@ -21,7 +21,7 @@ import java.util.UUID; */ public final class MurasaBehemoth extends CardImpl { - Condition condition = new CardsInControllerGraveyardCondition(1, StaticFilters.FILTER_CARD_LAND); + private static final Condition condition = new CardsInControllerGraveyardCondition(1, StaticFilters.FILTER_CARD_LAND); public MurasaBehemoth(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}{G}"); diff --git a/Mage.Sets/src/mage/cards/m/MurderousBetrayal.java b/Mage.Sets/src/mage/cards/m/MurderousBetrayal.java index b46b6dd5a58..8f599922719 100644 --- a/Mage.Sets/src/mage/cards/m/MurderousBetrayal.java +++ b/Mage.Sets/src/mage/cards/m/MurderousBetrayal.java @@ -1,8 +1,6 @@ - package mage.cards.m; import java.util.UUID; -import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.Cost; @@ -14,11 +12,8 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ColorPredicate; +import mage.filter.StaticFilters; import mage.game.Game; -import mage.game.events.GameEvent; import mage.players.Player; import mage.target.common.TargetCreaturePermanent; import mage.util.CardUtil; @@ -29,12 +24,6 @@ import mage.util.CardUtil; */ public final class MurderousBetrayal extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public MurderousBetrayal(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{B}{B}{B}"); @@ -42,7 +31,7 @@ public final class MurderousBetrayal extends CardImpl { Effect effect = new DestroyTargetEffect(true); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new MurderousBetrayalCost()); ability.addCost(new ManaCostsImpl("{B}{B}")); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/m/MurderousSpoils.java b/Mage.Sets/src/mage/cards/m/MurderousSpoils.java index d86ae5b5365..3087cecbd58 100644 --- a/Mage.Sets/src/mage/cards/m/MurderousSpoils.java +++ b/Mage.Sets/src/mage/cards/m/MurderousSpoils.java @@ -1,10 +1,8 @@ - package mage.cards.m; import java.util.ArrayList; import java.util.List; import java.util.UUID; -import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; @@ -15,9 +13,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Outcome; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ColorPredicate; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; @@ -28,20 +24,14 @@ import mage.target.targetpointer.FixedTarget; * @author wetterlicht */ public final class MurderousSpoils extends CardImpl { - - private static final FilterCreaturePermanent FILTER = new FilterCreaturePermanent("nonblack creature"); - - static { - FILTER.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } public MurderousSpoils(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{5}{B}"); // Destroy target nonblack creature. It can't be regenerated. You gain control of all Equipment that was attached to it. - this.getSpellAbility().addTarget(new TargetCreaturePermanent(FILTER)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); this.getSpellAbility().addEffect(new MurderousSpoilsEffect()); - + } private MurderousSpoils(final MurderousSpoils card) { diff --git a/Mage.Sets/src/mage/cards/m/MuscleBurst.java b/Mage.Sets/src/mage/cards/m/MuscleBurst.java index b77fb961938..afb7881ac63 100644 --- a/Mage.Sets/src/mage/cards/m/MuscleBurst.java +++ b/Mage.Sets/src/mage/cards/m/MuscleBurst.java @@ -1,11 +1,12 @@ package mage.cards.m; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.AdditiveDynamicValue; +import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.CardsInAllGraveyardsCount; -import mage.abilities.effects.Effect; +import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.common.InfoEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.cards.CardImpl; @@ -17,11 +18,11 @@ import mage.filter.FilterCard; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.AbilityPredicate; import mage.filter.predicate.mageobject.NamePredicate; -import mage.game.Game; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LoneFox */ public final class MuscleBurst extends CardImpl { @@ -29,18 +30,23 @@ public final class MuscleBurst extends CardImpl { private static final FilterCard filter = new FilterCard(); static { - filter.add(Predicates.or(new NamePredicate("Muscle Burst"), - new AbilityPredicate(CountAsMuscleBurstAbility.class))); + filter.add(Predicates.or( + new NamePredicate("Muscle Burst"), + new AbilityPredicate(CountAsMuscleBurstAbility.class) + )); } + private static final DynamicValue xValue = new AdditiveDynamicValue( + new CardsInAllGraveyardsCount(filter), StaticValue.get(3) + ); + public MuscleBurst(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}"); // Target creature gets +X/+X until end of turn, where X is 3 plus the number of cards named Muscle Burst in all graveyards. - MuscleBurstCount count = new MuscleBurstCount(filter); - Effect effect = new BoostTargetEffect(count, count, Duration.EndOfTurn, true); - effect.setText("Target creature gets +X/+X until end of turn, where X is 3 plus the number of cards named Muscle Burst in all graveyards."); - this.getSpellAbility().addEffect(effect); + this.getSpellAbility().addEffect(new BoostTargetEffect( + xValue, xValue, Duration.EndOfTurn, true + ).setText("Target creature gets +X/+X until end of turn, where X is 3 plus the number of cards named Muscle Burst in all graveyards.")); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } @@ -58,28 +64,6 @@ public final class MuscleBurst extends CardImpl { } } -class MuscleBurstCount extends CardsInAllGraveyardsCount { - - public MuscleBurstCount(FilterCard filter) { - super(filter); - } - - private MuscleBurstCount(MuscleBurstCount value) { - super(value); - } - - @Override - public MuscleBurstCount copy() { - return new MuscleBurstCount(this); - } - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - return super.calculate(game, sourceAbility, effect) + 3; - } - -} - class CountAsMuscleBurstAbility extends SimpleStaticAbility { public CountAsMuscleBurstAbility() { diff --git a/Mage.Sets/src/mage/cards/m/MuseVortex.java b/Mage.Sets/src/mage/cards/m/MuseVortex.java index 4bfc8c5561d..7545bab7454 100644 --- a/Mage.Sets/src/mage/cards/m/MuseVortex.java +++ b/Mage.Sets/src/mage/cards/m/MuseVortex.java @@ -9,12 +9,10 @@ import mage.constants.ComparisonType; import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.StaticFilters; import mage.filter.common.FilterInstantOrSorceryCard; import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; -import mage.target.TargetCard; import mage.target.common.TargetCardInExile; import java.util.UUID; @@ -45,10 +43,10 @@ class MuseVortexEffect extends OneShotEffect { MuseVortexEffect() { super(Outcome.Benefit); - staticText = "exile the top X cards of your library. You may cast an instant or sorcery spell " + - "with mana value X or less from among them without paying its mana cost. " + - "Then put the exiled instant and sorcery cards that weren't cast this way into your hand " + - "and the rest on the bottom of your library in a random order"; + staticText = "exile the top X cards of your library. You may cast an instant or sorcery spell " + + "with mana value X or less from among them without paying its mana cost. " + + "Then put the exiled instant and sorcery cards that weren't cast this way into your hand " + + "and the rest on the bottom of your library in a random order"; } private MuseVortexEffect(final MuseVortexEffect effect) { @@ -62,35 +60,36 @@ class MuseVortexEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player == null) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { return false; } int xValue = source.getManaCostsToPay().getX(); - Cards cards = new CardsImpl(player.getLibrary().getTopCards(game, xValue)); - player.moveCards(cards, Zone.EXILED, source, game); + Cards cards = new CardsImpl(controller.getLibrary().getTopCards(game, xValue)); + controller.moveCards(cards, Zone.EXILED, source, game); cards.retainZone(Zone.EXILED, game); - // TODO: this needs to be able to cast spells that aren't instant or sorcery cards (adventures/MDFCs) FilterCard filter = new FilterInstantOrSorceryCard("an instant or sorcery card with mana value " + xValue + " or less"); filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, xValue + 1)); - TargetCard target = new TargetCardInExile(0, 1, filter, null); + TargetCardInExile target = new TargetCardInExile(filter); target.setNotTarget(true); - player.choose(outcome, cards, target, game); - Card card = cards.get(target.getFirstTarget(), game); - if (card != null) { - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); - player.cast( - player.chooseAbilityForCast(card, game, true), - game, true, new ApprovingObject(source, game) - ); - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); + if (controller.choose(Outcome.Benefit, cards, target, game)) { + Card card = cards.get(target.getFirstTarget(), game); + if (card != null) { + game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); + Boolean cardWasCast = controller.cast(controller.chooseAbilityForCast(card, game, true), + game, true, new ApprovingObject(source, game)); + game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); + cards.remove(card); + if (cardWasCast) { + cards.remove(card); + } else { + game.informPlayer(controller, "You're not able to cast " + + card.getIdName() + " or you canceled the casting."); + } + controller.putCardsOnTopOfLibrary(cards, game, source, true); + return true; + } } - cards.retainZone(Zone.EXILED, game); - player.moveCards(new CardsImpl(cards.getCards( - StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY, game - )), Zone.HAND, source, game); - cards.retainZone(Zone.EXILED, game); - player.putCardsOnBottomOfLibrary(card, game, source, false); - return true; + return false; } } diff --git a/Mage.Sets/src/mage/cards/m/MutantsPrey.java b/Mage.Sets/src/mage/cards/m/MutantsPrey.java index 7d32150b716..bad72bb85fa 100644 --- a/Mage.Sets/src/mage/cards/m/MutantsPrey.java +++ b/Mage.Sets/src/mage/cards/m/MutantsPrey.java @@ -7,35 +7,22 @@ import mage.abilities.effects.common.FightTargetsEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.TargetController; -import mage.counters.CounterType; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; +import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; /** - * * @author LevelX2 */ - - public final class MutantsPrey extends CardImpl { - private static final FilterCreaturePermanent filter1 = new FilterCreaturePermanent("creature you control with a +1/+1 counter on it"); - private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent("creature an opponent controls"); - static { - filter1.add(TargetController.YOU.getControllerPredicate()); - filter1.add(CounterType.P1P1.getPredicate()); - filter2.add(TargetController.OPPONENT.getControllerPredicate()); - } - public MutantsPrey(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{G}"); - // Target creature you control with a +1/+1 counter on it fights target creature an opponent controls. this.getSpellAbility().addEffect(new FightTargetsEffect()); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter1)); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter2)); + this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent(StaticFilters.FILTER_CONTROLLED_CREATURE_P1P1)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); } private MutantsPrey(final MutantsPrey card) { diff --git a/Mage.Sets/src/mage/cards/m/Mutiny.java b/Mage.Sets/src/mage/cards/m/Mutiny.java index d908c3303af..aef8182e293 100644 --- a/Mage.Sets/src/mage/cards/m/Mutiny.java +++ b/Mage.Sets/src/mage/cards/m/Mutiny.java @@ -7,7 +7,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.TargetController; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.ControllerIdPredicate; @@ -24,18 +24,12 @@ import java.util.UUID; */ public final class Mutiny extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public Mutiny(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{R}"); // Target creature an opponent controls deals damage equal to its power to another target creature that player controls. this.getSpellAbility().addEffect(new MutinyEffect()); - this.getSpellAbility().addTarget(new MutinyFirstTarget(filter)); + this.getSpellAbility().addTarget(new MutinyFirstTarget(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); this.getSpellAbility().addTarget(new TargetCreaturePermanent(new FilterCreaturePermanent("another target creature that player controls"))); } diff --git a/Mage.Sets/src/mage/cards/m/MyojinOfBloomingDawn.java b/Mage.Sets/src/mage/cards/m/MyojinOfBloomingDawn.java new file mode 100644 index 00000000000..873c8850e0a --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MyojinOfBloomingDawn.java @@ -0,0 +1,64 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.condition.common.CastFromHandSourcePermanentCondition; +import mage.abilities.costs.common.RemoveCountersSourceCost; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.game.permanent.token.SpiritToken; +import mage.watchers.common.CastFromHandWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MyojinOfBloomingDawn extends CardImpl { + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(StaticFilters.FILTER_CONTROLLED_PERMANENT); + private static final Hint hint = new ValueHint("Permanents you control", xValue); + + public MyojinOfBloomingDawn(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{W}{W}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(4); + this.toughness = new MageInt(6); + + // Myojin of Blooming Dawn enters the battlefield with an indestructible counter on it if you cast it from your hand. + this.addAbility(new EntersBattlefieldAbility( + new AddCountersSourceEffect(CounterType.INDESTRUCTIBLE.createInstance()), + CastFromHandSourcePermanentCondition.instance, null, + "with an indestructible counter on it if you cast it from your hand" + ), new CastFromHandWatcher()); + + // Remove an indestructible counter from Myojin of Blooming Dawn: Create a 1/1 colorless Spirit creature token for each permanent you control. + this.addAbility(new SimpleActivatedAbility( + new CreateTokenEffect(new SpiritToken(), xValue), + new RemoveCountersSourceCost(CounterType.INDESTRUCTIBLE.createInstance()) + ).addHint(hint)); + } + + private MyojinOfBloomingDawn(final MyojinOfBloomingDawn card) { + super(card); + } + + @Override + public MyojinOfBloomingDawn copy() { + return new MyojinOfBloomingDawn(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MyojinOfCrypticDreams.java b/Mage.Sets/src/mage/cards/m/MyojinOfCrypticDreams.java new file mode 100644 index 00000000000..da089a8c8c3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MyojinOfCrypticDreams.java @@ -0,0 +1,79 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.condition.common.CastFromHandSourcePermanentCondition; +import mage.abilities.costs.common.RemoveCountersSourceCost; +import mage.abilities.effects.common.CopyTargetSpellEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.TargetController; +import mage.counters.CounterType; +import mage.filter.FilterSpell; +import mage.filter.predicate.Predicate; +import mage.game.Game; +import mage.game.stack.StackObject; +import mage.target.TargetSpell; +import mage.watchers.common.CastFromHandWatcher; + +import java.util.UUID; + +/** + * @author Alex-Vasile + */ +public class MyojinOfCrypticDreams extends CardImpl { + + private static final FilterSpell permanentSpellFilter = new FilterSpell("permanent spell you control"); + static { + permanentSpellFilter.add(TargetController.YOU.getControllerPredicate()); + permanentSpellFilter.add(MyojinOfCrypticDreamsPredicate.instance); + } + + public MyojinOfCrypticDreams(UUID ownderId, CardSetInfo setInfo) { + super(ownderId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{U}{U}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Myojin of Cryptic Dreams enters the battlefield with an indestructible counter on it if you cast it from your hand. + this.addAbility(new EntersBattlefieldAbility( + new AddCountersSourceEffect(CounterType.INDESTRUCTIBLE.createInstance()), + CastFromHandSourcePermanentCondition.instance, null, + "with an indestructible counter on it if you cast it from your hand" + ), new CastFromHandWatcher()); + + // Remove an indestructible counter from Myojin of Cryptic Dreams: + // Copy target permanent spell you control three times. (The copies become tokens.) + Ability ability = new SimpleActivatedAbility( + new CopyTargetSpellEffect(false, false, false) + .setText("Copy target permanent spell you control three times. (The copies become tokens.)"), + new RemoveCountersSourceCost(CounterType.INDESTRUCTIBLE.createInstance()) + ); + ability.addEffect(new CopyTargetSpellEffect(false, false, false).setText(" ")); + ability.addEffect(new CopyTargetSpellEffect(false, false, false).setText(" ")); + ability.addTarget(new TargetSpell(permanentSpellFilter)); + this.addAbility(ability); + } + + private MyojinOfCrypticDreams(final MyojinOfCrypticDreams card) { super(card); } + + @Override + public MyojinOfCrypticDreams copy() { return new MyojinOfCrypticDreams(this); } +} + +enum MyojinOfCrypticDreamsPredicate implements Predicate { + instance; + + @Override + public boolean apply(StackObject input, Game game) { + return input.isPermanent(game); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MyojinOfGrimBetrayal.java b/Mage.Sets/src/mage/cards/m/MyojinOfGrimBetrayal.java new file mode 100644 index 00000000000..21cc2a0feb6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MyojinOfGrimBetrayal.java @@ -0,0 +1,101 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.condition.common.CastFromHandSourcePermanentCondition; +import mage.abilities.costs.common.RemoveCountersSourceCost; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.CardsInAllGraveyardsCount; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.card.PutIntoGraveFromAnywhereThisTurnPredicate; +import mage.game.Game; +import mage.players.Player; +import mage.watchers.common.CardsPutIntoGraveyardWatcher; +import mage.watchers.common.CastFromHandWatcher; + +import java.util.UUID; + +/** +* @author Alex-Vasile +*/ +public class MyojinOfGrimBetrayal extends CardImpl { + + private static final FilterCreatureCard filter = new FilterCreatureCard(); + static { filter.add(PutIntoGraveFromAnywhereThisTurnPredicate.instance); } + private static final DynamicValue xValue = new CardsInAllGraveyardsCount(filter); + private static final Hint hint = new ValueHint("Permanents put into the graveyard this turn", xValue); + + public MyojinOfGrimBetrayal(UUID ownderId, CardSetInfo setInfo) { + super(ownderId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{B}{B}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(5); + this.toughness = new MageInt(2); + + // Myojin of Grim Betrayal enters the battlefield with an indestructible counter on it if you cast it from your hand. + this.addAbility(new EntersBattlefieldAbility( + new AddCountersSourceEffect(CounterType.INDESTRUCTIBLE.createInstance()), + CastFromHandSourcePermanentCondition.instance, null, + "with an indestructible counter on it if you cast it from your hand" + ), new CastFromHandWatcher()); + + // Remove an indestructible counter from Myojin of Grim Betrayal: + // Put onto the battlefield under your control all creature cards in all graveyards that were put there from anywhere this turn. + Ability ability = new SimpleActivatedAbility( + new MyojinOfGrimBetrayalEffect(filter), + new RemoveCountersSourceCost(CounterType.INDESTRUCTIBLE.createInstance()) + ).addHint(hint); + ability.addWatcher(new CardsPutIntoGraveyardWatcher()); + this.addAbility(ability); + } + + private MyojinOfGrimBetrayal(final MyojinOfGrimBetrayal card) { super(card); } + + @Override + public MyojinOfGrimBetrayal copy() {return new MyojinOfGrimBetrayal(this); } +} + +class MyojinOfGrimBetrayalEffect extends OneShotEffect { + + private final FilterCreatureCard filter; + + MyojinOfGrimBetrayalEffect(FilterCreatureCard filter) { + super(Outcome.PutCardInPlay); + this.filter = filter; + this.staticText = "Put onto the battlefield under your control all creature cards in all graveyards " + + "that were put there from anywhere this turn"; + } + + private MyojinOfGrimBetrayalEffect(final MyojinOfGrimBetrayalEffect effect) { + super(effect); + this.filter = effect.filter; + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + CardsPutIntoGraveyardWatcher watcher = game.getState().getWatcher(CardsPutIntoGraveyardWatcher.class); + if (controller == null || watcher == null) { return false; } + + Cards cards = new CardsImpl(watcher.getCardsPutIntoGraveyardFromBattlefield(game)); + cards.removeIf(uuid -> !game.getCard(uuid).isCreature(game)); + + return controller.moveCards(cards, Zone.BATTLEFIELD, source, game); + } + + @Override + public MyojinOfGrimBetrayalEffect copy() { return new MyojinOfGrimBetrayalEffect(this); } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/m/MyojinOfRoaringBlades.java b/Mage.Sets/src/mage/cards/m/MyojinOfRoaringBlades.java new file mode 100644 index 00000000000..4e44bae6b2d --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MyojinOfRoaringBlades.java @@ -0,0 +1,60 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.condition.common.CastFromHandSourcePermanentCondition; +import mage.abilities.costs.common.RemoveCountersSourceCost; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.CounterType; +import mage.target.common.TargetAnyTarget; +import mage.watchers.common.CastFromHandWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MyojinOfRoaringBlades extends CardImpl { + + public MyojinOfRoaringBlades(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{R}{R}{R}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(7); + this.toughness = new MageInt(4); + + // Myojin of Roaring Blades enters the battlefield with an indestructible counter on it if you cast it from your hand. + this.addAbility(new EntersBattlefieldAbility( + new AddCountersSourceEffect(CounterType.INDESTRUCTIBLE.createInstance()), + CastFromHandSourcePermanentCondition.instance, null, + "with an indestructible counter on it if you cast it from your hand" + ), new CastFromHandWatcher()); + + // Remove an indestructible counter from Myojin of Roaring Blades: It deals 7 damage to each of up to three targets. + Ability ability = new SimpleActivatedAbility( + new DamageTargetEffect(7) + .setText("it deals 7 damage to each of up to three targets"), + new RemoveCountersSourceCost(CounterType.INDESTRUCTIBLE.createInstance()) + ); + ability.addTarget(new TargetAnyTarget(0, 3)); + this.addAbility(ability); + } + + private MyojinOfRoaringBlades(final MyojinOfRoaringBlades card) { + super(card); + } + + @Override + public MyojinOfRoaringBlades copy() { + return new MyojinOfRoaringBlades(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MyojinOfToweringMight.java b/Mage.Sets/src/mage/cards/m/MyojinOfToweringMight.java new file mode 100644 index 00000000000..3b2234ad016 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MyojinOfToweringMight.java @@ -0,0 +1,66 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.condition.common.CastFromHandSourcePermanentCondition; +import mage.abilities.costs.common.RemoveCountersSourceCost; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.effects.common.counter.DistributeCountersEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.target.common.TargetCreaturePermanentAmount; +import mage.watchers.common.CastFromHandWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MyojinOfToweringMight extends CardImpl { + + public MyojinOfToweringMight(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{G}{G}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(8); + this.toughness = new MageInt(8); + + // Myojin of Towering Might enters the battlefield with an indestructible counter on it if you cast it from your hand. + this.addAbility(new EntersBattlefieldAbility( + new AddCountersSourceEffect(CounterType.INDESTRUCTIBLE.createInstance()), + CastFromHandSourcePermanentCondition.instance, null, + "with an indestructible counter on it if you cast it from your hand" + ), new CastFromHandWatcher()); + + // Remove an indestructible counter from Myojin of Towering Might: Distribute eight +1/+1 counters among any number of target creatures you control. They gain trample until end of turn. + Ability ability = new SimpleActivatedAbility(new DistributeCountersEffect( + CounterType.P1P1, 8, false, + "any number of target creatures you control" + ), new RemoveCountersSourceCost(CounterType.INDESTRUCTIBLE.createInstance())); + ability.addEffect(new GainAbilityTargetEffect( + TrampleAbility.getInstance(), Duration.EndOfTurn + ).setText("They gain trample until end of turn")); + ability.addTarget(new TargetCreaturePermanentAmount(8, StaticFilters.FILTER_CONTROLLED_CREATURES)); + this.addAbility(ability); + } + + private MyojinOfToweringMight(final MyojinOfToweringMight card) { + super(card); + } + + @Override + public MyojinOfToweringMight copy() { + return new MyojinOfToweringMight(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MyrBattlesphere.java b/Mage.Sets/src/mage/cards/m/MyrBattlesphere.java index 9dc9f7be711..d47e4bbdde6 100644 --- a/Mage.Sets/src/mage/cards/m/MyrBattlesphere.java +++ b/Mage.Sets/src/mage/cards/m/MyrBattlesphere.java @@ -81,7 +81,7 @@ class MyrBattlesphereTriggeredAbility extends TriggeredAbilityImpl { Permanent source = game.getPermanent(event.getSourceId()); if (source != null && source.getId().equals(this.getSourceId())) { UUID defenderId = game.getCombat().getDefenderId(event.getSourceId()); - this.getEffects().get(0).setTargetPointer(new FixedTarget(defenderId)); + this.getEffects().get(0).setTargetPointer(new FixedTarget(defenderId, game)); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/m/MysteriousTome.java b/Mage.Sets/src/mage/cards/m/MysteriousTome.java index 3458823bb07..100d607c828 100644 --- a/Mage.Sets/src/mage/cards/m/MysteriousTome.java +++ b/Mage.Sets/src/mage/cards/m/MysteriousTome.java @@ -21,7 +21,6 @@ public final class MysteriousTome extends CardImpl { public MysteriousTome(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{U}"); - this.transformable = true; this.secondSideCardClazz = mage.cards.c.ChillingChronicle.class; // {2}, {T}: Draw a card. Transform Mysterious Tome. @@ -30,7 +29,7 @@ public final class MysteriousTome extends CardImpl { new DrawCardSourceControllerEffect(1), new GenericManaCost(2) ); ability.addCost(new TapSourceCost()); - ability.addEffect(new TransformSourceEffect(true)); + ability.addEffect(new TransformSourceEffect()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/m/MysticDecree.java b/Mage.Sets/src/mage/cards/m/MysticDecree.java index 0922b3621a7..151cba2d414 100644 --- a/Mage.Sets/src/mage/cards/m/MysticDecree.java +++ b/Mage.Sets/src/mage/cards/m/MysticDecree.java @@ -1,4 +1,3 @@ - package mage.cards.m; import java.util.UUID; @@ -14,7 +13,7 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SuperType; import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; /** * @@ -27,11 +26,11 @@ public final class MysticDecree extends CardImpl { addSuperType(SuperType.WORLD); // All creatures lose flying and islandwalk. - Effect effect = new LoseAbilityAllEffect(FlyingAbility.getInstance(), Duration.WhileOnBattlefield, new FilterCreaturePermanent("All creatures")); + Effect effect = new LoseAbilityAllEffect(FlyingAbility.getInstance(), Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_ALL_CREATURES); effect.setText("All creatures lose flying"); - Effect effect2 = new LoseAbilityAllEffect(new IslandwalkAbility(), Duration.WhileOnBattlefield, new FilterCreaturePermanent("all creatures")); + Effect effect2 = new LoseAbilityAllEffect(new IslandwalkAbility(), Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_ALL_CREATURES); effect2.setText("and islandwalk"); - Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, effect); + Ability ability = new SimpleStaticAbility(effect); ability.addEffect(effect2); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/m/MysticMonstrosity.java b/Mage.Sets/src/mage/cards/m/MysticMonstrosity.java index 931d03eba21..3ba9014ef37 100644 --- a/Mage.Sets/src/mage/cards/m/MysticMonstrosity.java +++ b/Mage.Sets/src/mage/cards/m/MysticMonstrosity.java @@ -24,7 +24,6 @@ public final class MysticMonstrosity extends CardImpl { this.subtype.add(SubType.CONSTRUCT); this.power = new MageInt(5); this.toughness = new MageInt(6); - this.transformable = true; this.nightCard = true; // Lands you control have "{T}: Add one mana of any color." diff --git a/Mage.Sets/src/mage/cards/m/MysticRedaction.java b/Mage.Sets/src/mage/cards/m/MysticRedaction.java index d12844b862c..6acf24d5a39 100644 --- a/Mage.Sets/src/mage/cards/m/MysticRedaction.java +++ b/Mage.Sets/src/mage/cards/m/MysticRedaction.java @@ -21,7 +21,7 @@ public final class MysticRedaction extends CardImpl { // At the beginning of your upkeep, scry 1. this.addAbility(new BeginningOfUpkeepTriggeredAbility( - new ScryEffect(1), TargetController.YOU, false + new ScryEffect(1, false), TargetController.YOU, false )); // Whenever you discard a card, each opponent mills two cards. diff --git a/Mage.Sets/src/mage/cards/m/MysticReflection.java b/Mage.Sets/src/mage/cards/m/MysticReflection.java index 8cc8f5ebba3..ba053bf1835 100644 --- a/Mage.Sets/src/mage/cards/m/MysticReflection.java +++ b/Mage.Sets/src/mage/cards/m/MysticReflection.java @@ -88,7 +88,7 @@ class MysticReflectionEffect extends OneShotEffect { // The zone is ALL because if the targeted permanent leaves the battlefield, the replacement effect still applies. SimpleStaticAbility staticAbilityOnCard = new SimpleStaticAbility(Zone.ALL, new MysticReflectionReplacementEffect(watcher.getEnteredThisTurn(), source.getSourceId().toString())); MysticReflectionGainAbilityEffect gainAbilityEffect = new MysticReflectionGainAbilityEffect(staticAbilityOnCard); - gainAbilityEffect.setTargetPointer(new FixedTarget(targetedPermanent.getMainCard().getId())); + gainAbilityEffect.setTargetPointer(new FixedTarget(targetedPermanent.getMainCard().getId(), game)); game.addEffect(gainAbilityEffect, source); return true; } diff --git a/Mage.Sets/src/mage/cards/m/MysticSkull.java b/Mage.Sets/src/mage/cards/m/MysticSkull.java index 5c655efc6de..e9a14a86354 100644 --- a/Mage.Sets/src/mage/cards/m/MysticSkull.java +++ b/Mage.Sets/src/mage/cards/m/MysticSkull.java @@ -21,7 +21,6 @@ public final class MysticSkull extends CardImpl { public MysticSkull(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); - this.transformable = true; this.secondSideCardClazz = mage.cards.m.MysticMonstrosity.class; // {1}, {T}: Add one mana of any color. @@ -31,7 +30,7 @@ public final class MysticSkull extends CardImpl { // {5}, {T}: Transform Mystic Skull. this.addAbility(new TransformAbility()); - ability = new SimpleActivatedAbility(new TransformSourceEffect(true), new GenericManaCost(5)); + ability = new SimpleActivatedAbility(new TransformSourceEffect(), new GenericManaCost(5)); ability.addCost(new TapSourceCost()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/n/NacatlWarPride.java b/Mage.Sets/src/mage/cards/n/NacatlWarPride.java index 0d7cd8adde7..1248fb27cb9 100644 --- a/Mage.Sets/src/mage/cards/n/NacatlWarPride.java +++ b/Mage.Sets/src/mage/cards/n/NacatlWarPride.java @@ -105,7 +105,7 @@ class NacatlWarPrideEffect extends OneShotEffect { CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(controller.getId(), null, false, count, true, true); effect.setTargetPointer(new FixedTarget(origNactalWarPride, game)); effect.apply(game, source); - copies.addAll(effect.getAddedPermanent()); + copies.addAll(effect.getAddedPermanents()); if (!copies.isEmpty()) { FixedTargets fixedTargets = new FixedTargets(copies, game); diff --git a/Mage.Sets/src/mage/cards/n/NahiriHeirOfTheAncients.java b/Mage.Sets/src/mage/cards/n/NahiriHeirOfTheAncients.java index 4a8981a596f..571dc51406d 100644 --- a/Mage.Sets/src/mage/cards/n/NahiriHeirOfTheAncients.java +++ b/Mage.Sets/src/mage/cards/n/NahiriHeirOfTheAncients.java @@ -3,7 +3,6 @@ package mage.cards.n; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.dynamicvalue.common.StaticValue; @@ -53,7 +52,7 @@ public final class NahiriHeirOfTheAncients extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.NAHIRI); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Create a 1/1 white Kor Warrior creature token. You may attach an Equipment you control to it. this.addAbility(new LoyaltyAbility(new NahiriHeirOfTheAncientsEffect(), 1)); diff --git a/Mage.Sets/src/mage/cards/n/NahiriStormOfStone.java b/Mage.Sets/src/mage/cards/n/NahiriStormOfStone.java index ace6fe386a6..0270fe1df37 100644 --- a/Mage.Sets/src/mage/cards/n/NahiriStormOfStone.java +++ b/Mage.Sets/src/mage/cards/n/NahiriStormOfStone.java @@ -2,7 +2,6 @@ package mage.cards.n; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.MyTurnCondition; import mage.abilities.decorator.ConditionalContinuousEffect; @@ -44,7 +43,7 @@ public final class NahiriStormOfStone extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.NAHIRI); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(6)); + this.setStartingLoyalty(6); // As long as it's your turn, creatures you control have first strike and equip abilities you activate cost {1} less to activate. Ability ability = new SimpleStaticAbility(new ConditionalContinuousEffect( diff --git a/Mage.Sets/src/mage/cards/n/NahiriTheHarbinger.java b/Mage.Sets/src/mage/cards/n/NahiriTheHarbinger.java index 934edb37f7a..3d718744644 100644 --- a/Mage.Sets/src/mage/cards/n/NahiriTheHarbinger.java +++ b/Mage.Sets/src/mage/cards/n/NahiriTheHarbinger.java @@ -4,7 +4,6 @@ import java.util.UUID; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.costs.common.DiscardCardCost; import mage.abilities.effects.ContinuousEffect; @@ -57,7 +56,7 @@ public final class NahiriTheHarbinger extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.NAHIRI); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +2: You may discard a card. If you do, draw a card. this.addAbility(new LoyaltyAbility(new DoIfCostPaid(new DrawCardSourceControllerEffect(1), new DiscardCardCost()), 2)); diff --git a/Mage.Sets/src/mage/cards/n/NahiriTheLithomancer.java b/Mage.Sets/src/mage/cards/n/NahiriTheLithomancer.java index 0ae039061f5..1b4db2b2327 100644 --- a/Mage.Sets/src/mage/cards/n/NahiriTheLithomancer.java +++ b/Mage.Sets/src/mage/cards/n/NahiriTheLithomancer.java @@ -5,7 +5,6 @@ import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.common.CanBeYourCommanderAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; @@ -41,7 +40,7 @@ public final class NahiriTheLithomancer extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.NAHIRI); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +2: Create a 1/1 white Kor Soldier creature token. You may attach an Equipment you control to it. this.addAbility(new LoyaltyAbility(new NahiriTheLithomancerFirstAbilityEffect(), 2)); diff --git a/Mage.Sets/src/mage/cards/n/NahirisLithoforming.java b/Mage.Sets/src/mage/cards/n/NahirisLithoforming.java index 612f280fbac..ce13960799a 100644 --- a/Mage.Sets/src/mage/cards/n/NahirisLithoforming.java +++ b/Mage.Sets/src/mage/cards/n/NahirisLithoforming.java @@ -2,7 +2,7 @@ package mage.cards.n; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.PermanentsEnterBattlefieldTappedEffect; import mage.abilities.effects.common.continuous.PlayAdditionalLandsControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -11,8 +11,6 @@ import mage.constants.Duration; import mage.constants.Outcome; import mage.filter.StaticFilters; import mage.game.Game; -import mage.game.events.EntersTheBattlefieldEvent; -import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetPermanent; @@ -29,7 +27,9 @@ public final class NahirisLithoforming extends CardImpl { // Sacrifice X lands. For each land sacrificed this way, draw a card. You may play X additional lands this turn. Lands you control enter the battlefield tapped this turn. this.getSpellAbility().addEffect(new NahirisLithoformingSacrificeEffect()); - this.getSpellAbility().addEffect(new NahirisLithoformingTappedEffect()); + this.getSpellAbility().addEffect(new PermanentsEnterBattlefieldTappedEffect( + StaticFilters.FILTER_CONTROLLED_PERMANENT_LANDS, Duration.EndOfTurn + )); } private NahirisLithoforming(final NahirisLithoforming card) { @@ -88,45 +88,3 @@ class NahirisLithoformingSacrificeEffect extends OneShotEffect { return true; } } - -class NahirisLithoformingTappedEffect extends ReplacementEffectImpl { - - NahirisLithoformingTappedEffect() { - super(Duration.EndOfTurn, Outcome.Tap); - staticText = "Lands you control enter the battlefield tapped this turn."; - } - - NahirisLithoformingTappedEffect(final NahirisLithoformingTappedEffect effect) { - super(effect); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Permanent target = ((EntersTheBattlefieldEvent) event).getTarget(); - if (target != null) { - target.setTapped(true); - } - return false; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (source.getControllerId().equals(event.getPlayerId())) { - Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget(); - if (permanent != null && permanent.isLand(game)) { - return true; - } - } - return false; - } - - @Override - public NahirisLithoformingTappedEffect copy() { - return new NahirisLithoformingTappedEffect(this); - } -} diff --git a/Mage.Sets/src/mage/cards/n/NamelessConqueror.java b/Mage.Sets/src/mage/cards/n/NamelessConqueror.java new file mode 100644 index 00000000000..2151a7b5730 --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NamelessConqueror.java @@ -0,0 +1,43 @@ +package mage.cards.n; + +import mage.MageInt; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class NamelessConqueror extends CardImpl { + + public NamelessConqueror(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, ""); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SAMURAI); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + this.color.setRed(true); + this.nightCard = true; + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Haste + this.addAbility(HasteAbility.getInstance()); + } + + private NamelessConqueror(final NamelessConqueror card) { + super(card); + } + + @Override + public NamelessConqueror copy() { + return new NamelessConqueror(this); + } +} diff --git a/Mage.Sets/src/mage/cards/n/NaomiPillarOfOrder.java b/Mage.Sets/src/mage/cards/n/NaomiPillarOfOrder.java new file mode 100644 index 00000000000..77343d225d6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NaomiPillarOfOrder.java @@ -0,0 +1,48 @@ +package mage.cards.n; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldOrAttacksSourceTriggeredAbility; +import mage.abilities.condition.common.ControlArtifactAndEnchantmentCondition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.hint.common.ControlArtifactAndEnchantmentHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.game.permanent.token.SamuraiToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class NaomiPillarOfOrder extends CardImpl { + + public NaomiPillarOfOrder(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ADVISOR); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Whenever Naomi, Pillar of Order enters the battlefield or attacks, if you control an artifact and an enchantment, create a 2/2 white Samurai creature token with vigilance. + this.addAbility(new ConditionalInterveningIfTriggeredAbility( + new EntersBattlefieldOrAttacksSourceTriggeredAbility(new CreateTokenEffect(new SamuraiToken())), + ControlArtifactAndEnchantmentCondition.instance, "Whenever {this} enters the battlefield or " + + "attacks, if you control an artifact and an enchantment, create a 2/2 white Samurai creature token with vigilance." + ).addHint(ControlArtifactAndEnchantmentHint.instance)); + } + + private NaomiPillarOfOrder(final NaomiPillarOfOrder card) { + super(card); + } + + @Override + public NaomiPillarOfOrder copy() { + return new NaomiPillarOfOrder(this); + } +} diff --git a/Mage.Sets/src/mage/cards/n/NarnamRenegade.java b/Mage.Sets/src/mage/cards/n/NarnamRenegade.java index a105199e1a5..1a7f53b57af 100644 --- a/Mage.Sets/src/mage/cards/n/NarnamRenegade.java +++ b/Mage.Sets/src/mage/cards/n/NarnamRenegade.java @@ -1,7 +1,5 @@ - package mage.cards.n; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.condition.common.RevoltCondition; @@ -14,8 +12,9 @@ import mage.constants.SubType; import mage.counters.CounterType; import mage.watchers.common.RevoltWatcher; +import java.util.UUID; + /** - * * @author fireshoes */ public final class NarnamRenegade extends CardImpl { @@ -32,13 +31,11 @@ public final class NarnamRenegade extends CardImpl { this.addAbility(DeathtouchAbility.getInstance()); // Revolt — Narnam Renegade enters the battlefield with a +1/+1 counter on it if a permanent you controlled left this battlefield this turn. - this.addAbility( - new EntersBattlefieldAbility( - new AddCountersSourceEffect(CounterType.P1P1.createInstance()), - false, RevoltCondition.instance, - "Revolt — {this} enters the battlefield with a +1/+1 counter on it if a permanent you controlled left the battlefield this turn", null), - new RevoltWatcher() - ); + this.addAbility(new EntersBattlefieldAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false, + RevoltCondition.instance, "Revolt — {this} enters the battlefield with " + + "a +1/+1 counter on it if a permanent you controlled left the battlefield this turn.", null + ), new RevoltWatcher()); } private NarnamRenegade(final NarnamRenegade card) { diff --git a/Mage.Sets/src/mage/cards/n/NarsetOfTheAncientWay.java b/Mage.Sets/src/mage/cards/n/NarsetOfTheAncientWay.java index b69562ce7ec..31e88316cc1 100644 --- a/Mage.Sets/src/mage/cards/n/NarsetOfTheAncientWay.java +++ b/Mage.Sets/src/mage/cards/n/NarsetOfTheAncientWay.java @@ -5,7 +5,6 @@ import mage.MageObject; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.SpellAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.condition.Condition; import mage.abilities.costs.Cost; @@ -38,7 +37,7 @@ public final class NarsetOfTheAncientWay extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.NARSET); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: You gain 2 life. Add {U}, {R}, or {W}. Spend this mana only to cast a noncreature spell. this.addAbility(new LoyaltyAbility(new NarsetOfTheAncientWayManaEffect(), 1)); diff --git a/Mage.Sets/src/mage/cards/n/NarsetParterOfVeils.java b/Mage.Sets/src/mage/cards/n/NarsetParterOfVeils.java index 20996fe8a01..fec48a5f7d8 100644 --- a/Mage.Sets/src/mage/cards/n/NarsetParterOfVeils.java +++ b/Mage.Sets/src/mage/cards/n/NarsetParterOfVeils.java @@ -2,7 +2,6 @@ package mage.cards.n; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; @@ -36,7 +35,7 @@ public final class NarsetParterOfVeils extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.NARSET); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // Each opponent can't draw more than one card each turn. this.addAbility(new SimpleStaticAbility(new NarsetParterOfVeilsEffect()), new CardsAmountDrawnThisTurnWatcher()); diff --git a/Mage.Sets/src/mage/cards/n/NarsetTranscendent.java b/Mage.Sets/src/mage/cards/n/NarsetTranscendent.java index 17813fe54b1..a47d27c528c 100644 --- a/Mage.Sets/src/mage/cards/n/NarsetTranscendent.java +++ b/Mage.Sets/src/mage/cards/n/NarsetTranscendent.java @@ -5,7 +5,6 @@ import mage.MageObject; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; @@ -42,7 +41,7 @@ public final class NarsetTranscendent extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.NARSET); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(6)); + this.setStartingLoyalty(6); // +1: Look at the top card of your library. If it's a noncreature, nonland card, you may reveal it and put it into your hand. this.addAbility(new LoyaltyAbility(new NarsetTranscendentEffect1(), 1)); diff --git a/Mage.Sets/src/mage/cards/n/NashiMoonSagesScion.java b/Mage.Sets/src/mage/cards/n/NashiMoonSagesScion.java new file mode 100644 index 00000000000..076116e003d --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NashiMoonSagesScion.java @@ -0,0 +1,200 @@ +package mage.cards.n; + +import mage.MageInt; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.Costs; +import mage.abilities.costs.CostsImpl; +import mage.abilities.costs.common.PayLifeCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.asthought.CanPlayCardControllerEffect; +import mage.abilities.keyword.NinjutsuAbility; +import mage.cards.*; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.stack.Spell; +import mage.players.Player; +import mage.util.CardUtil; +import mage.watchers.Watcher; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class NashiMoonSagesScion extends CardImpl { + + public NashiMoonSagesScion(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{B}"); + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.RAT); + this.subtype.add(SubType.NINJA); + + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // Ninjutsu {3}{B} + this.addAbility(new NinjutsuAbility("{3}{B}")); + + // Whenever Nashi, Moon Sage's Scion deals combat damage to a player, exile the top card of each player's library. Until end of turn, you may play one of those cards. If you cast a spell this way, pay life equal to its mana value rather than paying its mana cost. + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility( + new NashiMoonSagesScionEffect(), false + ), new NashiMoonSagesScionWatcher()); + } + + private NashiMoonSagesScion(final NashiMoonSagesScion card) { + super(card); + } + + @Override + public NashiMoonSagesScion copy() { + return new NashiMoonSagesScion(this); + } +} + +class NashiMoonSagesScionEffect extends OneShotEffect { + + public NashiMoonSagesScionEffect() { + super(Outcome.Benefit); + this.staticText = "exile the top card of each player's library. Until end of turn, " + + "you may play one of those cards. If you cast a spell this way, " + + "pay life equal to its mana value rather than paying its mana cost"; + } + + public NashiMoonSagesScionEffect(final NashiMoonSagesScionEffect effect) { + super(effect); + } + + @Override + public NashiMoonSagesScionEffect copy() { + return new NashiMoonSagesScionEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + Cards cards = new CardsImpl(); + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + if (player != null) { + cards.add(player.getLibrary().getFromTop(game)); + } + } + Set cardSet = cards.getCards(game); + controller.moveCardsToExile( + cardSet, source, game, true, + CardUtil.getExileZoneId(game, source), + CardUtil.getSourceName(game, source) + ); + NashiMoonSagesScionWatcher.addCards(source, cardSet, game); + for (Card card : cardSet) { + game.addEffect(new NashiMoonSagesScionPlayEffect(game, card), source); + } + return true; + } +} + +class NashiMoonSagesScionWatcher extends Watcher { + + private final Map>> morMap = new HashMap<>(); + + public NashiMoonSagesScionWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.CLEANUP_STEP_POST) { + morMap.entrySet().removeIf(e -> !e.getKey().zoneCounterIsCurrent(game)); + morMap.values() + .stream() + .flatMap(Collection::stream) + .map(set -> set.removeIf(mor -> !mor.zoneCounterIsCurrent(game))); + morMap.values().removeIf(Set::isEmpty); + return; + } + if (event.getType() != GameEvent.EventType.SPELL_CAST || event.getAdditionalReference() == null) { + return; + } + Spell spell = game.getSpell(event.getTargetId()); + if (spell == null) { + return; + } + morMap.getOrDefault( + event.getAdditionalReference().getApprovingMageObjectReference(), Collections.emptySet() + ).removeIf(set -> set + .stream() + .anyMatch(mor -> mor.getSourceId().equals(spell.getMainCard().getId()) + && mor.getZoneChangeCounter() + 1 == spell.getZoneChangeCounter(game))); + } + + @Override + public void reset() { + super.reset(); + morMap.clear(); + } + + static void addCards(Ability source, Set cards, Game game) { + game.getState() + .getWatcher(NashiMoonSagesScionWatcher.class) + .morMap + .computeIfAbsent(new MageObjectReference(source), x -> new HashSet<>()) + .add(cards + .stream() + .map(card -> new MageObjectReference(card, game)) + .collect(Collectors.toSet())); + } + + static boolean checkCard(Game game, Ability source, MageObjectReference mor) { + return game.getState() + .getWatcher(NashiMoonSagesScionWatcher.class) + .morMap + .getOrDefault(new MageObjectReference(source), Collections.emptySet()) + .stream() + .flatMap(Collection::stream) + .anyMatch(mor::equals); + } +} + +class NashiMoonSagesScionPlayEffect extends CanPlayCardControllerEffect { + + NashiMoonSagesScionPlayEffect(Game game, Card card) { + super(game, card.getMainCard().getId(), card.getZoneChangeCounter(game), Duration.EndOfTurn); + } + + private NashiMoonSagesScionPlayEffect(final NashiMoonSagesScionPlayEffect effect) { + super(effect); + } + + @Override + public NashiMoonSagesScionPlayEffect copy() { + return new NashiMoonSagesScionPlayEffect(this); + } + + @Override + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + if (!super.applies(objectId, source, affectedControllerId, game) + || !NashiMoonSagesScionWatcher.checkCard(game, source, mor)) { + return false; + } + Card cardToCheck = mor.getCard(game); + if (cardToCheck.isLand(game)) { + return true; + } + // allows to play/cast with alternative life cost + Player controller = game.getPlayer(source.getControllerId()); + PayLifeCost lifeCost = new PayLifeCost(cardToCheck.getSpellAbility().getManaCosts().manaValue()); + Costs newCosts = new CostsImpl<>(); + newCosts.add(lifeCost); + newCosts.addAll(cardToCheck.getSpellAbility().getCosts()); + controller.setCastSourceIdWithAlternateMana(cardToCheck.getId(), null, newCosts); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/n/NaturesEmbrace.java b/Mage.Sets/src/mage/cards/n/NaturesEmbrace.java new file mode 100644 index 00000000000..b5e93278e03 --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NaturesEmbrace.java @@ -0,0 +1,94 @@ +package mage.cards.n; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.effects.mana.AddManaOfAnyColorEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.abilities.mana.SimpleManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class NaturesEmbrace extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent("creature or land"); + + static { + filter.add(Predicates.or( + CardType.CREATURE.getPredicate(), + CardType.LAND.getPredicate() + )); + } + + public NaturesEmbrace(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature or land + TargetPermanent auraTarget = new TargetPermanent(filter); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // As long as enchanted permanent is a creature, it gets +2/+2. + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new BoostEnchantedEffect(2, 2), NaturesEmbraceCondition.CREATURE, + "as long as enchanted permanent is a creature, it gets +2/+2" + ))); + + // As long as enchanted permanent is a land, it has "{T}: Add two mana of any one color." + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new GainAbilityAttachedEffect(new SimpleManaAbility( + Zone.BATTLEFIELD, new AddManaOfAnyColorEffect(2), new TapSourceCost() + ), AttachmentType.AURA), NaturesEmbraceCondition.LAND, "as long as enchanted permanent " + + "is a land, it has \"{T}: Add two mana of any one color.\"" + ))); + } + + private NaturesEmbrace(final NaturesEmbrace card) { + super(card); + } + + @Override + public NaturesEmbrace copy() { + return new NaturesEmbrace(this); + } +} + +enum NaturesEmbraceCondition implements Condition { + CREATURE(CardType.CREATURE), + LAND(CardType.LAND); + private final CardType cardType; + + NaturesEmbraceCondition(CardType cardType) { + this.cardType = cardType; + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (permanent == null) { + return false; + } + Permanent attached = game.getPermanent(permanent.getAttachedTo()); + return attached != null && attached.getCardType(game).contains(cardType); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/n/NebelgastBeguiler.java b/Mage.Sets/src/mage/cards/n/NebelgastBeguiler.java new file mode 100644 index 00000000000..4ed3c608a06 --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NebelgastBeguiler.java @@ -0,0 +1,44 @@ +package mage.cards.n; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.TapTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class NebelgastBeguiler extends CardImpl { + + public NebelgastBeguiler(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{W}"); + + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(2); + this.toughness = new MageInt(5); + + // {W}, {T}: Tap target creature. + Ability ability = new SimpleActivatedAbility(new TapTargetEffect(), new ManaCostsImpl<>("{W}")); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + private NebelgastBeguiler(final NebelgastBeguiler card) { + super(card); + } + + @Override + public NebelgastBeguiler copy() { + return new NebelgastBeguiler(this); + } +} diff --git a/Mage.Sets/src/mage/cards/n/NeckBreaker.java b/Mage.Sets/src/mage/cards/n/NeckBreaker.java index 4f9b4f0b7fb..96ced371e3f 100644 --- a/Mage.Sets/src/mage/cards/n/NeckBreaker.java +++ b/Mage.Sets/src/mage/cards/n/NeckBreaker.java @@ -30,7 +30,6 @@ public final class NeckBreaker extends CardImpl { // this card is the second face of double-faced card this.nightCard = true; - this.transformable = true; // Attacking creatures you control get +1/+0 and have trample. Ability ability = new SimpleStaticAbility(new BoostControlledEffect( diff --git a/Mage.Sets/src/mage/cards/n/Necroduality.java b/Mage.Sets/src/mage/cards/n/Necroduality.java new file mode 100644 index 00000000000..765756ee5de --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/Necroduality.java @@ -0,0 +1,47 @@ +package mage.cards.n; + +import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.effects.common.CreateTokenCopyTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SetTargetPointer; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.permanent.TokenPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Necroduality extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledCreaturePermanent(SubType.ZOMBIE); + + static { + filter.add(TokenPredicate.FALSE); + } + + public Necroduality(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{U}"); + + // Whenever a nontoken Zombie enters the battlefield under your control, create a token that's a copy of that creature. + this.addAbility(new EntersBattlefieldControlledTriggeredAbility( + Zone.BATTLEFIELD, new CreateTokenCopyTargetEffect(true), filter, false, + SetTargetPointer.PERMANENT, "Whenever a nontoken Zombie enters the battlefield " + + "under your control, create a token that's a copy of that creature." + )); + } + + private Necroduality(final Necroduality card) { + super(card); + } + + @Override + public Necroduality copy() { + return new Necroduality(this); + } +} diff --git a/Mage.Sets/src/mage/cards/n/NecromanticSelection.java b/Mage.Sets/src/mage/cards/n/NecromanticSelection.java index 5b7e42c5b0a..b2fec3f42bc 100644 --- a/Mage.Sets/src/mage/cards/n/NecromanticSelection.java +++ b/Mage.Sets/src/mage/cards/n/NecromanticSelection.java @@ -1,4 +1,3 @@ - package mage.cards.n; import mage.MageObject; @@ -73,10 +72,12 @@ class NecromanticSelectionEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); MageObject sourceObject = game.getObject(source.getSourceId()); - if (sourceObject != null && controller != null) { + if (sourceObject != null + && controller != null) { Cards cards = new CardsImpl(); for (Permanent permanent : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, controller.getId(), source.getSourceId(), game)) { permanent.destroy(source, game, false); + game.checkStateAndTriggered(); // Meren of the Clan Nel Toth bug #8515 if (game.getState().getZone(permanent.getId()) == Zone.GRAVEYARD) { cards.add(permanent); } @@ -88,13 +89,14 @@ class NecromanticSelectionEffect extends OneShotEffect { } filter.add(Predicates.or(cardIdPredicates)); Target target = new TargetCardInGraveyard(filter); - if (controller.chooseTarget(outcome, target, source, game)) { + target.setNotTarget(true); + if (controller.chooseTarget(Outcome.Benefit, target, source, game)) { Card card = game.getCard(target.getFirstTarget()); if (card != null) { controller.moveCards(card, Zone.BATTLEFIELD, source, game); ContinuousEffect effect = new BecomesBlackZombieAdditionEffect(); effect.setText("It's a black Zombie in addition to its other colors and types"); - effect.setTargetPointer(new FixedTarget(card.getId())); + effect.setTargetPointer(new FixedTarget(card.getId(), game)); game.addEffect(effect, source); } } diff --git a/Mage.Sets/src/mage/cards/n/NecropolisFiend.java b/Mage.Sets/src/mage/cards/n/NecropolisFiend.java index f7d666275c1..c44697f799f 100644 --- a/Mage.Sets/src/mage/cards/n/NecropolisFiend.java +++ b/Mage.Sets/src/mage/cards/n/NecropolisFiend.java @@ -24,7 +24,7 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; import mage.target.Target; @@ -63,7 +63,7 @@ public final class NecropolisFiend extends CardImpl { ability.addCost(new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent()); ability.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard( - 1, 1, new FilterCard("cards from your graveyard") + 1, 1, StaticFilters.FILTER_CARDS_FROM_YOUR_GRAVEYARD ), "Exile X cards from your graveyard")); ability.setTargetAdjuster(NecropolisFiendTargetAdjuster.instance); ability.setCostAdjuster(NecropolisFiendCostAdjuster.instance); @@ -117,4 +117,4 @@ enum NecropolisFiendTargetAdjuster implements TargetAdjuster { } } } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/n/NecropolisRegent.java b/Mage.Sets/src/mage/cards/n/NecropolisRegent.java index 16915ccbced..0e9fad4a391 100644 --- a/Mage.Sets/src/mage/cards/n/NecropolisRegent.java +++ b/Mage.Sets/src/mage/cards/n/NecropolisRegent.java @@ -1,4 +1,3 @@ - package mage.cards.n; import java.util.UUID; @@ -17,7 +16,6 @@ import mage.counters.CounterType; import mage.game.Game; import mage.game.events.DamagedPlayerEvent; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; @@ -28,10 +26,9 @@ import mage.target.targetpointer.FixedTarget; public final class NecropolisRegent extends CardImpl { public NecropolisRegent(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{B}{B}"); this.subtype.add(SubType.VAMPIRE); - this.power = new MageInt(6); this.toughness = new MageInt(5); @@ -79,7 +76,7 @@ class NecropolisRegentTriggeredAbility extends TriggeredAbilityImpl { if (creature != null && creature.isControlledBy(controllerId)) { this.getEffects().clear(); Effect effect = new AddCountersTargetEffect(CounterType.P1P1.createInstance(event.getAmount())); - effect.setTargetPointer(new FixedTarget(creature.getId())); + effect.setTargetPointer(new FixedTarget(creature.getId(), game)); this.addEffect(effect); return true; } @@ -91,4 +88,4 @@ class NecropolisRegentTriggeredAbility extends TriggeredAbilityImpl { public String getRule() { return "Whenever a creature you control deals combat damage to a player, put that many +1/+1 counters on it."; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/n/NecroticSliver.java b/Mage.Sets/src/mage/cards/n/NecroticSliver.java index a8940b0a6ad..d14976817d7 100644 --- a/Mage.Sets/src/mage/cards/n/NecroticSliver.java +++ b/Mage.Sets/src/mage/cards/n/NecroticSliver.java @@ -38,7 +38,7 @@ public final class NecroticSliver extends CardImpl { ability.addTarget(new TargetPermanent()); 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 \"{3}, Sacrifice this permanent: Destroy target permanent.\""))); } diff --git a/Mage.Sets/src/mage/cards/n/NeedletoothRaptor.java b/Mage.Sets/src/mage/cards/n/NeedletoothRaptor.java index 6bec37f45b9..d69ab55059b 100644 --- a/Mage.Sets/src/mage/cards/n/NeedletoothRaptor.java +++ b/Mage.Sets/src/mage/cards/n/NeedletoothRaptor.java @@ -10,8 +10,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; /** @@ -20,12 +19,6 @@ import mage.target.common.TargetCreaturePermanent; */ public final class NeedletoothRaptor extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public NeedletoothRaptor(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); this.subtype.add(SubType.DINOSAUR); @@ -34,7 +27,7 @@ public final class NeedletoothRaptor extends CardImpl { // Enrage — Whenever Needletooth Raptor is dealt damage, it deals 5 damage to target creature an opponent controls. Ability ability = new DealtDamageToSourceTriggeredAbility(new DamageTargetEffect(5).setText("it deals 5 damage to target creature an opponent controls"), false, true); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/n/NefaroxOverlordOfGrixis.java b/Mage.Sets/src/mage/cards/n/NefaroxOverlordOfGrixis.java index e287152a99f..16c8f779575 100644 --- a/Mage.Sets/src/mage/cards/n/NefaroxOverlordOfGrixis.java +++ b/Mage.Sets/src/mage/cards/n/NefaroxOverlordOfGrixis.java @@ -3,7 +3,7 @@ package mage.cards.n; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.AttacksAloneTriggeredAbility; +import mage.abilities.common.AttacksAloneSourceTriggeredAbility; import mage.abilities.effects.common.SacrificeEffect; import mage.abilities.keyword.ExaltedAbility; import mage.abilities.keyword.FlyingAbility; @@ -33,7 +33,7 @@ public final class NefaroxOverlordOfGrixis extends CardImpl { // Exalted this.addAbility(new ExaltedAbility()); // Whenever Nefarox, Overlord of Grixis attacks alone, defending player sacrifices a creature. - this.addAbility(new AttacksAloneTriggeredAbility(new SacrificeEffect( + this.addAbility(new AttacksAloneSourceTriggeredAbility(new SacrificeEffect( StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT, 1, "defending player"))); } diff --git a/Mage.Sets/src/mage/cards/n/NeganTheColdBlooded.java b/Mage.Sets/src/mage/cards/n/NeganTheColdBlooded.java index 52e27d0d563..bc812c21a67 100644 --- a/Mage.Sets/src/mage/cards/n/NeganTheColdBlooded.java +++ b/Mage.Sets/src/mage/cards/n/NeganTheColdBlooded.java @@ -46,7 +46,7 @@ public final class NeganTheColdBlooded extends CardImpl { // Whenever an opponent sacrifices a creature, you create a Treasure token. this.addAbility(new SacrificeAllTriggeredAbility( - new CreateTokenEffect(new TreasureToken()), + new CreateTokenEffect(new TreasureToken()).setText("you create a Treasure token"), StaticFilters.FILTER_PERMANENT_A_CREATURE, TargetController.OPPONENT, false )); diff --git a/Mage.Sets/src/mage/cards/n/NeglectedHeirloom.java b/Mage.Sets/src/mage/cards/n/NeglectedHeirloom.java index f2506666e47..810e1cb6d35 100644 --- a/Mage.Sets/src/mage/cards/n/NeglectedHeirloom.java +++ b/Mage.Sets/src/mage/cards/n/NeglectedHeirloom.java @@ -28,7 +28,6 @@ public final class NeglectedHeirloom extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}"); this.subtype.add(SubType.EQUIPMENT); - this.transformable = true; this.secondSideCardClazz = mage.cards.a.AshmouthBlade.class; // Equipped creature gets +1/+1. @@ -56,7 +55,7 @@ public final class NeglectedHeirloom extends CardImpl { class NeglectedHeirloomTriggeredAbility extends TriggeredAbilityImpl { public NeglectedHeirloomTriggeredAbility() { - super(Zone.BATTLEFIELD, new TransformSourceEffect(true), false); + super(Zone.BATTLEFIELD, new TransformSourceEffect(), false); } public NeglectedHeirloomTriggeredAbility(final NeglectedHeirloomTriggeredAbility ability) { diff --git a/Mage.Sets/src/mage/cards/n/NemesisTrap.java b/Mage.Sets/src/mage/cards/n/NemesisTrap.java index d86c393656e..512f96f5b54 100644 --- a/Mage.Sets/src/mage/cards/n/NemesisTrap.java +++ b/Mage.Sets/src/mage/cards/n/NemesisTrap.java @@ -44,7 +44,7 @@ public final class NemesisTrap extends CardImpl { this.subtype.add(SubType.TRAP); // If a white creature is attacking, you may pay {B}{B} rather than pay Nemesis Trap's mana cost. - this.addAbility(new AlternativeCostSourceAbility(new ManaCostsImpl("{B}{B}"), new PermanentsOnTheBattlefieldCondition(filter, ComparisonType.MORE_THAN, 0, false))); + this.addAbility(new AlternativeCostSourceAbility(new ManaCostsImpl("{B}{B}"), new PermanentsOnTheBattlefieldCondition(filter, false))); // Exile target attacking creature. Create a token that's a copy of that creature. Exile it at the beginning of the next end step. this.getSpellAbility().addEffect(new NemesisTrapEffect()); @@ -88,7 +88,7 @@ class NemesisTrapEffect extends OneShotEffect { CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(); effect.setTargetPointer(new FixedTarget(targetedCreature, game)); effect.apply(game, source); - for (Permanent addedToken : effect.getAddedPermanent()) { + for (Permanent addedToken : effect.getAddedPermanents()) { Effect exileEffect = new ExileTargetEffect("Exile " + addedToken.getName() + " at the beginning of the next end step"); exileEffect.setTargetPointer(new FixedTarget(addedToken, game)); DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); diff --git a/Mage.Sets/src/mage/cards/n/NerfHerder.java b/Mage.Sets/src/mage/cards/n/NerfHerder.java index 3c1ef799152..4125fb53966 100644 --- a/Mage.Sets/src/mage/cards/n/NerfHerder.java +++ b/Mage.Sets/src/mage/cards/n/NerfHerder.java @@ -14,8 +14,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Zone; -import mage.counters.CounterType; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; /** * @@ -23,12 +22,6 @@ import mage.filter.common.FilterCreaturePermanent; */ public final class NerfHerder extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Each creature with a +1/+1 counter on it"); - - static { - filter.add(CounterType.P1P1.getPredicate()); - } - public NerfHerder(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{G}"); this.subtype.add(SubType.HUMAN); @@ -39,8 +32,11 @@ public final class NerfHerder extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new AbilitiesCostReductionControllerEffect(MonstrosityAbility.class, "Monstrosity"))); // Each creature you control with a +1/+1 counter on it has trample. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledEffect(TrampleAbility.getInstance(), Duration.WhileOnBattlefield, filter))); - + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledEffect( + TrampleAbility.getInstance(), + Duration.WhileOnBattlefield, + StaticFilters.FILTER_CONTROLLED_CREATURE_P1P1)) + ); } private NerfHerder(final NerfHerder card) { diff --git a/Mage.Sets/src/mage/cards/n/NessianWildsRavager.java b/Mage.Sets/src/mage/cards/n/NessianWildsRavager.java index 2d3609755e4..b0a2761a92b 100644 --- a/Mage.Sets/src/mage/cards/n/NessianWildsRavager.java +++ b/Mage.Sets/src/mage/cards/n/NessianWildsRavager.java @@ -40,8 +40,12 @@ public final class NessianWildsRavager extends CardImpl { // When Nessian Wilds Ravager enters the battlefield, if tribute wasn't paid, you may have Nessian Wilds Ravager fight another target creature. TriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new FightTargetSourceEffect(), true); ability.addTarget(new TargetCreaturePermanent(filter)); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, TributeNotPaidCondition.instance, - "When {this} enters the battlefield, if its tribute wasn't paid, you may have {this} fight another target creature.")); + this.addAbility(new ConditionalInterveningIfTriggeredAbility( + ability, + TributeNotPaidCondition.instance, + "When {this} enters the battlefield, if tribute wasn't paid, " + + "you may have {this} fight another target creature. " + + "(Each deals damage equal to its power to the other.)")); } private NessianWildsRavager(final NessianWildsRavager card) { diff --git a/Mage.Sets/src/mage/cards/n/NettlingImp.java b/Mage.Sets/src/mage/cards/n/NettlingImp.java index 8bd273d6ce5..881c20ef962 100644 --- a/Mage.Sets/src/mage/cards/n/NettlingImp.java +++ b/Mage.Sets/src/mage/cards/n/NettlingImp.java @@ -1,4 +1,3 @@ - package mage.cards.n; import java.util.UUID; @@ -102,9 +101,9 @@ class NettlingImpDelayedDestroyEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { DestroyTargetEffect effect = new DestroyTargetEffect(); - effect.setTargetPointer(new FixedTarget(source.getFirstTarget())); + effect.setTargetPointer(new FixedTarget(source.getFirstTarget(), game)); AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility - = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(Zone.ALL, effect, TargetController.ANY, new InvertCondition(TargetAttackedThisTurnCondition.instance)); + = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect, TargetController.ANY, new InvertCondition(TargetAttackedThisTurnCondition.instance)); delayedAbility.getDuration(); delayedAbility.getTargets().addAll(source.getTargets()); game.addDelayedTriggeredAbility(delayedAbility, source); diff --git a/Mage.Sets/src/mage/cards/n/NetworkDisruptor.java b/Mage.Sets/src/mage/cards/n/NetworkDisruptor.java new file mode 100644 index 00000000000..36d2c3adeda --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NetworkDisruptor.java @@ -0,0 +1,46 @@ +package mage.cards.n; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class NetworkDisruptor extends CardImpl { + + public NetworkDisruptor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{U}"); + + this.subtype.add(SubType.MOONFOLK); + this.subtype.add(SubType.ROGUE); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When Network Disruptor enters the battlefield, tap target permanent. + Ability ability = new EntersBattlefieldTriggeredAbility(new TapTargetEffect()); + ability.addTarget(new TargetPermanent()); + this.addAbility(ability); + } + + private NetworkDisruptor(final NetworkDisruptor card) { + super(card); + } + + @Override + public NetworkDisruptor copy() { + return new NetworkDisruptor(this); + } +} diff --git a/Mage.Sets/src/mage/cards/n/NetworkTerminal.java b/Mage.Sets/src/mage/cards/n/NetworkTerminal.java new file mode 100644 index 00000000000..c597aeeb162 --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NetworkTerminal.java @@ -0,0 +1,56 @@ +package mage.cards.n; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.common.TapTargetCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.DrawDiscardControllerEffect; +import mage.abilities.mana.AnyColorManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.common.FilterControlledArtifactPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.filter.predicate.permanent.TappedPredicate; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class NetworkTerminal extends CardImpl { + + private static final FilterControlledPermanent filter = new FilterControlledArtifactPermanent("another untapped artifact you control"); + + static { + filter.add(AnotherPredicate.instance); + filter.add(TappedPredicate.UNTAPPED); + } + + public NetworkTerminal(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); + + // {T}: Add one mana of any color. + this.addAbility(new AnyColorManaAbility()); + + // {1}, {T}, Tap another untapped artifact you control: Draw a card, then discard a card. + Ability ability = new SimpleActivatedAbility( + new DrawDiscardControllerEffect(1, 1), new GenericManaCost(1) + ); + ability.addCost(new TapSourceCost()); + ability.addCost(new TapTargetCost(new TargetControlledPermanent(filter))); + this.addAbility(ability); + } + + private NetworkTerminal(final NetworkTerminal card) { + super(card); + } + + @Override + public NetworkTerminal copy() { + return new NetworkTerminal(this); + } +} diff --git a/Mage.Sets/src/mage/cards/n/NewBlood.java b/Mage.Sets/src/mage/cards/n/NewBlood.java index e4c75ebc8b0..04a34f1b1a1 100644 --- a/Mage.Sets/src/mage/cards/n/NewBlood.java +++ b/Mage.Sets/src/mage/cards/n/NewBlood.java @@ -1,7 +1,5 @@ - package mage.cards.n; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.Mode; @@ -10,38 +8,39 @@ import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.GainControlTargetEffect; -import mage.abilities.text.TextPartSubType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.choices.Choice; import mage.choices.ChoiceCreatureType; import mage.constants.*; -import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.mageobject.TextPartSubtypePredicate; +import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.permanent.TappedPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; -import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetControlledPermanent; import mage.target.common.TargetCreaturePermanent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class NewBlood extends CardImpl { + private static final FilterControlledPermanent filter + = new FilterControlledPermanent(SubType.VAMPIRE, "an untapped Vampire you control"); + + static { + filter.add(TappedPredicate.UNTAPPED); + } + public NewBlood(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}{B}"); - TextPartSubType textPartVampire = (TextPartSubType) addTextPart(new TextPartSubType(SubType.VAMPIRE)); - FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("an untapped Vampire you control"); - filter.add(new TextPartSubtypePredicate(textPartVampire)); - filter.add(TappedPredicate.UNTAPPED); // As an additional cost to cast New Blood, tap an untapped Vampire you control. - this.getSpellAbility().addCost(new TapTargetCost( - new TargetControlledCreaturePermanent(1, 1, filter, true))); + this.getSpellAbility().addCost(new TapTargetCost(new TargetControlledPermanent(filter))); // Gain control of target creature. Change the text of that creature by replacing all instances of one creature type with Vampire. getSpellAbility().addEffect(new NewBloodEffect()); @@ -136,36 +135,26 @@ class ChangeCreatureTypeTargetEffect extends ContinuousEffectImpl { if (controller == null) { return false; } - if (fromSubType != null) { - boolean objectFound = false; - for (UUID targetId : targetPointer.getTargets(game, source)) { - MageObject targetObject = game.getObject(targetId); - if (targetObject != null) { - objectFound = true; - switch (layer) { - case TextChangingEffects_3: - targetObject.changeSubType(fromSubType, toSubType); - break; - case TypeChangingEffects_4: - if (sublayer == SubLayer.NA) { - if (targetObject.hasSubtype(fromSubType, game)) { - targetObject.removeSubType(game, fromSubType); - if (!targetObject.hasSubtype(toSubType, game)) { - targetObject.addSubType(game, toSubType); - } - } - break; - } - } - } - if (!objectFound && this.getDuration() == Duration.Custom) { - this.discard(); - } - } - return true; - } else { + if (fromSubType == null) { throw new UnsupportedOperationException("No subtype to change set"); } + boolean objectFound = false; + for (UUID targetId : targetPointer.getTargets(game, source)) { + MageObject targetObject = game.getObject(targetId); + if (targetObject != null) { + objectFound = true; + if (targetObject.hasSubtype(fromSubType, game)) { + targetObject.removeSubType(game, fromSubType); + if (!targetObject.hasSubtype(toSubType, game)) { + targetObject.addSubType(game, toSubType); + } + } + } + if (!objectFound && this.getDuration() == Duration.Custom) { + this.discard(); + } + } + return true; } @Override @@ -175,8 +164,7 @@ class ChangeCreatureTypeTargetEffect extends ContinuousEffectImpl { @Override public boolean hasLayer(Layer layer) { - return layer == Layer.TextChangingEffects_3 - || layer == Layer.TypeChangingEffects_4; + return layer == Layer.TypeChangingEffects_4; } @Override diff --git a/Mage.Sets/src/mage/cards/n/NezumiBladeblesser.java b/Mage.Sets/src/mage/cards/n/NezumiBladeblesser.java new file mode 100644 index 00000000000..f2cea8c2d41 --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NezumiBladeblesser.java @@ -0,0 +1,62 @@ +package mage.cards.n; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.abilities.keyword.DeathtouchAbility; +import mage.abilities.keyword.MenaceAbility; +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 weirddan455 + */ +public final class NezumiBladeblesser extends CardImpl { + + private static final Condition artifactCondition = new PermanentsOnTheBattlefieldCondition(StaticFilters.FILTER_PERMANENT_ARTIFACT); + private static final Condition enchantmentCondition = new PermanentsOnTheBattlefieldCondition(StaticFilters.FILTER_PERMANENT_ENCHANTMENT); + private static final Hint artifactHint = new ConditionHint(artifactCondition, "You control an artifact"); + private static final Hint enchantmentHint = new ConditionHint(enchantmentCondition, "You control an enchantment"); + + public NezumiBladeblesser(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.subtype.add(SubType.RAT); + this.subtype.add(SubType.SAMURAI); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // Nezumi Bladeblesser has deathtouch as long as you control an artifact. + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new GainAbilitySourceEffect(DeathtouchAbility.getInstance()), + artifactCondition, + "{this} has deathtouch as long as you control an artifact" + )).addHint(artifactHint)); + + // Nezumi Bladeblesser has menace as long as you control an enchantment. + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new GainAbilitySourceEffect(new MenaceAbility()), + enchantmentCondition, + "{this} has menace as long as you control an enchantment (This creature can't be blocked except by two or more creatures.)" // temporary + )).addHint(enchantmentHint)); + } + + private NezumiBladeblesser(final NezumiBladeblesser card) { + super(card); + } + + @Override + public NezumiBladeblesser copy() { + return new NezumiBladeblesser(this); + } +} diff --git a/Mage.Sets/src/mage/cards/n/NezumiProwler.java b/Mage.Sets/src/mage/cards/n/NezumiProwler.java new file mode 100644 index 00000000000..2ce934955b0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NezumiProwler.java @@ -0,0 +1,53 @@ +package mage.cards.n; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.DeathtouchAbility; +import mage.abilities.keyword.LifelinkAbility; +import mage.abilities.keyword.NinjutsuAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class NezumiProwler extends CardImpl { + + public NezumiProwler(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{1}{B}"); + + this.subtype.add(SubType.RAT); + this.subtype.add(SubType.NINJA); + this.power = new MageInt(3); + this.toughness = new MageInt(1); + + // Ninjutsu {1}{B} + this.addAbility(new NinjutsuAbility("{1}{B}")); + + // When Nezumi Prowler enters the battlefield, target creature you control gains deathtouch and lifelink until end of turn. + Ability ability = new EntersBattlefieldTriggeredAbility( + new GainAbilityTargetEffect(DeathtouchAbility.getInstance()) + .setText("target creature you control gains deathtouch") + ); + ability.addEffect(new GainAbilityTargetEffect(LifelinkAbility.getInstance()) + .setText("and lifelink until end of turn")); + ability.addTarget(new TargetControlledCreaturePermanent()); + this.addAbility(ability); + } + + private NezumiProwler(final NezumiProwler card) { + super(card); + } + + @Override + public NezumiProwler copy() { + return new NezumiProwler(this); + } +} diff --git a/Mage.Sets/src/mage/cards/n/NezumiRoadCaptain.java b/Mage.Sets/src/mage/cards/n/NezumiRoadCaptain.java new file mode 100644 index 00000000000..c3a6420efa2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NezumiRoadCaptain.java @@ -0,0 +1,50 @@ +package mage.cards.n; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class NezumiRoadCaptain extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent(SubType.VEHICLE, "Vehicles"); + + public NezumiRoadCaptain(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, ""); + + this.subtype.add(SubType.RAT); + this.subtype.add(SubType.ROGUE); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + this.color.setBlack(true); + this.nightCard = true; + + // Menace + this.addAbility(new MenaceAbility(false)); + + // Vehicles you control have menace. + this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( + new MenaceAbility(true), Duration.WhileOnBattlefield, filter + ))); + } + + private NezumiRoadCaptain(final NezumiRoadCaptain card) { + super(card); + } + + @Override + public NezumiRoadCaptain copy() { + return new NezumiRoadCaptain(this); + } +} diff --git a/Mage.Sets/src/mage/cards/n/NiblisOfFrost.java b/Mage.Sets/src/mage/cards/n/NiblisOfFrost.java index 98ab76d9153..37f28345f74 100644 --- a/Mage.Sets/src/mage/cards/n/NiblisOfFrost.java +++ b/Mage.Sets/src/mage/cards/n/NiblisOfFrost.java @@ -11,9 +11,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; import mage.filter.StaticFilters; -import mage.filter.common.FilterCreaturePermanent; import mage.target.common.TargetCreaturePermanent; import java.util.UUID; @@ -22,11 +20,6 @@ import java.util.UUID; * @author fireshoes */ public final class NiblisOfFrost extends CardImpl { - private static final FilterCreaturePermanent filterCreature = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filterCreature.add(TargetController.OPPONENT.getControllerPredicate()); - } public NiblisOfFrost(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}{U}"); @@ -42,7 +35,7 @@ public final class NiblisOfFrost extends CardImpl { // Whenever you cast an instant or sorcery spell, tap target creature an opponent controls. That creature doesn't untap during its controller's next untap step. Ability ability = new SpellCastControllerTriggeredAbility(new TapTargetEffect(), StaticFilters.FILTER_SPELL_AN_INSTANT_OR_SORCERY, false); - ability.addTarget(new TargetCreaturePermanent(filterCreature)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); ability.addEffect(new DontUntapInControllersNextUntapStepTargetEffect("That creature")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/n/NicolBolasDragonGod.java b/Mage.Sets/src/mage/cards/n/NicolBolasDragonGod.java index fb6d6a6fd58..a92ed571ecf 100644 --- a/Mage.Sets/src/mage/cards/n/NicolBolasDragonGod.java +++ b/Mage.Sets/src/mage/cards/n/NicolBolasDragonGod.java @@ -3,7 +3,6 @@ package mage.cards.n; import mage.MageObject; 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.OneShotEffect; @@ -37,7 +36,7 @@ public final class NicolBolasDragonGod extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.BOLAS); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // Nicol Bolas, Dragon-God has all loyalty abilities of all other planeswalkers on the battlefield. this.addAbility(new SimpleStaticAbility(new NicolBolasDragonGodGainAbilitiesEffect())); diff --git a/Mage.Sets/src/mage/cards/n/NicolBolasGodPharaoh.java b/Mage.Sets/src/mage/cards/n/NicolBolasGodPharaoh.java index a496767a99c..b61ac321907 100644 --- a/Mage.Sets/src/mage/cards/n/NicolBolasGodPharaoh.java +++ b/Mage.Sets/src/mage/cards/n/NicolBolasGodPharaoh.java @@ -2,7 +2,6 @@ package mage.cards.n; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageTargetEffect; @@ -47,7 +46,7 @@ public final class NicolBolasGodPharaoh extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.BOLAS); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(7)); + this.setStartingLoyalty(7); // +2: Target opponent exiles cards from the top of their library until they exile a nonland card. Until end of turn, you may cast that card without paying its mana cost. LoyaltyAbility ability = new LoyaltyAbility(new NicolBolasGodPharaohPlusTwoEffect(), 2); diff --git a/Mage.Sets/src/mage/cards/n/NicolBolasPlaneswalker.java b/Mage.Sets/src/mage/cards/n/NicolBolasPlaneswalker.java index c7a0fd8f8d4..d62befa2e3d 100644 --- a/Mage.Sets/src/mage/cards/n/NicolBolasPlaneswalker.java +++ b/Mage.Sets/src/mage/cards/n/NicolBolasPlaneswalker.java @@ -3,7 +3,6 @@ package mage.cards.n; import java.util.UUID; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.SacrificeEffect; @@ -38,7 +37,7 @@ public final class NicolBolasPlaneswalker extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.BOLAS); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +3: Destroy target noncreature permanent. LoyaltyAbility ability = new LoyaltyAbility(new DestroyTargetEffect(), 3); diff --git a/Mage.Sets/src/mage/cards/n/NicolBolasTheArisen.java b/Mage.Sets/src/mage/cards/n/NicolBolasTheArisen.java index 1b28ff7329e..3c5ca837b9d 100644 --- a/Mage.Sets/src/mage/cards/n/NicolBolasTheArisen.java +++ b/Mage.Sets/src/mage/cards/n/NicolBolasTheArisen.java @@ -3,7 +3,6 @@ package mage.cards.n; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; @@ -48,9 +47,8 @@ public final class NicolBolasTheArisen extends CardImpl { this.color.setBlack(true); this.color.setRed(true); this.nightCard = true; - this.transformable = true; - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(7)); + this.setStartingLoyalty(7); // +2: Draw two cards. this.addAbility(new LoyaltyAbility(new DrawCardSourceControllerEffect(2), 2)); diff --git a/Mage.Sets/src/mage/cards/n/NicolBolasTheDeceiver.java b/Mage.Sets/src/mage/cards/n/NicolBolasTheDeceiver.java index 40e68414f29..5f3d56ac0ac 100644 --- a/Mage.Sets/src/mage/cards/n/NicolBolasTheDeceiver.java +++ b/Mage.Sets/src/mage/cards/n/NicolBolasTheDeceiver.java @@ -4,7 +4,6 @@ package mage.cards.n; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamagePlayersEffect; import mage.abilities.effects.common.DestroyTargetEffect; @@ -34,7 +33,7 @@ public final class NicolBolasTheDeceiver extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{5}{U}{B}{R}"); this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.BOLAS); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +3: Each opponent loses 3 life unless that player sacrifices a nonland permanent or discards a card. this.addAbility(new LoyaltyAbility(new NicolBolasTheDeceiverFirstEffect(), 3)); diff --git a/Mage.Sets/src/mage/cards/n/NicolBolasTheRavager.java b/Mage.Sets/src/mage/cards/n/NicolBolasTheRavager.java index 30a8d6b8266..690788279a6 100644 --- a/Mage.Sets/src/mage/cards/n/NicolBolasTheRavager.java +++ b/Mage.Sets/src/mage/cards/n/NicolBolasTheRavager.java @@ -2,7 +2,7 @@ package mage.cards.n; import java.util.UUID; import mage.MageInt; -import mage.abilities.Gender; +import mage.abilities.Pronoun; import mage.abilities.common.ActivateAsSorceryActivatedAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; @@ -34,7 +34,6 @@ public final class NicolBolasTheRavager extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(4); - this.transformable = true; this.secondSideCardClazz = NicolBolasTheArisen.class; // Flying @@ -47,7 +46,7 @@ public final class NicolBolasTheRavager extends CardImpl { this.addAbility(new TransformAbility()); this.addAbility(new ActivateAsSorceryActivatedAbility( Zone.BATTLEFIELD, - new ExileAndReturnTransformedSourceEffect(Gender.MALE), + new ExileAndReturnTransformedSourceEffect(Pronoun.HE), new ManaCostsImpl("{4}{U}{B}{R}") )); } diff --git a/Mage.Sets/src/mage/cards/n/NightMarketAeronaut.java b/Mage.Sets/src/mage/cards/n/NightMarketAeronaut.java index 9c0819d902f..7a2a91fd8d8 100644 --- a/Mage.Sets/src/mage/cards/n/NightMarketAeronaut.java +++ b/Mage.Sets/src/mage/cards/n/NightMarketAeronaut.java @@ -1,10 +1,6 @@ - - package mage.cards.n; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.condition.common.RevoltCondition; import mage.abilities.effects.common.counter.AddCountersSourceEffect; @@ -16,6 +12,8 @@ import mage.constants.SubType; import mage.counters.CounterType; import mage.watchers.common.RevoltWatcher; +import java.util.UUID; + /** * @author JRHerlehy */ @@ -34,11 +32,11 @@ public final class NightMarketAeronaut extends CardImpl { // Revolt — Night Market Aeronaut enters the battlefield with a +1/+1 counter on it if // a permanent you controlled left the battlefield this turn. - Ability ability = new EntersBattlefieldAbility( - new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false, RevoltCondition.instance, - "Revolt — {this} enters the battlefield with a +1/+1 counter on it if a permanent you controlled left the battlefield this turn", null); - ability.addWatcher(new RevoltWatcher()); - this.addAbility(ability); + this.addAbility(new EntersBattlefieldAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false, + RevoltCondition.instance, "Revolt — {this} enters the battlefield with " + + "a +1/+1 counter on it if a permanent you controlled left the battlefield this turn.", null + ), new RevoltWatcher()); } private NightMarketAeronaut(final NightMarketAeronaut card) { diff --git a/Mage.Sets/src/mage/cards/n/NightOfSoulsBetrayal.java b/Mage.Sets/src/mage/cards/n/NightOfSoulsBetrayal.java index 57e293281f9..9e1b67ddb2b 100644 --- a/Mage.Sets/src/mage/cards/n/NightOfSoulsBetrayal.java +++ b/Mage.Sets/src/mage/cards/n/NightOfSoulsBetrayal.java @@ -1,5 +1,3 @@ - - package mage.cards.n; import java.util.UUID; @@ -10,21 +8,18 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SuperType; -import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; /** * * @author Loki */ public final class NightOfSoulsBetrayal extends CardImpl { - private static FilterCreaturePermanent filter = new FilterCreaturePermanent("All creatures"); public NightOfSoulsBetrayal (UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{B}{B}"); addSuperType(SuperType.LEGENDARY); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostAllEffect(-1, -1, Duration.WhileOnBattlefield, filter, false))); + this.addAbility(new SimpleStaticAbility(new BoostAllEffect(-1, -1, Duration.WhileOnBattlefield))); } public NightOfSoulsBetrayal (final NightOfSoulsBetrayal card) { diff --git a/Mage.Sets/src/mage/cards/n/NightfallPredator.java b/Mage.Sets/src/mage/cards/n/NightfallPredator.java index 7b3d840e22c..658b093721b 100644 --- a/Mage.Sets/src/mage/cards/n/NightfallPredator.java +++ b/Mage.Sets/src/mage/cards/n/NightfallPredator.java @@ -30,11 +30,13 @@ public final class NightfallPredator extends CardImpl { // this card is the second face of double-faced card this.nightCard = true; - this.transformable = true; // {R}, {tap}: Nightfall Predator fights target creature. Ability activatedAbility = new SimpleActivatedAbility( - new FightTargetSourceEffect().setText("{this} fights target creature"), new ManaCostsImpl("{R}") + new FightTargetSourceEffect().setText( + "{this} fights target creature. " + + "(Each deals damage equal to its power to the other.)"), + new ManaCostsImpl("{R}") ); activatedAbility.addCost(new TapSourceCost()); activatedAbility.addTarget(new TargetCreaturePermanent()); diff --git a/Mage.Sets/src/mage/cards/n/NighthawkScavenger.java b/Mage.Sets/src/mage/cards/n/NighthawkScavenger.java index 75eaa65edef..cdc12df2eb2 100644 --- a/Mage.Sets/src/mage/cards/n/NighthawkScavenger.java +++ b/Mage.Sets/src/mage/cards/n/NighthawkScavenger.java @@ -34,7 +34,7 @@ public final class NighthawkScavenger extends CardImpl { this.subtype.add(SubType.VAMPIRE); this.subtype.add(SubType.ROGUE); - this.power = new MageInt(0); + this.power = new MageInt(1); this.toughness = new MageInt(3); // Flying @@ -50,7 +50,8 @@ public final class NighthawkScavenger extends CardImpl { this.addAbility(new SimpleStaticAbility( Zone.ALL, new SetPowerSourceEffect(xValue, Duration.EndOfGame) .setText("{this}'s power is equal to 1 plus the number of " + - "card types among cards in your opponents' graveyards.") + "card types among cards in your opponents' graveyards. " + + "(Cards in graveyards have only the characteristics of their front face.)") ).addHint(CardTypesInGraveyardHint.OPPONENTS)); } diff --git a/Mage.Sets/src/mage/cards/n/NightsWhisper.java b/Mage.Sets/src/mage/cards/n/NightsWhisper.java index 98c35080b60..2f5a296d983 100644 --- a/Mage.Sets/src/mage/cards/n/NightsWhisper.java +++ b/Mage.Sets/src/mage/cards/n/NightsWhisper.java @@ -19,8 +19,8 @@ public final class NightsWhisper extends CardImpl { // You draw two cards and you lose 2 life. - this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(2)); - this.getSpellAbility().addEffect(new LoseLifeSourceControllerEffect(2)); + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(2).setText("you draw two cards")); + this.getSpellAbility().addEffect(new LoseLifeSourceControllerEffect(2).concatBy("and")); } private NightsWhisper(final NightsWhisper card) { diff --git a/Mage.Sets/src/mage/cards/n/NightscapeBattlemage.java b/Mage.Sets/src/mage/cards/n/NightscapeBattlemage.java index 836425cc4b9..06cb34ea3fd 100644 --- a/Mage.Sets/src/mage/cards/n/NightscapeBattlemage.java +++ b/Mage.Sets/src/mage/cards/n/NightscapeBattlemage.java @@ -1,9 +1,7 @@ - package mage.cards.n; import java.util.UUID; import mage.MageInt; -import mage.ObjectColor; import mage.abilities.TriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.KickedCostCondition; @@ -15,9 +13,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -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 mage.target.common.TargetLandPermanent; @@ -28,12 +24,6 @@ import mage.target.common.TargetLandPermanent; */ public final class NightscapeBattlemage extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("up to two nonblack creatures"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public NightscapeBattlemage(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{B}"); this.subtype.add(SubType.ZOMBIE); @@ -47,7 +37,7 @@ public final class NightscapeBattlemage extends CardImpl { this.addAbility(kickerAbility); // When Nightscape Battlemage enters the battlefield, if it was kicked with its {2}{U} kicker, return up to two target nonblack creatures to their owners' hands. TriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new ReturnToHandTargetEffect(), false); - ability.addTarget(new TargetCreaturePermanent(0, 2, filter, false)); + ability.addTarget(new TargetCreaturePermanent(0, 2, StaticFilters.FILTER_PERMANENT_CREATURES_NON_BLACK, false)); this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new KickedCostCondition("{2}{U}"), "When {this} enters the battlefield, if it was kicked with its {2}{U} kicker, return up to two target nonblack creatures to their owners' hands.")); // When Nightscape Battlemage enters the battlefield, if it was kicked with its {2}{R} kicker, destroy target land. diff --git a/Mage.Sets/src/mage/cards/n/NightskyMimic.java b/Mage.Sets/src/mage/cards/n/NightskyMimic.java index e6caf289c39..d2491c53289 100644 --- a/Mage.Sets/src/mage/cards/n/NightskyMimic.java +++ b/Mage.Sets/src/mage/cards/n/NightskyMimic.java @@ -32,7 +32,7 @@ public final class NightskyMimic extends CardImpl { filter.add(new ColorPredicate(ObjectColor.BLACK)); } - private String rule = "Whenever you cast a spell that's both white and black, {this} has base power and toughness 4/4 until end of turn and gains flying until end of turn."; + private static final String rule = "Whenever you cast a spell that's both white and black, {this} has base power and toughness 4/4 until end of turn and gains flying until end of turn."; public NightskyMimic(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W/B}"); diff --git a/Mage.Sets/src/mage/cards/n/NightsquadCommando.java b/Mage.Sets/src/mage/cards/n/NightsquadCommando.java index 8ae8c016bc7..90943a1c836 100644 --- a/Mage.Sets/src/mage/cards/n/NightsquadCommando.java +++ b/Mage.Sets/src/mage/cards/n/NightsquadCommando.java @@ -8,7 +8,6 @@ import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.hint.common.RaidHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.SubType; import mage.game.permanent.token.HumanSoldierToken; @@ -31,12 +30,10 @@ public final class NightsquadCommando extends CardImpl { // When Nightsquad Commando enters the battlefield, if you attacked this turn, create a 1/1 white Human Soldier creature token. this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new HumanSoldierToken())), - RaidCondition.instance, "When {this} enters the battlefield, " + - "if you attacked this turn, create a 1/1 white Human Soldier creature token.") - .setAbilityWord(AbilityWord.RAID) - .addHint(RaidHint.instance), - new PlayerAttackedWatcher()); + new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new HumanSoldierToken())), + RaidCondition.instance, "When {this} enters the battlefield, " + + "if you attacked this turn, create a 1/1 white Human Soldier creature token." + ).addHint(RaidHint.instance), new PlayerAttackedWatcher()); } private NightsquadCommando(final NightsquadCommando card) { diff --git a/Mage.Sets/src/mage/cards/n/NihilSpellbomb.java b/Mage.Sets/src/mage/cards/n/NihilSpellbomb.java index eb1849d20eb..17d84c5f541 100644 --- a/Mage.Sets/src/mage/cards/n/NihilSpellbomb.java +++ b/Mage.Sets/src/mage/cards/n/NihilSpellbomb.java @@ -1,7 +1,5 @@ - package mage.cards.n; -import java.util.UUID; import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; @@ -13,25 +11,30 @@ import mage.abilities.effects.common.ExileGraveyardAllTargetPlayerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Zone; import mage.target.TargetPlayer; +import java.util.UUID; + /** - * * @author North */ public final class NihilSpellbomb extends CardImpl { public NihilSpellbomb(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{1}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}"); // {T}, Sacrifice Nihil Spellbomb: Exile all cards from target player's graveyard. - SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileGraveyardAllTargetPlayerEffect(), new TapSourceCost()); + SimpleActivatedAbility ability = new SimpleActivatedAbility( + new ExileGraveyardAllTargetPlayerEffect(), new TapSourceCost() + ); ability.addCost(new SacrificeSourceCost()); ability.addTarget(new TargetPlayer()); this.addAbility(ability); + // When Nihil Spellbomb is put into a graveyard from the battlefield, you may pay {B}. If you do, draw a card. - this.addAbility(new DiesSourceTriggeredAbility(new DoIfCostPaid(new DrawCardSourceControllerEffect(1), new ManaCostsImpl("{B}")), false)); + this.addAbility(new DiesSourceTriggeredAbility(new DoIfCostPaid( + new DrawCardSourceControllerEffect(1), new ManaCostsImpl<>("{B}") + ), false).setTriggerPhrase("When {this} is put into a graveyard from the battlefield, ")); } private NihilSpellbomb(final NihilSpellbomb card) { diff --git a/Mage.Sets/src/mage/cards/n/Nihiloor.java b/Mage.Sets/src/mage/cards/n/Nihiloor.java index 26a3def0d6a..72623718818 100644 --- a/Mage.Sets/src/mage/cards/n/Nihiloor.java +++ b/Mage.Sets/src/mage/cards/n/Nihiloor.java @@ -66,7 +66,7 @@ public final class Nihiloor extends CardImpl { class NihiloorControlEffect extends OneShotEffect { - private static final FilterPermanent filter + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("untapped creatured you control"); static { @@ -74,6 +74,7 @@ class NihiloorControlEffect extends OneShotEffect { } private static final class NihiloorPredicate implements Predicate { + private final Permanent permanent; private final UUID playerId; @@ -85,15 +86,16 @@ class NihiloorControlEffect extends OneShotEffect { @Override public boolean apply(Permanent input, Game game) { return input.isControlledBy(playerId) + && input.isCreature(game) && input.getPower().getValue() <= permanent.getPower().getValue(); } } NihiloorControlEffect() { super(Outcome.Benefit); - staticText = "for each opponent, tap up to one untapped creature you control. When you do, " + - "gain control of target creature that player controls with power less than " + - "or equal to the tapped creature's power for as long as you control {this}"; + staticText = "for each opponent, tap up to one untapped creature you control. When you do, " + + "gain control of target creature that player controls with power less than " + + "or equal to the tapped creature's power for as long as you control {this}"; } private NihiloorControlEffect(final NihiloorControlEffect effect) { @@ -125,13 +127,13 @@ class NihiloorControlEffect extends OneShotEffect { } FilterPermanent filter2 = new FilterPermanent( "creature controlled by " + opponent.getName() - + " with power " + permanent.getPower().getValue() + " or less" + + " with power " + permanent.getPower().getValue() + " or less" ); filter2.add(new NihiloorPredicate(permanent, playerId)); ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( new GainControlTargetEffect(Duration.Custom, true), - false, "gain control of target creature that player controls with " + - "power less than or equal to the tapped creature's power for as long as you control {this}" + false, "gain control of target creature that player controls with " + + "power less than or equal to the tapped creature's power for as long as you control {this}" ); ability.addTarget(new TargetPermanent(filter2)); game.fireReflexiveTriggeredAbility(ability, source); @@ -159,7 +161,8 @@ class NihiloorLoseLifeEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(game.getControllerId(getTargetPointer().getFirst(game, source))); - return player != null && player.loseLife(2, game, source, false) > 0; + Player owner = game.getPlayer(game.getOwnerId(getTargetPointer().getFirst(game, source))); + return owner != null + && owner.loseLife(2, game, source, false) > 0; } } diff --git a/Mage.Sets/src/mage/cards/n/NikaraLairScavenger.java b/Mage.Sets/src/mage/cards/n/NikaraLairScavenger.java index cbfc63ea250..22747e17551 100644 --- a/Mage.Sets/src/mage/cards/n/NikaraLairScavenger.java +++ b/Mage.Sets/src/mage/cards/n/NikaraLairScavenger.java @@ -45,7 +45,7 @@ public final class NikaraLairScavenger extends CardImpl { this.addAbility(new PartnerWithAbility("Yannik, Scavenging Sentinel")); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // Whenever another creature you control leaves the battlefield, if it had one or more counters on it, you draw a card and lose 1 life. Ability ability = new LeavesBattlefieldAllTriggeredAbility(new DrawCardSourceControllerEffect(1) diff --git a/Mage.Sets/src/mage/cards/n/NikkoOnna.java b/Mage.Sets/src/mage/cards/n/NikkoOnna.java index db9486ad721..0db75a430b1 100644 --- a/Mage.Sets/src/mage/cards/n/NikkoOnna.java +++ b/Mage.Sets/src/mage/cards/n/NikkoOnna.java @@ -33,7 +33,7 @@ public final class NikkoOnna extends CardImpl { this.addAbility(ability); // Whenever you cast a Spirit or Arcane spell, you may return Nikko-Onna to its owner's hand. - this.addAbility(new SpellCastControllerTriggeredAbility(new ReturnToHandSourceEffect(true), StaticFilters.SPIRIT_OR_ARCANE_CARD, true)); + this.addAbility(new SpellCastControllerTriggeredAbility(new ReturnToHandSourceEffect(true), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true)); } private NikkoOnna(final NikkoOnna card) { diff --git a/Mage.Sets/src/mage/cards/n/NikoAris.java b/Mage.Sets/src/mage/cards/n/NikoAris.java index 5f2871f4b55..84c23b95cb2 100644 --- a/Mage.Sets/src/mage/cards/n/NikoAris.java +++ b/Mage.Sets/src/mage/cards/n/NikoAris.java @@ -4,7 +4,6 @@ import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.LoyaltyAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.MultipliedValue; import mage.abilities.dynamicvalue.common.CardsDrawnThisTurnDynamicValue; @@ -52,7 +51,7 @@ public final class NikoAris extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.NIKO); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // When Niko Aris enters the battlefield, create X Shard tokens. this.addAbility(new EntersBattlefieldTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/n/NikoDefiesDestiny.java b/Mage.Sets/src/mage/cards/n/NikoDefiesDestiny.java index 6a56e48c04f..aff6ac2688b 100644 --- a/Mage.Sets/src/mage/cards/n/NikoDefiesDestiny.java +++ b/Mage.Sets/src/mage/cards/n/NikoDefiesDestiny.java @@ -55,7 +55,7 @@ public final class NikoDefiesDestiny extends CardImpl { this.subtype.add(SubType.SAGA); // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) - SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); + SagaAbility sagaAbility = new SagaAbility(this); // I — You gain 2 life for each foretold card you own in exile. sagaAbility.addChapterEffect( @@ -110,7 +110,6 @@ enum NikoDefiesDestinyValue implements DynamicValue { Cards cardsForetoldInExileZones = new CardsImpl(); FilterCard filter = new FilterCard(); filter.add(new OwnerIdPredicate(controller.getId())); - filter.add(new AbilityPredicate(ForetellAbility.class)); for (ExileZone exile : exileZones) { for (Card card : exile.getCards(filter, game)) { // verify that the card is actually Foretold diff --git a/Mage.Sets/src/mage/cards/n/NimanaSkitterSneak.java b/Mage.Sets/src/mage/cards/n/NimanaSkitterSneak.java index 87769a082af..65cbac10a1a 100644 --- a/Mage.Sets/src/mage/cards/n/NimanaSkitterSneak.java +++ b/Mage.Sets/src/mage/cards/n/NimanaSkitterSneak.java @@ -37,7 +37,8 @@ public final class NimanaSkitterSneak extends CardImpl { )); ability.addEffect(new ConditionalContinuousEffect( new GainAbilitySourceEffect(new MenaceAbility()), - CardsInOpponentGraveyardCondition.EIGHT, "and has menace" + CardsInOpponentGraveyardCondition.EIGHT, "and has menace. " + + "(It can't be blocked except by two or more creatures.)" )); this.addAbility(ability.addHint(CardsInOpponentGraveyardCondition.EIGHT.getHint())); } diff --git a/Mage.Sets/src/mage/cards/n/NinjaOfTheDeepHours.java b/Mage.Sets/src/mage/cards/n/NinjaOfTheDeepHours.java index 08d14ed8663..b5254ee23e7 100644 --- a/Mage.Sets/src/mage/cards/n/NinjaOfTheDeepHours.java +++ b/Mage.Sets/src/mage/cards/n/NinjaOfTheDeepHours.java @@ -27,7 +27,7 @@ public final class NinjaOfTheDeepHours extends CardImpl { this.toughness = new MageInt(2); // Ninjutsu {1}{U} ({1}{U}, Return an unblocked attacker you control to hand: Put this card onto the battlefield from your hand tapped and attacking.) - this.addAbility(new NinjutsuAbility(new ManaCostsImpl("{1}{U}"))); + this.addAbility(new NinjutsuAbility("{1}{U}")); // Whenever Ninja of the Deep Hours deals combat damage to a player, you may draw a card. this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new DrawCardSourceControllerEffect(1), true, false)); diff --git a/Mage.Sets/src/mage/cards/n/NinjaOfTheNewMoon.java b/Mage.Sets/src/mage/cards/n/NinjaOfTheNewMoon.java index a6fd06839b2..92a5372415e 100644 --- a/Mage.Sets/src/mage/cards/n/NinjaOfTheNewMoon.java +++ b/Mage.Sets/src/mage/cards/n/NinjaOfTheNewMoon.java @@ -24,7 +24,7 @@ public final class NinjaOfTheNewMoon extends CardImpl { this.toughness = new MageInt(3); // Ninjutsu {3}{B} - this.addAbility(new NinjutsuAbility(new ManaCostsImpl("{3}{B}"))); + this.addAbility(new NinjutsuAbility("{3}{B}")); } private NinjaOfTheNewMoon(final NinjaOfTheNewMoon card) { diff --git a/Mage.Sets/src/mage/cards/n/NinjasKunai.java b/Mage.Sets/src/mage/cards/n/NinjasKunai.java new file mode 100644 index 00000000000..55275ef79a1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NinjasKunai.java @@ -0,0 +1,98 @@ +package mage.cards.n; + +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.SacrificeAttachmentCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.GainAbilityWithAttachmentEffect; +import mage.abilities.keyword.EquipAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetAnyTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class NinjasKunai extends CardImpl { + + public NinjasKunai(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}"); + + this.subtype.add(SubType.EQUIPMENT); + + // Equipped creature has "{1}, {T}, Sacrifice Ninja's Kunai: Ninja's Kunai deals 3 damage to any target." + this.addAbility(new SimpleStaticAbility(new GainAbilityWithAttachmentEffect( + "equipped creature has \"{1}, {T}, Sacrifice {this}: {this} deals 3 damage to any target.\"", + new NinjasKunaiEffect(), new TargetAnyTarget(), new SacrificeAttachmentCost(), new GenericManaCost(1), new TapSourceCost() + ))); + + // Equip {1} + this.addAbility(new EquipAbility(1)); + } + + private NinjasKunai(final NinjasKunai card) { + super(card); + } + + @Override + public NinjasKunai copy() { + return new NinjasKunai(this); + } +} + +class NinjasKunaiEffect extends OneShotEffect { + + NinjasKunaiEffect() { + super(Outcome.Benefit); + } + + private NinjasKunaiEffect(final NinjasKunaiEffect effect) { + super(effect); + } + + @Override + public NinjasKunaiEffect copy() { + return new NinjasKunaiEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Object object = getValue("attachedPermanent"); + Player player = game.getPlayer(source.getControllerId()); + if (!(object instanceof Permanent) || player == null) { + return false; + } + Permanent permanent = (Permanent) object; + Permanent targetedPermanent = game.getPermanent(source.getFirstTarget()); + if (targetedPermanent == null) { + Player targetedPlayer = game.getPlayer(source.getFirstTarget()); + if (targetedPlayer != null) { + targetedPlayer.damage(3, permanent.getId(), source, game); + } + } else { + targetedPermanent.damage(3, permanent.getId(), source, game); + } + return true; + } + + @Override + public String getText(Mode mode) { + String name = "Ninja's Kunai"; + Object object = getValue("attachedPermanent"); + if (object instanceof Permanent) { + name = ((Permanent) object).getName(); + } + return name + "deals 3 damage to target any target."; + } +} diff --git a/Mage.Sets/src/mage/cards/n/NissaGenesisMage.java b/Mage.Sets/src/mage/cards/n/NissaGenesisMage.java index c1c242dedcc..1ec5a10b4c3 100644 --- a/Mage.Sets/src/mage/cards/n/NissaGenesisMage.java +++ b/Mage.Sets/src/mage/cards/n/NissaGenesisMage.java @@ -4,7 +4,6 @@ package mage.cards.n; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.common.LookLibraryAndPickControllerEffect; import mage.abilities.effects.common.UntapTargetEffect; @@ -42,7 +41,7 @@ public final class NissaGenesisMage extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.NISSA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); //+2: Untap up to two target creatures and up to two target lands. Ability ability = new LoyaltyAbility(new UntapTargetEffect(false).setText("Untap up to two target creatures and up to two target lands"), +2); diff --git a/Mage.Sets/src/mage/cards/n/NissaNaturesArtisan.java b/Mage.Sets/src/mage/cards/n/NissaNaturesArtisan.java index e8ad1de5d8d..f7bb433f727 100644 --- a/Mage.Sets/src/mage/cards/n/NissaNaturesArtisan.java +++ b/Mage.Sets/src/mage/cards/n/NissaNaturesArtisan.java @@ -1,35 +1,24 @@ package mage.cards.n; -import java.util.LinkedHashSet; -import java.util.Set; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.continuous.BoostControlledEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.abilities.keyword.TrampleAbility; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.cards.Cards; -import mage.cards.CardsImpl; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.SuperType; -import mage.constants.Zone; +import mage.cards.*; +import mage.constants.*; import mage.filter.common.FilterLandCard; import mage.game.Game; import mage.players.Player; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.UUID; + /** - * * @author fireshoes */ public final class NissaNaturesArtisan extends CardImpl { @@ -39,7 +28,7 @@ public final class NissaNaturesArtisan extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.NISSA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +3: You gain 3 life. this.addAbility(new LoyaltyAbility(new GainLifeEffect(3), 3)); @@ -49,13 +38,12 @@ public final class NissaNaturesArtisan extends CardImpl { this.addAbility(new LoyaltyAbility(new NissaNaturesArtisanEffect(), -4)); // -12: Creatures you control get +5/+5 and gain trample until end of turn. - Effect effect = new BoostControlledEffect(5, 5, Duration.EndOfTurn); - effect.setText("Creature you control get +5/+5"); - LoyaltyAbility ability = new LoyaltyAbility(effect, -12); - Effect effect2 = new GainAbilityControlledEffect( - TrampleAbility.getInstance(), Duration.EndOfTurn); - effect2.setText("and gain trample until end of turn"); - ability.addEffect(effect2); + LoyaltyAbility ability = new LoyaltyAbility(new BoostControlledEffect( + 5, 5, Duration.EndOfTurn + ).setText("creatures you control get +5/+5"), -12); + ability.addEffect(new GainAbilityControlledEffect( + TrampleAbility.getInstance(), Duration.EndOfTurn + ).setText("and gain trample until end of turn")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/n/NissaOfShadowedBoughs.java b/Mage.Sets/src/mage/cards/n/NissaOfShadowedBoughs.java index 1b3a3667d46..b805cc44228 100644 --- a/Mage.Sets/src/mage/cards/n/NissaOfShadowedBoughs.java +++ b/Mage.Sets/src/mage/cards/n/NissaOfShadowedBoughs.java @@ -3,7 +3,6 @@ package mage.cards.n; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.common.LandfallAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.UntapTargetEffect; import mage.abilities.effects.common.continuous.BecomesCreatureTargetEffect; @@ -42,7 +41,7 @@ public final class NissaOfShadowedBoughs extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.NISSA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // Landfall — Whenever a land enters the battlefield under your control, put a loyalty counter on Nissa of Shadowed Boughs. this.addAbility(new LandfallAbility(new AddCountersSourceEffect(CounterType.LOYALTY.createInstance()))); diff --git a/Mage.Sets/src/mage/cards/n/NissaRevane.java b/Mage.Sets/src/mage/cards/n/NissaRevane.java index 794f46ea940..3e3d1382640 100644 --- a/Mage.Sets/src/mage/cards/n/NissaRevane.java +++ b/Mage.Sets/src/mage/cards/n/NissaRevane.java @@ -4,7 +4,6 @@ import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; import mage.cards.CardImpl; @@ -14,7 +13,6 @@ import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.SuperType; import mage.filter.FilterCard; -import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.mageobject.NamePredicate; import mage.game.Game; @@ -38,7 +36,7 @@ public final class NissaRevane extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{2}{G}{G}"); this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.NISSA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(2)); + this.setStartingLoyalty(2); LoyaltyAbility ability1 = new LoyaltyAbility(new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(1, nissasChosenFilter)), 1); this.addAbility(ability1); diff --git a/Mage.Sets/src/mage/cards/n/NissaSageAnimist.java b/Mage.Sets/src/mage/cards/n/NissaSageAnimist.java index 0d50bfa2ba0..c212bc3aaf8 100644 --- a/Mage.Sets/src/mage/cards/n/NissaSageAnimist.java +++ b/Mage.Sets/src/mage/cards/n/NissaSageAnimist.java @@ -4,7 +4,6 @@ import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; @@ -38,7 +37,7 @@ public final class NissaSageAnimist extends CardImpl { this.nightCard = true; - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +1: Reveal the top card of your library. If it's a land card, put it onto the battlefield. Otherwise, put it into your hand. this.addAbility(new LoyaltyAbility(new NissaSageAnimistPlusOneEffect(), 1)); diff --git a/Mage.Sets/src/mage/cards/n/NissaStewardOfElements.java b/Mage.Sets/src/mage/cards/n/NissaStewardOfElements.java index 60717a9fbcc..b5f975d4060 100644 --- a/Mage.Sets/src/mage/cards/n/NissaStewardOfElements.java +++ b/Mage.Sets/src/mage/cards/n/NissaStewardOfElements.java @@ -1,21 +1,21 @@ package mage.cards.n; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.dynamicvalue.common.CountersSourceCount; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.EntersBattlefieldWithXCountersEffect; import mage.abilities.effects.common.UntapTargetEffect; import mage.abilities.effects.common.continuous.BecomesCreatureTargetEffect; import mage.abilities.effects.keyword.ScryEffect; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.HasteAbility; -import mage.cards.*; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; import mage.constants.*; import mage.counters.CounterType; import mage.filter.common.FilterControlledLandPermanent; @@ -27,8 +27,9 @@ import mage.game.permanent.token.TokenImpl; import mage.players.Player; import mage.target.TargetPermanent; +import java.util.UUID; + /** - * * @author fireshoes */ public final class NissaStewardOfElements extends CardImpl { @@ -38,9 +39,7 @@ public final class NissaStewardOfElements extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.NISSA); - Ability abilityETB = new EntersBattlefieldAbility(new EntersBattlefieldWithXCountersEffect(CounterType.LOYALTY.createInstance())); - abilityETB.setRuleVisible(false); - this.addAbility(abilityETB); + this.setStartingLoyalty(-2); // -2 loyalty means X // +2: Scry 2. this.addAbility(new LoyaltyAbility(new ScryEffect(2), 2)); diff --git a/Mage.Sets/src/mage/cards/n/NissaVastwoodSeer.java b/Mage.Sets/src/mage/cards/n/NissaVastwoodSeer.java index 0323490ff24..5d05e75c2b9 100644 --- a/Mage.Sets/src/mage/cards/n/NissaVastwoodSeer.java +++ b/Mage.Sets/src/mage/cards/n/NissaVastwoodSeer.java @@ -3,7 +3,7 @@ package mage.cards.n; import java.util.UUID; import mage.MageInt; -import mage.abilities.Gender; +import mage.abilities.Pronoun; import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; @@ -40,8 +40,7 @@ public final class NissaVastwoodSeer extends CardImpl { this.subtype.add(SubType.SCOUT); this.power = new MageInt(2); this.toughness = new MageInt(2); - - this.transformable = true; + this.secondSideCardClazz = NissaSageAnimist.class; // When Nissa, Vastwood Seer enters the battlefield, you may search your library for a basic Forest card, reveal it, put it into your hand, then shuffle your library. @@ -50,7 +49,7 @@ public final class NissaVastwoodSeer extends CardImpl { // Whenever a land enters the battlefield under your control, if you control seven or more lands, exile Nissa, then return her to the battlefield transformed under her owner's control. this.addAbility(new TransformAbility()); this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldControlledTriggeredAbility(new ExileAndReturnTransformedSourceEffect(Gender.FEMALE), new FilterLandPermanent()), + new EntersBattlefieldControlledTriggeredAbility(new ExileAndReturnTransformedSourceEffect(Pronoun.SHE), new FilterLandPermanent()), new PermanentsOnTheBattlefieldCondition(new FilterLandPermanent(), ComparisonType.MORE_THAN, 6, true), "Whenever a land enters the battlefield under your control, if you control seven or more lands, exile {this}, then return her to the battlefield transformed under her owner's control.")); } diff --git a/Mage.Sets/src/mage/cards/n/NissaVitalForce.java b/Mage.Sets/src/mage/cards/n/NissaVitalForce.java index 5150a71ecc7..6ad1c2decec 100644 --- a/Mage.Sets/src/mage/cards/n/NissaVitalForce.java +++ b/Mage.Sets/src/mage/cards/n/NissaVitalForce.java @@ -1,22 +1,17 @@ package mage.cards.n; -import java.util.UUID; import mage.MageInt; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.common.GetEmblemEffect; -import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; import mage.abilities.effects.common.UntapTargetEffect; import mage.abilities.effects.common.continuous.BecomesCreatureTargetEffect; import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.SuperType; -import mage.constants.TargetController; +import mage.constants.*; +import mage.filter.FilterCard; import mage.filter.common.FilterLandPermanent; import mage.filter.common.FilterPermanentCard; import mage.game.command.emblems.NissaVitalForceEmblem; @@ -24,13 +19,15 @@ import mage.game.permanent.token.TokenImpl; import mage.target.common.TargetCardInYourGraveyard; import mage.target.common.TargetLandPermanent; +import java.util.UUID; + /** - * * @author fireshoes */ public final class NissaVitalForce extends CardImpl { private static final FilterLandPermanent filter = new FilterLandPermanent("land you control"); + private static final FilterCard filter2 = new FilterPermanentCard("permanent card from your graveyard"); static { filter.add(TargetController.YOU.getControllerPredicate()); @@ -41,17 +38,19 @@ public final class NissaVitalForce extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.NISSA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +1: Untap target land you control. Until your next turn, it becomes a 5/5 Elemental creature with haste. It's still a land. LoyaltyAbility ability = new LoyaltyAbility(new UntapTargetEffect(), 1); - ability.addEffect(new BecomesCreatureTargetEffect(new NissaVitalForceToken(), false, true, Duration.UntilYourNextTurn)); + ability.addEffect(new BecomesCreatureTargetEffect( + new NissaVitalForceToken(), false, true, Duration.UntilYourNextTurn + ).setText("Until your next turn, it becomes a 5/5 Elemental creature with haste. It's still a land")); ability.addTarget(new TargetLandPermanent(filter)); this.addAbility(ability); // -3: Return target permanent card from your graveyard to your hand. - ability = new LoyaltyAbility(new ReturnToHandTargetEffect(), -3); - ability.addTarget(new TargetCardInYourGraveyard(new FilterPermanentCard("permanent card from your graveyard"))); + ability = new LoyaltyAbility(new ReturnFromGraveyardToHandTargetEffect(), -3); + ability.addTarget(new TargetCardInYourGraveyard(filter2)); this.addAbility(ability); // -6: You get an emblem with "Whenever a land enters the battlefield under your control, you may draw a card." @@ -79,6 +78,7 @@ class NissaVitalForceToken extends TokenImpl { this.toughness = new MageInt(5); this.addAbility(HasteAbility.getInstance()); } + public NissaVitalForceToken(final NissaVitalForceToken token) { super(token); } diff --git a/Mage.Sets/src/mage/cards/n/NissaVoiceOfZendikar.java b/Mage.Sets/src/mage/cards/n/NissaVoiceOfZendikar.java index 65d034ef764..b3e25056c9e 100644 --- a/Mage.Sets/src/mage/cards/n/NissaVoiceOfZendikar.java +++ b/Mage.Sets/src/mage/cards/n/NissaVoiceOfZendikar.java @@ -3,7 +3,6 @@ package mage.cards.n; import java.util.UUID; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.Effect; import mage.abilities.effects.common.CreateTokenEffect; @@ -38,7 +37,7 @@ public final class NissaVoiceOfZendikar extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.NISSA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +1: Create a 0/1 green Plant creature token. this.addAbility(new LoyaltyAbility(new CreateTokenEffect(new PlantToken()), 1)); diff --git a/Mage.Sets/src/mage/cards/n/NissaWhoShakesTheWorld.java b/Mage.Sets/src/mage/cards/n/NissaWhoShakesTheWorld.java index 3f85834cac9..2ff7174e0f5 100644 --- a/Mage.Sets/src/mage/cards/n/NissaWhoShakesTheWorld.java +++ b/Mage.Sets/src/mage/cards/n/NissaWhoShakesTheWorld.java @@ -4,7 +4,6 @@ import mage.MageInt; import mage.Mana; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.common.GetEmblemEffect; import mage.abilities.effects.common.UntapTargetEffect; import mage.abilities.effects.common.continuous.BecomesCreatureTargetEffect; @@ -52,7 +51,7 @@ public final class NissaWhoShakesTheWorld extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.NISSA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // Whenever you tap a Forest for mana, add an additional {G}. this.addAbility(new NissaWhoShakesTheWorldTriggeredAbility()); diff --git a/Mage.Sets/src/mage/cards/n/NissaWorldwaker.java b/Mage.Sets/src/mage/cards/n/NissaWorldwaker.java index 9eab0f0dfd1..3318c4d700c 100644 --- a/Mage.Sets/src/mage/cards/n/NissaWorldwaker.java +++ b/Mage.Sets/src/mage/cards/n/NissaWorldwaker.java @@ -4,7 +4,6 @@ package mage.cards.n; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.UntapTargetEffect; @@ -40,7 +39,7 @@ public final class NissaWorldwaker extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.NISSA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +1: Target land you control becomes a 4/4 Elemental creature with trample. It's still a land. LoyaltyAbility ability = new LoyaltyAbility(new BecomesCreatureTargetEffect(new NissaWorldwakerToken(), false, true, Duration.Custom), 1); diff --git a/Mage.Sets/src/mage/cards/n/NissasChosen.java b/Mage.Sets/src/mage/cards/n/NissasChosen.java index 04b22398cc0..4a7568ae43b 100644 --- a/Mage.Sets/src/mage/cards/n/NissasChosen.java +++ b/Mage.Sets/src/mage/cards/n/NissasChosen.java @@ -52,7 +52,7 @@ class NissasChosenEffect extends ReplacementEffectImpl { public NissasChosenEffect() { super(Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = "If {this} would be put into a graveyard from the battlefield, put it on the bottom of its owner's library instead"; + staticText = "If {this} would die, put it on the bottom of its owner's library instead"; } public NissasChosenEffect(final NissasChosenEffect effect) { diff --git a/Mage.Sets/src/mage/cards/n/NissasJudgment.java b/Mage.Sets/src/mage/cards/n/NissasJudgment.java index e97eafd7dc1..751ead12ead 100644 --- a/Mage.Sets/src/mage/cards/n/NissasJudgment.java +++ b/Mage.Sets/src/mage/cards/n/NissasJudgment.java @@ -10,9 +10,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.TargetController; -import mage.counters.CounterType; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -25,12 +23,6 @@ import mage.target.targetpointer.SecondTargetPointer; */ public final class NissasJudgment extends CardImpl { - private static final FilterCreaturePermanent FILTER = new FilterCreaturePermanent("creature an opponent controls"); - - static { - FILTER.add(TargetController.OPPONENT.getControllerPredicate()); - } - public NissasJudgment(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{G}"); @@ -41,7 +33,7 @@ public final class NissasJudgment extends CardImpl { // Choose up to one target creature an opponent controls. Each creature you control with a +1/+1 counter on it deals damage equal to its power to that creature. effect = new NissasJudgmentEffect(); effect.setTargetPointer(new SecondTargetPointer()); // First target is used by Support - getSpellAbility().addTarget(new TargetCreaturePermanent(0, 1, FILTER, false)); + getSpellAbility().addTarget(new TargetCreaturePermanent(0, 1, StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE, false)); getSpellAbility().addEffect(effect); } @@ -57,14 +49,6 @@ public final class NissasJudgment extends CardImpl { class NissasJudgmentEffect extends OneShotEffect { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - private static final FilterCreaturePermanent filterWithCounter = new FilterCreaturePermanent(); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - filterWithCounter.add(CounterType.P1P1.getPredicate()); - } - public NissasJudgmentEffect() { super(Outcome.Damage); this.staticText = "Choose up to one target creature an opponent controls. Each creature you control with a +1/+1 counter on it deals damage equal to its power to that creature"; @@ -85,7 +69,7 @@ class NissasJudgmentEffect extends OneShotEffect { if (controller != null) { Permanent targetCreature = game.getPermanent(getTargetPointer().getFirst(game, source)); if (targetCreature != null) { - for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filterWithCounter, controller.getId(), game)) { + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_CONTROLLED_CREATURE_P1P1, controller.getId(), game)) { if (permanent.getPower().getValue() > 0) { targetCreature.damage(permanent.getPower().getValue(), permanent.getId(), source, game, false, true); } diff --git a/Mage.Sets/src/mage/cards/n/NivixCyclops.java b/Mage.Sets/src/mage/cards/n/NivixCyclops.java index 9b7a476c0b6..9c9bd72bc27 100644 --- a/Mage.Sets/src/mage/cards/n/NivixCyclops.java +++ b/Mage.Sets/src/mage/cards/n/NivixCyclops.java @@ -4,19 +4,15 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SpellCastControllerTriggeredAbility; -import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.common.combat.CanAttackAsThoughItDidntHaveDefenderSourceEffect; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.keyword.DefenderAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AsThoughEffectType; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.Outcome; import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.permanent.Permanent; /** * @@ -40,9 +36,8 @@ public final class NivixCyclops extends CardImpl { StaticFilters.FILTER_SPELL_AN_INSTANT_OR_SORCERY, false ); - ability.addEffect(new AsThoughNoDefenderEffect()); + ability.addEffect(new CanAttackAsThoughItDidntHaveDefenderSourceEffect(Duration.EndOfTurn, "and")); this.addAbility(ability); - } private NivixCyclops(final NivixCyclops card) { @@ -54,35 +49,3 @@ public final class NivixCyclops extends CardImpl { return new NivixCyclops(this); } } - -class AsThoughNoDefenderEffect extends AsThoughEffectImpl { - - public AsThoughNoDefenderEffect() { - super(AsThoughEffectType.ATTACK, Duration.EndOfTurn, Outcome.Benefit); - staticText = "and it can attack as though it didn't have defender"; - } - - public AsThoughNoDefenderEffect(final AsThoughNoDefenderEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public AsThoughNoDefenderEffect copy() { - return new AsThoughNoDefenderEffect(this); - } - - @Override - public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { - Permanent nivixCyclops = game.getPermanent(source.getSourceId()); - if (nivixCyclops != null - && nivixCyclops.getAbilities().containsKey(DefenderAbility.getInstance().getId())) { - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/n/NivixGuildmage.java b/Mage.Sets/src/mage/cards/n/NivixGuildmage.java index e04068bd74c..6e6c4ad78bb 100644 --- a/Mage.Sets/src/mage/cards/n/NivixGuildmage.java +++ b/Mage.Sets/src/mage/cards/n/NivixGuildmage.java @@ -24,7 +24,7 @@ import mage.target.TargetSpell; */ public final class NivixGuildmage extends CardImpl { - private static final FilterSpell filter = new FilterSpell("instant or sorcery spell"); + private static final FilterSpell filter = new FilterSpell("instant or sorcery spell you control"); static { filter.add(Predicates.or( diff --git a/Mage.Sets/src/mage/cards/n/NoEscape.java b/Mage.Sets/src/mage/cards/n/NoEscape.java index 384d09ce1e5..e43ba896da4 100644 --- a/Mage.Sets/src/mage/cards/n/NoEscape.java +++ b/Mage.Sets/src/mage/cards/n/NoEscape.java @@ -34,7 +34,7 @@ public final class NoEscape extends CardImpl { this.getSpellAbility().addTarget(new TargetSpell(filter)); // Scry 1. - this.getSpellAbility().addEffect(new ScryEffect(1)); + this.getSpellAbility().addEffect(new ScryEffect(1, false)); } private NoEscape(final NoEscape card) { diff --git a/Mage.Sets/src/mage/cards/n/NobilisOfWar.java b/Mage.Sets/src/mage/cards/n/NobilisOfWar.java index 3dc3af6ddb2..75057c314bc 100644 --- a/Mage.Sets/src/mage/cards/n/NobilisOfWar.java +++ b/Mage.Sets/src/mage/cards/n/NobilisOfWar.java @@ -1,4 +1,3 @@ - package mage.cards.n; import java.util.UUID; @@ -9,10 +8,9 @@ import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.Zone; -import mage.filter.common.FilterAttackingCreature; +import mage.constants.SubType; +import mage.filter.StaticFilters; /** * @@ -30,9 +28,9 @@ public final class NobilisOfWar extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); + // Attacking creatures you control get +2/+0. - BoostControlledEffect boostEffect = new BoostControlledEffect(2, 0, Duration.WhileOnBattlefield, new FilterAttackingCreature("Attacking creatures"), false); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, boostEffect)); + this.addAbility(new SimpleStaticAbility(new BoostControlledEffect(2, 0, Duration.WhileOnBattlefield, StaticFilters.FILTER_ATTACKING_CREATURES))); } private NobilisOfWar(final NobilisOfWar card) { diff --git a/Mage.Sets/src/mage/cards/n/NogginWhack.java b/Mage.Sets/src/mage/cards/n/NogginWhack.java index 929f19feef7..2753905ff2d 100644 --- a/Mage.Sets/src/mage/cards/n/NogginWhack.java +++ b/Mage.Sets/src/mage/cards/n/NogginWhack.java @@ -1,17 +1,13 @@ package mage.cards.n; import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.discard.DiscardCardYouChooseTargetEffect; import mage.abilities.keyword.ProwlAbility; -import mage.cards.*; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.FilterCard; -import mage.game.Game; -import mage.players.Player; -import mage.target.TargetCard; +import mage.constants.TargetController; import mage.target.TargetPlayer; import java.util.List; @@ -26,13 +22,11 @@ public final class NogginWhack extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.TRIBAL, CardType.SORCERY}, "{2}{B}{B}"); this.subtype.add(SubType.ROGUE); - // Prowl {1}{B} this.addAbility(new ProwlAbility(this, "{1}{B}")); // Target player reveals three cards from their hand. You choose two of them. That player discards those cards. - this.getSpellAbility().addEffect(new NogginWhackEffect()); + this.getSpellAbility().addEffect(new DiscardCardYouChooseTargetEffect(2, TargetController.ANY, 3)); this.getSpellAbility().addTarget(new TargetPlayer()); - } private NogginWhack(final NogginWhack card) { @@ -44,57 +38,3 @@ public final class NogginWhack extends CardImpl { return new NogginWhack(this); } } - -class NogginWhackEffect extends OneShotEffect { - - NogginWhackEffect() { - super(Outcome.Benefit); - this.staticText = "Target player reveals three cards from their hand. You choose two of them. That player discards those cards"; - } - - private NogginWhackEffect(final NogginWhackEffect effect) { - super(effect); - } - - @Override - public NogginWhackEffect copy() { - return new NogginWhackEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - Player targetPlayer = game.getPlayer(source.getFirstTarget()); - Card sourceCard = game.getCard(source.getSourceId()); - if (controller == null || targetPlayer == null || sourceCard == null) { - return false; - } - Cards cardsInHand = new CardsImpl(); - cardsInHand.addAll(targetPlayer.getHand()); - - int count = Math.min(cardsInHand.size(), 3); - - TargetCard target = new TargetCard(count, Zone.HAND, new FilterCard()); - Cards revealedCards = new CardsImpl(); - - if (targetPlayer.chooseTarget(Outcome.Discard, cardsInHand, target, source, game)) { - List targets = target.getTargets(); - for (UUID targetId : targets) { - Card card = game.getCard(targetId); - if (card != null) { - revealedCards.add(card); - } - } - } - - int cardsToDiscard = Math.min(revealedCards.size(), 2); - TargetCard targetInHand = new TargetCard(cardsToDiscard, cardsToDiscard, Zone.HAND, new FilterCard("card to discard")); - - if (!revealedCards.isEmpty()) { - targetPlayer.revealCards(source, revealedCards, game); - controller.chooseTarget(Outcome.Exile, revealedCards, targetInHand, source, game); - targetPlayer.discard(new CardsImpl(targetInHand.getTargets()), false, source, game); - } - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/n/NoggleHedgeMage.java b/Mage.Sets/src/mage/cards/n/NoggleHedgeMage.java index 38da0be2459..dc876fe2a81 100644 --- a/Mage.Sets/src/mage/cards/n/NoggleHedgeMage.java +++ b/Mage.Sets/src/mage/cards/n/NoggleHedgeMage.java @@ -34,8 +34,8 @@ public final class NoggleHedgeMage extends CardImpl { filter2.add(SubType.MOUNTAIN.getPredicate()); } - private String rule = "When {this} enters the battlefield, if you control two or more Islands, you may tap two target permanents."; - private String rule2 = "When {this} enters the battlefield, if you control two or more Mountains, you may have {this} deal 2 damage to target player or planeswalker."; + private static final String rule = "When {this} enters the battlefield, if you control two or more Islands, you may tap two target permanents."; + private static final String rule2 = "When {this} enters the battlefield, if you control two or more Mountains, you may have {this} deal 2 damage to target player or planeswalker."; public NoggleHedgeMage(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U/R}"); diff --git a/Mage.Sets/src/mage/cards/n/NorikaYamazakiThePoet.java b/Mage.Sets/src/mage/cards/n/NorikaYamazakiThePoet.java new file mode 100644 index 00000000000..7b817165863 --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NorikaYamazakiThePoet.java @@ -0,0 +1,60 @@ +package mage.cards.n; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksAloneControlledTriggeredAbility; +import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; +import mage.constants.*; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.common.FilterEnchantmentCard; +import mage.filter.predicate.Predicates; +import mage.target.common.TargetCardInYourGraveyard; + +/** + * + * @author weirddan455 + */ +public final class NorikaYamazakiThePoet extends CardImpl { + + private static final FilterControlledPermanent filter = new FilterControlledPermanent("a Samurai or Warrior you control"); + private static final FilterEnchantmentCard filter2 = new FilterEnchantmentCard("enchantment card from your graveyard"); + + static { + filter.add(Predicates.or(SubType.SAMURAI.getPredicate(), SubType.WARRIOR.getPredicate())); + } + + public NorikaYamazakiThePoet(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SAMURAI); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // Whenever a Samurai or Warrior you control attacks alone, you may cast target enchantment card from your graveyard this turn. + Ability ability = new AttacksAloneControlledTriggeredAbility( + new PlayFromNotOwnHandZoneTargetEffect(Zone.GRAVEYARD, TargetController.YOU, Duration.EndOfTurn, false, true) + .setText("you may cast target enchantment card from your graveyard this turn"), + filter, false, false + ); + ability.addTarget(new TargetCardInYourGraveyard(filter2)); + this.addAbility(ability); + } + + private NorikaYamazakiThePoet(final NorikaYamazakiThePoet card) { + super(card); + } + + @Override + public NorikaYamazakiThePoet copy() { + return new NorikaYamazakiThePoet(this); + } +} diff --git a/Mage.Sets/src/mage/cards/n/Norritt.java b/Mage.Sets/src/mage/cards/n/Norritt.java index ae6f04d6f63..21cff23e104 100644 --- a/Mage.Sets/src/mage/cards/n/Norritt.java +++ b/Mage.Sets/src/mage/cards/n/Norritt.java @@ -102,9 +102,9 @@ class NorrittDelayedDestroyEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { DestroyTargetEffect effect = new DestroyTargetEffect(); - effect.setTargetPointer(new FixedTarget(source.getFirstTarget())); + effect.setTargetPointer(new FixedTarget(source.getFirstTarget(), game)); AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility - = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(Zone.ALL, effect, TargetController.ANY, new InvertCondition(TargetAttackedThisTurnCondition.instance)); + = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect, TargetController.ANY, new InvertCondition(TargetAttackedThisTurnCondition.instance)); delayedAbility.getDuration(); delayedAbility.getTargets().addAll(source.getTargets()); game.addDelayedTriggeredAbility(delayedAbility, source); diff --git a/Mage.Sets/src/mage/cards/n/NotoriousAssassin.java b/Mage.Sets/src/mage/cards/n/NotoriousAssassin.java index df0444ea3a6..30ff3a27962 100644 --- a/Mage.Sets/src/mage/cards/n/NotoriousAssassin.java +++ b/Mage.Sets/src/mage/cards/n/NotoriousAssassin.java @@ -1,9 +1,7 @@ - package mage.cards.n; import java.util.UUID; import mage.MageInt; -import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.DiscardCardCost; @@ -15,9 +13,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; -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; /** @@ -26,12 +22,6 @@ import mage.target.common.TargetCreaturePermanent; */ public final class NotoriousAssassin extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public NotoriousAssassin(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}"); this.subtype.add(SubType.HUMAN); @@ -44,7 +34,7 @@ public final class NotoriousAssassin extends CardImpl { Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(true), new ManaCostsImpl("{2}{B}")); ability.addCost(new TapSourceCost()); ability.addCost(new DiscardCardCost()); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/n/NourishingShoal.java b/Mage.Sets/src/mage/cards/n/NourishingShoal.java index 4e0885a8689..67444f4b9e0 100644 --- a/Mage.Sets/src/mage/cards/n/NourishingShoal.java +++ b/Mage.Sets/src/mage/cards/n/NourishingShoal.java @@ -1,4 +1,3 @@ - package mage.cards.n; import mage.ObjectColor; @@ -11,33 +10,34 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterOwnedCard; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardIdPredicate; import mage.filter.predicate.mageobject.ColorPredicate; import mage.target.common.TargetCardInHand; import java.util.UUID; /** - * * @author LevelX2 */ public final class NourishingShoal extends CardImpl { + private static final FilterOwnedCard filter + = new FilterOwnedCard("a red card with mana value X from your hand"); + + static { + filter.add(new ColorPredicate(ObjectColor.GREEN)); + } + public NourishingShoal(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{X}{G}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{X}{G}{G}"); this.subtype.add(SubType.ARCANE); - // You may exile a green card with converted mana cost X from your hand rather than pay Nourishing Shoal's mana cost. - FilterOwnedCard filter = new FilterOwnedCard("a green card with mana value X from your hand"); - filter.add(new ColorPredicate(ObjectColor.GREEN)); - filter.add(Predicates.not(new CardIdPredicate(this.getId()))); // the exile cost can never be paid with the card itself - this.addAbility(new AlternativeCostSourceAbility(new ExileFromHandCost(new TargetCardInHand(filter), true))); + this.addAbility(new AlternativeCostSourceAbility(new ExileFromHandCost( + new TargetCardInHand(filter), true + ))); // You gain X life. this.getSpellAbility().addEffect(new GainLifeEffect(ExileFromHandCostCardConvertedMana.instance)); - } private NourishingShoal(final NourishingShoal card) { @@ -48,4 +48,4 @@ public final class NourishingShoal extends CardImpl { public NourishingShoal copy() { return new NourishingShoal(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/n/NoviceKnight.java b/Mage.Sets/src/mage/cards/n/NoviceKnight.java index 717ed56ba84..f11ae24013c 100644 --- a/Mage.Sets/src/mage/cards/n/NoviceKnight.java +++ b/Mage.Sets/src/mage/cards/n/NoviceKnight.java @@ -4,15 +4,16 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.AsThoughEffectImpl; -import mage.constants.SubType; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalAsThoughEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.combat.CanAttackAsThoughItDidntHaveDefenderSourceEffect; import mage.abilities.keyword.DefenderAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AsThoughEffectType; import mage.constants.CardType; +import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; @@ -35,9 +36,11 @@ public final class NoviceKnight extends CardImpl { this.addAbility(DefenderAbility.getInstance()); // As long as Novice Knight is enchanted or equipped, it can attack as though it didn't have defender. - this.addAbility(new SimpleStaticAbility( - Zone.BATTLEFIELD, new NoviceKnightEffect() - )); + Effect effect = new ConditionalAsThoughEffect( + new CanAttackAsThoughItDidntHaveDefenderSourceEffect(Duration.WhileOnBattlefield), + EnchantedOrEquippedSourceCondition.instance); + effect.setText("As long as {this} is enchanted or equipped, it can attack as though it didn't have defender"); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); } private NoviceKnight(final NoviceKnight card) { @@ -50,44 +53,28 @@ public final class NoviceKnight extends CardImpl { } } -class NoviceKnightEffect extends AsThoughEffectImpl { +enum EnchantedOrEquippedSourceCondition implements Condition { - public NoviceKnightEffect() { - super(AsThoughEffectType.ATTACK, Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = "As long as {this} is enchanted or equipped, " - + "it can attack as though it didn't have defender."; - } - - public NoviceKnightEffect(final NoviceKnightEffect effect) { - super(effect); - } + instance; @Override public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public NoviceKnightEffect copy() { - return new NoviceKnightEffect(this); - } - - @Override - public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - if (!objectId.equals(source.getSourceId())) { - return false; - } Permanent permanent = game.getBattlefield().getPermanent(source.getSourceId()); if (permanent != null) { for (UUID uuid : permanent.getAttachments()) { Permanent attached = game.getBattlefield().getPermanent(uuid); if (attached != null - && (attached.hasSubtype(SubType.EQUIPMENT, game) - || attached.hasSubtype(SubType.AURA, game))) { - return true; + && (attached.isEnchantment(game) + || attached.hasSubtype(SubType.EQUIPMENT, game))) { + return true; } } } return false; } + + @Override + public String toString() { + return "{this} is enchanted or equipped"; + } } diff --git a/Mage.Sets/src/mage/cards/n/NovijenSages.java b/Mage.Sets/src/mage/cards/n/NovijenSages.java index 9d03483bf91..5db1619e8cd 100644 --- a/Mage.Sets/src/mage/cards/n/NovijenSages.java +++ b/Mage.Sets/src/mage/cards/n/NovijenSages.java @@ -24,7 +24,8 @@ import mage.target.TargetPermanent; */ public final class NovijenSages extends CardImpl { - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("creatures you control with a +1/+1 counter on it"); + // Creatures you control with +1/+1 counters, name is what it is to make rules come out + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("among creatures you control"); static { filter.add(CounterType.P1P1.getPredicate()); diff --git a/Mage.Sets/src/mage/cards/n/NoxiousGearhulk.java b/Mage.Sets/src/mage/cards/n/NoxiousGearhulk.java index 8e7ef16071a..5743a737fcb 100644 --- a/Mage.Sets/src/mage/cards/n/NoxiousGearhulk.java +++ b/Mage.Sets/src/mage/cards/n/NoxiousGearhulk.java @@ -38,7 +38,7 @@ public final class NoxiousGearhulk extends CardImpl { this.toughness = new MageInt(4); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // When Noxious Gearhulk enters the battlefield, you may destroy another target creature. If a creature is destroyed this way, you gain life equal to its toughness. Ability ability = new EntersBattlefieldTriggeredAbility(new NoxiousGearhulkEffect()); diff --git a/Mage.Sets/src/mage/cards/n/NoyanDarRoilShaper.java b/Mage.Sets/src/mage/cards/n/NoyanDarRoilShaper.java index 96fb75d16fd..77213c1e226 100644 --- a/Mage.Sets/src/mage/cards/n/NoyanDarRoilShaper.java +++ b/Mage.Sets/src/mage/cards/n/NoyanDarRoilShaper.java @@ -1,4 +1,3 @@ - package mage.cards.n; import java.util.UUID; @@ -33,7 +32,7 @@ import mage.target.targetpointer.FixedTarget; * @author fireshoes */ public final class NoyanDarRoilShaper extends CardImpl { - + private static final FilterSpell filter = new FilterSpell("instant or sorcery card"); static { @@ -43,7 +42,7 @@ public final class NoyanDarRoilShaper extends CardImpl { } public NoyanDarRoilShaper(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{W}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}{U}"); addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.MERFOLK); this.subtype.add(SubType.ALLY); @@ -90,8 +89,8 @@ class NoyanDarEffect extends OneShotEffect { targetId = target.getFirstTarget(); } if (targetId != null) { - FixedTarget fixedTarget = new FixedTarget(targetId); - ContinuousEffect continuousEffect = new BecomesCreatureTargetEffect(new AwakenElementalToken(), false, true, Duration.Custom); + FixedTarget fixedTarget = new FixedTarget(targetId, game); + ContinuousEffect continuousEffect = new BecomesCreatureTargetEffect(new AwakenElementalToken(), false, true, Duration.EndOfGame); continuousEffect.setTargetPointer(fixedTarget); game.addEffect(continuousEffect, source); Effect effect = new AddCountersTargetEffect(CounterType.P1P1.createInstance(3)); @@ -102,7 +101,6 @@ class NoyanDarEffect extends OneShotEffect { } } - class AwakenElementalToken extends TokenImpl { public AwakenElementalToken() { @@ -115,6 +113,7 @@ class AwakenElementalToken extends TokenImpl { this.addAbility(HasteAbility.getInstance()); } + public AwakenElementalToken(final AwakenElementalToken token) { super(token); } diff --git a/Mage.Sets/src/mage/cards/n/NullBrooch.java b/Mage.Sets/src/mage/cards/n/NullBrooch.java index 64a4de391dd..55022d6be35 100644 --- a/Mage.Sets/src/mage/cards/n/NullBrooch.java +++ b/Mage.Sets/src/mage/cards/n/NullBrooch.java @@ -12,8 +12,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Zone; -import mage.filter.FilterSpell; -import mage.filter.predicate.Predicates; +import mage.filter.StaticFilters; import mage.target.TargetSpell; /** @@ -21,12 +20,6 @@ import mage.target.TargetSpell; * @author fireshoes */ public final class NullBrooch extends CardImpl { - - private static final FilterSpell filter = new FilterSpell("noncreature spell"); - - static { - filter.add(Predicates.not(CardType.CREATURE.getPredicate())); - } public NullBrooch(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{4}"); @@ -35,7 +28,7 @@ public final class NullBrooch extends CardImpl { Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CounterTargetEffect(), new ManaCostsImpl("{2}")); ability.addCost(new TapSourceCost()); ability.addCost(new DiscardHandCost()); - ability.addTarget(new TargetSpell(filter)); + ability.addTarget(new TargetSpell(StaticFilters.FILTER_SPELL_NON_CREATURE)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/n/NullpriestOfOblivion.java b/Mage.Sets/src/mage/cards/n/NullpriestOfOblivion.java index d6e357ac779..edf7bd1c416 100644 --- a/Mage.Sets/src/mage/cards/n/NullpriestOfOblivion.java +++ b/Mage.Sets/src/mage/cards/n/NullpriestOfOblivion.java @@ -35,7 +35,7 @@ public final class NullpriestOfOblivion extends CardImpl { this.addAbility(new KickerAbility("{3}{B}")); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // Lifelink this.addAbility(LifelinkAbility.getInstance()); diff --git a/Mage.Sets/src/mage/cards/n/NurturingPresence.java b/Mage.Sets/src/mage/cards/n/NurturingPresence.java new file mode 100644 index 00000000000..e5cae38bfc2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NurturingPresence.java @@ -0,0 +1,60 @@ +package mage.cards.n; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.permanent.token.SpiritWhiteToken; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class NurturingPresence extends CardImpl { + + public NurturingPresence(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // Enchanted creature has "Whenever a creature enters the battlefield under your control, this creature gets +1/+1 until end of turn." + this.addAbility(new SimpleStaticAbility(new GainAbilityAttachedEffect( + new EntersBattlefieldControlledTriggeredAbility( + new BoostSourceEffect(1, 1, Duration.EndOfTurn) + .setText("this creature gets +1/+1 until end of turn"), + StaticFilters.FILTER_PERMANENT_A_CREATURE + ), AttachmentType.AURA + ))); + + // When Nurturing Presence enters the battlefield, create a 1/1 white Spirit creature token with flying. + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new SpiritWhiteToken()))); + } + + private NurturingPresence(final NurturingPresence card) { + super(card); + } + + @Override + public NurturingPresence copy() { + return new NurturingPresence(this); + } +} diff --git a/Mage.Sets/src/mage/cards/n/NyleaGodOfTheHunt.java b/Mage.Sets/src/mage/cards/n/NyleaGodOfTheHunt.java index 9b0635f87a5..85f4456c6fa 100644 --- a/Mage.Sets/src/mage/cards/n/NyleaGodOfTheHunt.java +++ b/Mage.Sets/src/mage/cards/n/NyleaGodOfTheHunt.java @@ -45,7 +45,7 @@ public final class NyleaGodOfTheHunt extends CardImpl { // Other creatures you control have trample. this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( TrampleAbility.getInstance(), Duration.WhileOnBattlefield, - StaticFilters.FILTER_PERMANENT_CREATURE, true + StaticFilters.FILTER_PERMANENT_CREATURES, true ))); // {3}{G}: Target creature gets +2/+2 until end of turn. diff --git a/Mage.Sets/src/mage/cards/o/OKagachiMadeManifest.java b/Mage.Sets/src/mage/cards/o/OKagachiMadeManifest.java new file mode 100644 index 00000000000..ad16a1600fb --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OKagachiMadeManifest.java @@ -0,0 +1,120 @@ +package mage.cards.o; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.InfoEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class OKagachiMadeManifest extends CardImpl { + + public OKagachiMadeManifest(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, ""); + + this.subtype.add(SubType.DRAGON); + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(6); + this.toughness = new MageInt(6); + this.nightCard = true; + + // O-Kagachi Made Manifest is all colors. + this.color.setWhite(true); + this.color.setBlue(true); + this.color.setBlack(true); + this.color.setRed(true); + this.color.setGreen(true); + this.addAbility(new SimpleStaticAbility(new InfoEffect("{this} is all colors"))); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Whenever O-Kagachi Made Manifest attacks, defending player chooses a nonland card in your graveyard. Return that card to your hand. O-Kagachi Made Manifest gets +X/+0 until end of turn, where X is the mana value of that card. + this.addAbility(new AttacksTriggeredAbility( + new OKagachiMadeManifestEffect(), false, null, SetTargetPointer.PLAYER + )); + } + + private OKagachiMadeManifest(final OKagachiMadeManifest card) { + super(card); + } + + @Override + public OKagachiMadeManifest copy() { + return new OKagachiMadeManifest(this); + } +} + +class OKagachiMadeManifestEffect extends OneShotEffect { + + OKagachiMadeManifestEffect() { + super(Outcome.Benefit); + staticText = "defending player chooses a nonland card in your graveyard. Return that card to your hand. " + + "{this} gets +X/+0 until end of turn, where X is the mana value of that card"; + } + + private OKagachiMadeManifestEffect(final OKagachiMadeManifestEffect effect) { + super(effect); + } + + @Override + public OKagachiMadeManifestEffect copy() { + return new OKagachiMadeManifestEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Player player = game.getPlayer(getTargetPointer().getFirst(game, source)); + if (controller == null || player == null) { + return false; + } + Card card; + switch (controller.getGraveyard().count(StaticFilters.FILTER_CARD_A_NON_LAND, game)) { + case 0: + return false; + case 1: + card = controller + .getGraveyard() + .getCards(StaticFilters.FILTER_CARD_A_NON_LAND, game) + .stream() + .findFirst() + .orElse(null); + break; + default: + TargetCard target = new TargetCardInGraveyard(StaticFilters.FILTER_CARD_A_NON_LAND); + target.setNotTarget(true); + player.choose(Outcome.ReturnToHand, controller.getGraveyard(), target, game); + card = game.getCard(target.getFirstTarget()); + } + if (card == null) { + return false; + } + int manaValue = card.getManaValue(); + player.moveCards(card, Zone.HAND, source, game); + if (manaValue > 0) { + game.addEffect(new BoostSourceEffect(manaValue, 0, Duration.EndOfTurn), source); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/o/OakshadeStalker.java b/Mage.Sets/src/mage/cards/o/OakshadeStalker.java new file mode 100644 index 00000000000..ca4969bf919 --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OakshadeStalker.java @@ -0,0 +1,44 @@ +package mage.cards.o; + +import mage.MageInt; +import mage.abilities.common.PayMoreToCastAsThoughtItHadFlashAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.keyword.DayboundAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class OakshadeStalker extends CardImpl { + + public OakshadeStalker(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.RANGER); + this.subtype.add(SubType.WEREWOLF); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + this.secondSideCardClazz = mage.cards.m.MoonlitAmbusher.class; + + // You may cast this spell as though it had flash if you pay {2} more to cast it. + this.addAbility(new PayMoreToCastAsThoughtItHadFlashAbility(this, new ManaCostsImpl<>("{2}"))); + + // Daybound + this.addAbility(new DayboundAbility()); + } + + private OakshadeStalker(final OakshadeStalker card) { + super(card); + } + + @Override + public OakshadeStalker copy() { + return new OakshadeStalker(this); + } +} diff --git a/Mage.Sets/src/mage/cards/o/OathOfChandra.java b/Mage.Sets/src/mage/cards/o/OathOfChandra.java index e57dc281258..0be50f31cfc 100644 --- a/Mage.Sets/src/mage/cards/o/OathOfChandra.java +++ b/Mage.Sets/src/mage/cards/o/OathOfChandra.java @@ -16,7 +16,7 @@ import mage.abilities.effects.common.DamageTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; @@ -29,12 +29,6 @@ import mage.watchers.Watcher; */ public final class OathOfChandra extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public OathOfChandra(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{R}"); addSuperType(SuperType.LEGENDARY); @@ -43,8 +37,9 @@ public final class OathOfChandra extends CardImpl { Effect effect = new DamageTargetEffect(3); effect.setText("it deals 3 damage to target creature an opponent controls"); Ability ability = new EntersBattlefieldTriggeredAbility(effect, false); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); this.addAbility(ability); + // At the beginning of each end step, if a planeswalker entered the battlefield under your control this turn, Oath of Chandra deals 2 damage to each opponent. this.addAbility(new ConditionalInterveningIfTriggeredAbility(new BeginningOfEndStepTriggeredAbility( new DamagePlayersEffect(Outcome.Damage, StaticValue.get(2), TargetController.OPPONENT), diff --git a/Mage.Sets/src/mage/cards/o/OathOfTheAncientWood.java b/Mage.Sets/src/mage/cards/o/OathOfTheAncientWood.java index 87235786993..40ce4a223f5 100644 --- a/Mage.Sets/src/mage/cards/o/OathOfTheAncientWood.java +++ b/Mage.Sets/src/mage/cards/o/OathOfTheAncientWood.java @@ -23,7 +23,7 @@ public final class OathOfTheAncientWood extends CardImpl { // Whenever Oath of the Ancient Wood or another enchantment enters the battlefield under your control, you may put a +1/+1 counter on target creature. Ability ability = new EntersBattlefieldThisOrAnotherTriggeredAbility( new AddCountersTargetEffect(CounterType.P1P1.createInstance()), - StaticFilters.FILTER_ENCHANTMENT_PERMANENT, true, true + StaticFilters.FILTER_PERMANENT_ENCHANTMENT, true, true ); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/o/ObNixilisOfTheBlackOath.java b/Mage.Sets/src/mage/cards/o/ObNixilisOfTheBlackOath.java index 6bd31e361d5..71fcec21b88 100644 --- a/Mage.Sets/src/mage/cards/o/ObNixilisOfTheBlackOath.java +++ b/Mage.Sets/src/mage/cards/o/ObNixilisOfTheBlackOath.java @@ -5,7 +5,6 @@ import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.common.CanBeYourCommanderAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.GetEmblemEffect; @@ -32,7 +31,7 @@ public final class ObNixilisOfTheBlackOath extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.NIXILIS); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +2: Each opponent loses 1 life. You gain life equal to the life lost this way. this.addAbility(new LoyaltyAbility(new ObNixilisOfTheBlackOathEffect1(), 2)); diff --git a/Mage.Sets/src/mage/cards/o/ObNixilisReignited.java b/Mage.Sets/src/mage/cards/o/ObNixilisReignited.java index 3a59579b594..3c96f68cb0a 100644 --- a/Mage.Sets/src/mage/cards/o/ObNixilisReignited.java +++ b/Mage.Sets/src/mage/cards/o/ObNixilisReignited.java @@ -1,7 +1,6 @@ package mage.cards.o; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; @@ -28,7 +27,7 @@ public final class ObNixilisReignited extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.NIXILIS); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +1: You draw a card and you lose 1 life. Effect effect = new DrawCardSourceControllerEffect(1, "you"); diff --git a/Mage.Sets/src/mage/cards/o/ObNixilisTheHateTwisted.java b/Mage.Sets/src/mage/cards/o/ObNixilisTheHateTwisted.java index 71d1cbf9ee5..4a592ad3856 100644 --- a/Mage.Sets/src/mage/cards/o/ObNixilisTheHateTwisted.java +++ b/Mage.Sets/src/mage/cards/o/ObNixilisTheHateTwisted.java @@ -3,7 +3,6 @@ package mage.cards.o; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.common.DrawCardOpponentTriggeredAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageTargetEffect; import mage.cards.CardImpl; @@ -29,11 +28,11 @@ public final class ObNixilisTheHateTwisted extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.NIXILIS); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // Whenever an opponent draws a card, Ob Nixilis, the Hate-Twisted deals 1 damage to that player. this.addAbility(new DrawCardOpponentTriggeredAbility(new DamageTargetEffect( - 1, "that player" + 1, true, "that player" ), false, true)); // -2: Destroy target creature. Its controller draws two cards. diff --git a/Mage.Sets/src/mage/cards/o/ObiWanKenobi.java b/Mage.Sets/src/mage/cards/o/ObiWanKenobi.java index 0ad5ca8a1ca..383dcae1362 100644 --- a/Mage.Sets/src/mage/cards/o/ObiWanKenobi.java +++ b/Mage.Sets/src/mage/cards/o/ObiWanKenobi.java @@ -4,7 +4,6 @@ package mage.cards.o; import java.util.UUID; import mage.abilities.Ability; 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.TapTargetEffect; @@ -31,7 +30,7 @@ public final class ObiWanKenobi extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.OBI_WAN); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +1:Up to one target creature you control gains vigilance and protection from color of your choice until end of turn. Effect effect = new GainAbilityTargetEffect(VigilanceAbility.getInstance(), Duration.EndOfTurn); diff --git a/Mage.Sets/src/mage/cards/o/ObsessiveCollector.java b/Mage.Sets/src/mage/cards/o/ObsessiveCollector.java new file mode 100644 index 00000000000..622f82af119 --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/ObsessiveCollector.java @@ -0,0 +1,92 @@ +package mage.cards.o; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.WardAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.game.Game; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ObsessiveCollector extends CardImpl { + + public ObsessiveCollector(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); + + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Ward {2} + this.addAbility(new WardAbility(new ManaCostsImpl<>("{2}"))); + + // Whenever Obsessive Collector deals combat damage to a player, seek a card with mana value equal to the number of cards in your hand. + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new ObsessiveCollectorEffect(), false)); + } + + private ObsessiveCollector(final ObsessiveCollector card) { + super(card); + } + + @Override + public ObsessiveCollector copy() { + return new ObsessiveCollector(this); + } +} + +enum ObsessiveCollectorPredicate implements ObjectSourcePlayerPredicate { + instance; + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + return game.getPlayer(input.getPlayerId()).getHand().size() == input.getObject().getManaValue(); + } +} + +class ObsessiveCollectorEffect extends OneShotEffect { + + private static final FilterCard filter = new FilterCard(); + + static { + filter.add(ObsessiveCollectorPredicate.instance); + } + + ObsessiveCollectorEffect() { + super(Outcome.Benefit); + staticText = "seek a card with mana value equal to the number of cards in your hand"; + } + + private ObsessiveCollectorEffect(final ObsessiveCollectorEffect effect) { + super(effect); + } + + @Override + public ObsessiveCollectorEffect copy() { + return new ObsessiveCollectorEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + return player != null && player.seekCard(filter, source, game); + } +} diff --git a/Mage.Sets/src/mage/cards/o/ObsidianBattleAxe.java b/Mage.Sets/src/mage/cards/o/ObsidianBattleAxe.java index 80df73eeaa3..0479884ec08 100644 --- a/Mage.Sets/src/mage/cards/o/ObsidianBattleAxe.java +++ b/Mage.Sets/src/mage/cards/o/ObsidianBattleAxe.java @@ -1,4 +1,3 @@ - package mage.cards.o; import java.util.UUID; @@ -23,10 +22,7 @@ import mage.filter.common.FilterCreaturePermanent; */ public final class ObsidianBattleAxe extends CardImpl { - private static final FilterPermanent filter = new FilterCreaturePermanent("a Warrior creature"); - static { - filter.add(SubType.WARRIOR.getPredicate()); - } + private static final FilterPermanent filter = new FilterCreaturePermanent(SubType.WARRIOR, "a Warrior creature"); public ObsidianBattleAxe(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.TRIBAL,CardType.ARTIFACT},"{3}"); @@ -35,7 +31,7 @@ public final class ObsidianBattleAxe extends CardImpl { // Equipped creature gets +2/+1 and has haste. Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(2, 1)); - ability.addEffect(new GainAbilityAttachedEffect(HasteAbility.getInstance(), AttachmentType.EQUIPMENT)); + ability.addEffect(new GainAbilityAttachedEffect(HasteAbility.getInstance(), AttachmentType.EQUIPMENT).setText("and has haste")); this.addAbility(ability); // Whenever a Warrior creature enters the battlefield, you may attach Obsidian Battle-Axe to it. this.addAbility(new EntersBattlefieldAllTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/o/OccultEpiphany.java b/Mage.Sets/src/mage/cards/o/OccultEpiphany.java new file mode 100644 index 00000000000..ed9e8d2a8ef --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OccultEpiphany.java @@ -0,0 +1,77 @@ +package mage.cards.o; + +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.SpiritWhiteToken; +import mage.players.Player; + +import java.util.Collection; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class OccultEpiphany extends CardImpl { + + public OccultEpiphany(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{X}{U}"); + + // Draw X cards, then discard X cards. Create a 1/1 white Spirit creature token with flying for each card type among cards discarded this way. + this.getSpellAbility().addEffect(new OccultEpiphanyEffect()); + } + + private OccultEpiphany(final OccultEpiphany card) { + super(card); + } + + @Override + public OccultEpiphany copy() { + return new OccultEpiphany(this); + } +} + +class OccultEpiphanyEffect extends OneShotEffect { + + OccultEpiphanyEffect() { + super(Outcome.Benefit); + staticText = "draw X cards, then discard X cards. Create a 1/1 white Spirit creature token " + + "with flying for each card type among cards discarded this way"; + } + + private OccultEpiphanyEffect(final OccultEpiphanyEffect effect) { + super(effect); + } + + @Override + public OccultEpiphanyEffect copy() { + return new OccultEpiphanyEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + int xValue = source.getManaCostsToPay().getX(); + if (player == null || xValue < 1) { + return false; + } + player.drawCards(xValue, source, game); + int cardTypes = player + .discard(xValue, false, false, source, game) + .getCards(game) + .stream() + .map(card -> card.getCardType(game)) + .flatMap(Collection::stream) + .distinct() + .mapToInt(x -> 1) + .sum(); + if (cardTypes > 0) { + new SpiritWhiteToken().putOntoBattlefield(cardTypes, game, source); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/o/OchreJelly.java b/Mage.Sets/src/mage/cards/o/OchreJelly.java index 7fb3326d3d7..36dd3fc0234 100644 --- a/Mage.Sets/src/mage/cards/o/OchreJelly.java +++ b/Mage.Sets/src/mage/cards/o/OchreJelly.java @@ -47,9 +47,9 @@ public final class OchreJelly extends CardImpl { new DiesSourceTriggeredAbility(new CreateDelayedTriggeredAbilityEffect( new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new OchreJellyEffect()) )), OchreJellyCondition.instance, CardUtil.italicizeWithEmDash("Split") - + "When {this} dies, if it had two or more +1/+1 counters on it, " + - "create a token that's a copy of it at the beginning of the next end step. " + - "The token enters the battlefield with half that many +1/+1 counters on it, rounded down." + + "When {this} dies, if it had two or more +1/+1 counters on it, " + + "create a token that's a copy of it at the beginning of the next end step. " + + "The token enters the battlefield with half that many +1/+1 counters on it, rounded down." )); } @@ -95,13 +95,10 @@ class OchreJellyEffect extends OneShotEffect { if (permanent == null) { return false; } - CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(); + final int counters = permanent.getCounters(game).getCount(CounterType.P1P1) / 2; + CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(CounterType.P1P1, counters); effect.setSavedPermanent(permanent); effect.apply(game, source); - int counters = permanent.getCounters(game).getCount(CounterType.P1P1) / 2; - for (Permanent token : effect.getAddedPermanent()) { - permanent.addCounters(CounterType.P1P1.createInstance(counters), source.getControllerId(), source, game); - } return true; } } diff --git a/Mage.Sets/src/mage/cards/o/OdiousWitch.java b/Mage.Sets/src/mage/cards/o/OdiousWitch.java new file mode 100644 index 00000000000..253b38f0029 --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OdiousWitch.java @@ -0,0 +1,49 @@ +package mage.cards.o; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.LoseLifeTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SetTargetPointer; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class OdiousWitch extends CardImpl { + + public OdiousWitch(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARLOCK); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + this.color.setBlack(true); + this.nightCard = true; + + // Whenever Odious Witch attacks, defending player loses 1 life and you gain 1 life. + Ability ability = new AttacksTriggeredAbility( + new LoseLifeTargetEffect(1) + .setText("defending player loses 1 life"), + false, null, SetTargetPointer.PLAYER + ); + ability.addEffect(new GainLifeEffect(1).concatBy("and")); + this.addAbility(ability); + } + + private OdiousWitch(final OdiousWitch card) { + super(card); + } + + @Override + public OdiousWitch copy() { + return new OdiousWitch(this); + } +} diff --git a/Mage.Sets/src/mage/cards/o/OdricBloodCursed.java b/Mage.Sets/src/mage/cards/o/OdricBloodCursed.java new file mode 100644 index 00000000000..d7040df0cac --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OdricBloodCursed.java @@ -0,0 +1,126 @@ +package mage.cards.o; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.*; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.BloodToken; + +/** + * + * @author weirddan455 + */ +public final class OdricBloodCursed extends CardImpl { + + public OdricBloodCursed(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.VAMPIRE); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // When Odric, Blood-Cursed enters the battlefield, create X Blood tokens, where X is the number of abilities from among flying, first strike, double strike, deathtouch, haste, hexproof, indestructible, lifelink, menace, reach, trample, and vigilance found among creatures you control. + this.addAbility(new EntersBattlefieldTriggeredAbility(new OdricBloodCursedEffect())); + } + + private OdricBloodCursed(final OdricBloodCursed card) { + super(card); + } + + @Override + public OdricBloodCursed copy() { + return new OdricBloodCursed(this); + } +} + +class OdricBloodCursedEffect extends OneShotEffect { + + public OdricBloodCursedEffect() { + super(Outcome.Benefit); + staticText = "create X Blood tokens, where X is the number of abilities from among flying, first strike, double strike, deathtouch, haste, hexproof, indestructible, lifelink, menace, reach, trample, and vigilance found among creatures you control"; + } + + private OdricBloodCursedEffect(final OdricBloodCursedEffect effect) { + super(effect); + } + + @Override + public OdricBloodCursedEffect copy() { + return new OdricBloodCursedEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + int flying = 0; + int firstStrike = 0; + int doubleStrike = 0; + int deathtouch = 0; + int haste = 0; + int hexproof = 0; + int indestructible = 0; + int lifelink = 0; + int menance = 0; + int reach = 0; + int trample = 0; + int vigilance = 0; + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(source.getControllerId())) { + if (permanent.isCreature(game)) { + for (Ability ability : permanent.getAbilities(game)) { + if (ability instanceof FlyingAbility) { + flying = 1; + } + if (ability instanceof FirstStrikeAbility) { + firstStrike = 1; + } + if (ability instanceof DoubleStrikeAbility) { + doubleStrike = 1; + } + if (ability instanceof DeathtouchAbility) { + deathtouch = 1; + } + if (ability instanceof HasteAbility) { + haste = 1; + } + if (ability instanceof HexproofAbility) { + hexproof = 1; + } + if (ability instanceof IndestructibleAbility) { + indestructible = 1; + } + if (ability instanceof LifelinkAbility) { + lifelink = 1; + } + if (ability instanceof MenaceAbility) { + menance = 1; + } + if (ability instanceof ReachAbility) { + reach = 1; + } + if (ability instanceof TrampleAbility) { + trample = 1; + } + if (ability instanceof VigilanceAbility) { + vigilance = 1; + } + } + } + } + int numTokens = flying + firstStrike + doubleStrike + deathtouch + haste + hexproof + indestructible + lifelink + menance + reach + trample + vigilance; + if (numTokens < 1) { + return false; + } + return new BloodToken().putOntoBattlefield(numTokens, game, source, source.getControllerId()); + } +} diff --git a/Mage.Sets/src/mage/cards/o/OdricMasterTactician.java b/Mage.Sets/src/mage/cards/o/OdricMasterTactician.java index 4128e6cc6db..cecbd547efe 100644 --- a/Mage.Sets/src/mage/cards/o/OdricMasterTactician.java +++ b/Mage.Sets/src/mage/cards/o/OdricMasterTactician.java @@ -1,19 +1,17 @@ package mage.cards.o; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.combat.ChooseBlockersEffect; import mage.abilities.keyword.FirstStrikeAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.game.Game; import mage.game.events.GameEvent; -import mage.players.Player; -import mage.watchers.common.ChooseBlockersRedundancyWatcher; +import mage.watchers.common.ControlCombatRedundancyWatcher; + +import java.util.UUID; /** * @author noxx @@ -49,9 +47,8 @@ public final class OdricMasterTactician extends CardImpl { class OdricMasterTacticianTriggeredAbility extends TriggeredAbilityImpl { public OdricMasterTacticianTriggeredAbility() { - super(Zone.BATTLEFIELD, new OdricMasterTacticianChooseBlockersEffect()); - this.addWatcher(new ChooseBlockersRedundancyWatcher()); - this.addEffect(new ChooseBlockersRedundancyWatcherIncrementEffect()); + super(Zone.BATTLEFIELD, new ChooseBlockersEffect(Duration.EndOfCombat)); + this.addWatcher(new ControlCombatRedundancyWatcher()); } public OdricMasterTacticianTriggeredAbility(final OdricMasterTacticianTriggeredAbility ability) { @@ -72,80 +69,4 @@ class OdricMasterTacticianTriggeredAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { return game.getCombat().getAttackers().size() >= 4 && game.getCombat().getAttackers().contains(this.sourceId); } - - private class ChooseBlockersRedundancyWatcherIncrementEffect extends OneShotEffect { - - ChooseBlockersRedundancyWatcherIncrementEffect() { - super(Outcome.Neutral); - } - - ChooseBlockersRedundancyWatcherIncrementEffect(final ChooseBlockersRedundancyWatcherIncrementEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - ChooseBlockersRedundancyWatcher watcher = game.getState().getWatcher(ChooseBlockersRedundancyWatcher.class); - if (watcher != null) { - watcher.increment(); - return true; - } - return false; - } - - @Override - public ChooseBlockersRedundancyWatcherIncrementEffect copy() { - return new ChooseBlockersRedundancyWatcherIncrementEffect(this); - } - } -} - -class OdricMasterTacticianChooseBlockersEffect extends ContinuousRuleModifyingEffectImpl { - - public OdricMasterTacticianChooseBlockersEffect() { - super(Duration.EndOfCombat, Outcome.Benefit, false, false); - staticText = "Whenever {this} and at least three other creatures attack, you choose which creatures block this combat and how those creatures block"; - } - - public OdricMasterTacticianChooseBlockersEffect(final OdricMasterTacticianChooseBlockersEffect effect) { - super(effect); - } - - @Override - public OdricMasterTacticianChooseBlockersEffect copy() { - return new OdricMasterTacticianChooseBlockersEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - return false; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DECLARING_BLOCKERS; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - ChooseBlockersRedundancyWatcher watcher = game.getState().getWatcher(ChooseBlockersRedundancyWatcher.class); - if (watcher == null) { - return false; - } - watcher.decrement(); - watcher.copyCount--; - if (watcher.copyCountApply > 0) { - game.informPlayers(source.getSourceObject(game).getIdName() + " didn't apply"); - this.discard(); - return false; - } - watcher.copyCountApply = watcher.copyCount; - Player blockController = game.getPlayer(source.getControllerId()); - if (blockController != null) { - game.getCombat().selectBlockers(blockController, source, game); - return true; - } - this.discard(); - return false; - } } diff --git a/Mage.Sets/src/mage/cards/o/OffspringsRevenge.java b/Mage.Sets/src/mage/cards/o/OffspringsRevenge.java index 43aad22f053..ef8a130fc80 100644 --- a/Mage.Sets/src/mage/cards/o/OffspringsRevenge.java +++ b/Mage.Sets/src/mage/cards/o/OffspringsRevenge.java @@ -91,7 +91,7 @@ class OffspringsRevengeEffect extends OneShotEffect { effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game) + 1)); player.moveCards(card, Zone.EXILED, source, game); effect.apply(game, source); - effect.getAddedPermanent().stream().forEach(permanent -> { + effect.getAddedPermanents().stream().forEach(permanent -> { ContinuousEffect continuousEffect = new GainAbilityTargetEffect( HasteAbility.getInstance(), Duration.UntilYourNextTurn ); diff --git a/Mage.Sets/src/mage/cards/o/OggyarBattleSeer.java b/Mage.Sets/src/mage/cards/o/OggyarBattleSeer.java index 1beee9d8abe..d7ab24b189f 100644 --- a/Mage.Sets/src/mage/cards/o/OggyarBattleSeer.java +++ b/Mage.Sets/src/mage/cards/o/OggyarBattleSeer.java @@ -29,7 +29,7 @@ public final class OggyarBattleSeer extends CardImpl { this.addAbility(HasteAbility.getInstance()); // {T}: Scry 1. - this.addAbility(new SimpleActivatedAbility(new ScryEffect(1), new TapSourceCost())); + this.addAbility(new SimpleActivatedAbility(new ScryEffect(1, false), new TapSourceCost())); } private OggyarBattleSeer(final OggyarBattleSeer card) { diff --git a/Mage.Sets/src/mage/cards/o/OgreErrant.java b/Mage.Sets/src/mage/cards/o/OgreErrant.java index a48c0222e82..d1b5759607c 100644 --- a/Mage.Sets/src/mage/cards/o/OgreErrant.java +++ b/Mage.Sets/src/mage/cards/o/OgreErrant.java @@ -41,7 +41,8 @@ public final class OgreErrant extends CardImpl { // Whenever Ogre Errant attacks, another target attacking Knight gains menace until end of turn. Ability ability = new AttacksTriggeredAbility(new GainAbilityTargetEffect( new MenaceAbility(), Duration.EndOfTurn - ).setText("another target attacking Knight gains menace until end of turn"), false); + ).setText("another target attacking Knight gains menace until end of turn." + + "(It can't be blocked except by two or more creatures.)"), false); ability.addTarget(new TargetPermanent(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/o/OgreHeadHelm.java b/Mage.Sets/src/mage/cards/o/OgreHeadHelm.java new file mode 100644 index 00000000000..3d4822d1932 --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OgreHeadHelm.java @@ -0,0 +1,130 @@ +package mage.cards.o; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostEquippedEffect; +import mage.abilities.keyword.ReconfigureAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +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 mage.players.Player; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class OgreHeadHelm extends CardImpl { + + public OgreHeadHelm(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{1}{R}"); + + this.subtype.add(SubType.EQUIPMENT); + this.subtype.add(SubType.OGRE); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Equipped creature gets +2/+2. + this.addAbility(new SimpleStaticAbility(new BoostEquippedEffect(2, 2))); + + // Whenever Ogre-Head Helm or equipped creature deals combat damage to a player, you may sacrifice it. If you do, discard your hand, then draw three cards. + this.addAbility(new OgreHeadHelmTriggeredAbility()); + + // Reconfigure {3} + this.addAbility(new ReconfigureAbility("{3}")); + } + + private OgreHeadHelm(final OgreHeadHelm card) { + super(card); + } + + @Override + public OgreHeadHelm copy() { + return new OgreHeadHelm(this); + } +} + +class OgreHeadHelmTriggeredAbility extends TriggeredAbilityImpl { + + OgreHeadHelmTriggeredAbility() { + super(Zone.BATTLEFIELD, new OgreHeadHelmEffect()); + } + + private OgreHeadHelmTriggeredAbility(final OgreHeadHelmTriggeredAbility ability) { + super(ability); + } + + @Override + public OgreHeadHelmTriggeredAbility copy() { + return new OgreHeadHelmTriggeredAbility(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())) { + this.getEffects().setTargetPointer(new FixedTarget(event.getSourceId(), game)); + return true; + } + Permanent permanent = getSourcePermanentOrLKI(game); + if (permanent != null && event.getSourceId().equals(permanent.getAttachedTo())) { + this.getEffects().setTargetPointer(new FixedTarget(event.getSourceId(), game)); + return true; + } + return false; + } + + @Override + public String getRule() { + return "Whenever {this} or equipped creature deals combat damage to a player, " + + "you may sacrifice it. If you do, discard your hand, then draw three cards."; + } +} + +class OgreHeadHelmEffect extends OneShotEffect { + + OgreHeadHelmEffect() { + super(Outcome.DrawCard); + } + + private OgreHeadHelmEffect(final OgreHeadHelmEffect effect) { + super(effect); + } + + @Override + public OgreHeadHelmEffect copy() { + return new OgreHeadHelmEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (player == null || permanent == null || !player.chooseUse( + outcome, "Sacrifice " + permanent.getName() + '?', source, game + ) || !permanent.sacrifice(source, game)) { + return false; + } + player.discard(player.getHand(), false, source, game); + player.drawCards(3, source, game); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/o/OgreJailbreaker.java b/Mage.Sets/src/mage/cards/o/OgreJailbreaker.java index 7087ecef896..63d1d29377b 100644 --- a/Mage.Sets/src/mage/cards/o/OgreJailbreaker.java +++ b/Mage.Sets/src/mage/cards/o/OgreJailbreaker.java @@ -1,18 +1,17 @@ - package mage.cards.o; import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; -import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.decorator.ConditionalAsThoughEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.combat.CanAttackAsThoughItDidntHaveDefenderSourceEffect; import mage.abilities.keyword.DefenderAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.FilterPermanent; -import mage.game.Game; /** * @@ -20,6 +19,12 @@ import mage.game.Game; */ public final class OgreJailbreaker extends CardImpl { + private static final FilterPermanent filter = new FilterPermanent("Gate"); + + static { + filter.add(SubType.GATE.getPredicate()); + } + public OgreJailbreaker(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}"); this.subtype.add(SubType.OGRE); @@ -30,8 +35,13 @@ public final class OgreJailbreaker extends CardImpl { // Defender this.addAbility(DefenderAbility.getInstance()); + // Ogre Jailbreaker can attack as though it didn't have defender as long as you control a Gate. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new OgreJailbreakerEffect())); + Effect effect = new ConditionalAsThoughEffect( + new CanAttackAsThoughItDidntHaveDefenderSourceEffect(Duration.WhileOnBattlefield), + new PermanentsOnTheBattlefieldCondition(filter)); + effect.setText("{this} can attack as though it didn't have defender as long as you control a Gate"); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); } @@ -44,42 +54,3 @@ public final class OgreJailbreaker extends CardImpl { return new OgreJailbreaker(this); } } - -class OgreJailbreakerEffect extends AsThoughEffectImpl { - - private static final FilterPermanent filter = new FilterPermanent(); - private PermanentsOnTheBattlefieldCondition gateCondition; - static { - filter.add(SubType.GATE.getPredicate()); - } - - public OgreJailbreakerEffect() { - super(AsThoughEffectType.ATTACK, Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = "{this} can attack as though it didn't have defender as long as you control a Gate"; - gateCondition = new PermanentsOnTheBattlefieldCondition(filter); - } - - public OgreJailbreakerEffect(final OgreJailbreakerEffect effect) { - super(effect); - this.gateCondition = effect.gateCondition; - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public OgreJailbreakerEffect copy() { - return new OgreJailbreakerEffect(this); - } - - @Override - public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { - if (sourceId.equals(source.getSourceId()) && gateCondition.apply(game, source)) { - return true; - } - return false; - } - -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/o/OkibaGangShinobi.java b/Mage.Sets/src/mage/cards/o/OkibaGangShinobi.java index c5c0c90eb58..95b4445f46d 100644 --- a/Mage.Sets/src/mage/cards/o/OkibaGangShinobi.java +++ b/Mage.Sets/src/mage/cards/o/OkibaGangShinobi.java @@ -27,7 +27,7 @@ public final class OkibaGangShinobi extends CardImpl { this.toughness = new MageInt(2); // Ninjutsu {3}{B} ({3}{B}, Return an unblocked attacker you control to hand: Put this card onto the battlefield from your hand tapped and attacking.) - this.addAbility(new NinjutsuAbility(new ManaCostsImpl("{3}{B}"))); + this.addAbility(new NinjutsuAbility("{3}{B}")); // Whenever Okiba-Gang Shinobi deals combat damage to a player, that player discards two cards. this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new DiscardTargetEffect(2), false, true)); diff --git a/Mage.Sets/src/mage/cards/o/OkibaReckonerRaid.java b/Mage.Sets/src/mage/cards/o/OkibaReckonerRaid.java new file mode 100644 index 00000000000..ef5f69bdea1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OkibaReckonerRaid.java @@ -0,0 +1,55 @@ +package mage.cards.o; + +import mage.abilities.common.SagaAbility; +import mage.abilities.effects.Effects; +import mage.abilities.effects.common.ExileSagaAndReturnTransformedEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.LoseLifeOpponentsEffect; +import mage.abilities.keyword.TransformAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SagaChapter; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class OkibaReckonerRaid extends CardImpl { + + public OkibaReckonerRaid(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{B}"); + + this.subtype.add(SubType.SAGA); + this.secondSideCardClazz = mage.cards.n.NezumiRoadCaptain.class; + + // (As this Saga enters and after your draw step, add a lore counter.) + SagaAbility sagaAbility = new SagaAbility(this); + + // I, II — Each opponent loses 1 life and you gain 1 life. + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_II, + new Effects( + new LoseLifeOpponentsEffect(1), + new GainLifeEffect(1).concatBy("and") + ) + ); + + // III — Exile this Saga, then return it to the battlefield transformed under your control. + this.addAbility(new TransformAbility()); + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, new ExileSagaAndReturnTransformedEffect()); + + this.addAbility(sagaAbility); + } + + private OkibaReckonerRaid(final OkibaReckonerRaid card) { + super(card); + } + + @Override + public OkibaReckonerRaid copy() { + return new OkibaReckonerRaid(this); + } +} diff --git a/Mage.Sets/src/mage/cards/o/OkibaSalvage.java b/Mage.Sets/src/mage/cards/o/OkibaSalvage.java new file mode 100644 index 00000000000..7fc74ead283 --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OkibaSalvage.java @@ -0,0 +1,88 @@ +package mage.cards.o; + +import mage.abilities.Ability; +import mage.abilities.condition.common.ControlArtifactAndEnchantmentCondition; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.hint.common.ControlArtifactAndEnchantmentHint; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.filter.FilterCard; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class OkibaSalvage extends CardImpl { + + private static final FilterCard filter = new FilterCard("creature or Vehicle card from your graveyard"); + + static { + filter.add(Predicates.or( + CardType.CREATURE.getPredicate(), + SubType.VEHICLE.getPredicate() + )); + } + + public OkibaSalvage(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{B}"); + + // Return target creature or Vehicle card from your graveyard to the battlefield. Then put two +1/+1 counters on that permanent if you control an artifact and an enchantment. + this.getSpellAbility().addEffect(new OkibaSalvageEffect()); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(filter)); + this.getSpellAbility().addHint(ControlArtifactAndEnchantmentHint.instance); + } + + private OkibaSalvage(final OkibaSalvage card) { + super(card); + } + + @Override + public OkibaSalvage copy() { + return new OkibaSalvage(this); + } +} + +class OkibaSalvageEffect extends OneShotEffect { + + OkibaSalvageEffect() { + super(Outcome.Benefit); + staticText = "return target creature or Vehicle card from your graveyard to the battlefield. " + + "Then put two +1/+1 counters on that permanent if you control an artifact and an enchantment"; + } + + private OkibaSalvageEffect(final OkibaSalvageEffect effect) { + super(effect); + } + + @Override + public OkibaSalvageEffect copy() { + return new OkibaSalvageEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Card card = game.getCard(source.getFirstTarget()); + if (player == null || card == null) { + return false; + } + player.moveCards(card, Zone.BATTLEFIELD, source, game); + Permanent permanent = game.getPermanent(card.getId()); + if (permanent != null && ControlArtifactAndEnchantmentCondition.instance.apply(game, source)) { + permanent.addCounters(CounterType.P1P1.createInstance(2), source, game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/o/OkoTheTrickster.java b/Mage.Sets/src/mage/cards/o/OkoTheTrickster.java index 86e1ed6c1b4..cb98c9ec02f 100644 --- a/Mage.Sets/src/mage/cards/o/OkoTheTrickster.java +++ b/Mage.Sets/src/mage/cards/o/OkoTheTrickster.java @@ -2,7 +2,6 @@ package mage.cards.o; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.PreventAllDamageToSourceEffect; import mage.abilities.effects.common.continuous.GainAbilityAllEffect; @@ -31,7 +30,7 @@ public final class OkoTheTrickster extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.OKO); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Put two +1/+1 counters on up to one target creature you control. Ability ability = new LoyaltyAbility( diff --git a/Mage.Sets/src/mage/cards/o/OkoThiefOfCrowns.java b/Mage.Sets/src/mage/cards/o/OkoThiefOfCrowns.java index 79e5881224e..57804a64654 100644 --- a/Mage.Sets/src/mage/cards/o/OkoThiefOfCrowns.java +++ b/Mage.Sets/src/mage/cards/o/OkoThiefOfCrowns.java @@ -1,11 +1,9 @@ package mage.cards.o; -import mage.MageInt; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; +import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.common.CreateTokenEffect; -import mage.abilities.effects.common.continuous.BecomesCreatureTargetEffect; import mage.abilities.effects.common.continuous.ExchangeControlTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -14,8 +12,9 @@ import mage.filter.FilterPermanent; import mage.filter.StaticFilters; import mage.filter.common.FilterOpponentsCreaturePermanent; import mage.filter.predicate.mageobject.PowerPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; import mage.game.permanent.token.FoodToken; -import mage.game.permanent.token.TokenImpl; import mage.target.TargetPermanent; import java.util.UUID; @@ -25,7 +24,8 @@ import java.util.UUID; */ public final class OkoThiefOfCrowns extends CardImpl { - private static final FilterPermanent filter = new FilterOpponentsCreaturePermanent("creature an opponent controls with power 3 or less"); + private static final FilterPermanent filter + = new FilterOpponentsCreaturePermanent("creature an opponent controls with power 3 or less"); static { filter.add(new PowerPredicate(ComparisonType.FEWER_THAN, 4)); @@ -36,15 +36,13 @@ public final class OkoThiefOfCrowns extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.OKO); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +2: Create a Food token. this.addAbility(new LoyaltyAbility(new CreateTokenEffect(new FoodToken()), 2)); // +1: Target artifact or creature loses all abilities and becomes a green Elk creature with base power and toughness 3/3. - Ability ability = new LoyaltyAbility(new BecomesCreatureTargetEffect( - new OkoThiefOfCrownsToken(), true, false, Duration.Custom - ).setText("target artifact or creature loses all abilities and becomes a green Elk creature with base power and toughness 3/3"), 1); + Ability ability = new LoyaltyAbility(new OkoThiefOfCrownsEffect(), 1); ability.addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_CREATURE)); this.addAbility(ability); @@ -68,22 +66,71 @@ public final class OkoThiefOfCrowns extends CardImpl { } } -class OkoThiefOfCrownsToken extends TokenImpl { +class OkoThiefOfCrownsEffect extends ContinuousEffectImpl { - OkoThiefOfCrownsToken() { - super("", "green Elk creature with base power and toughness 3/3"); - cardType.add(CardType.CREATURE); - color.setGreen(true); - subtype.add(SubType.ELK); - power = new MageInt(3); - toughness = new MageInt(3); + OkoThiefOfCrownsEffect() { + super(Duration.Custom, Outcome.Benefit); + staticText = "target artifact or creature loses all abilities " + + "and becomes a green Elk creature with base power and toughness 3/3"; } - private OkoThiefOfCrownsToken(final OkoThiefOfCrownsToken token) { - super(token); + private OkoThiefOfCrownsEffect(final OkoThiefOfCrownsEffect effect) { + super(effect); } - public OkoThiefOfCrownsToken copy() { - return new OkoThiefOfCrownsToken(this); + @Override + public OkoThiefOfCrownsEffect copy() { + return new OkoThiefOfCrownsEffect(this); } -} \ No newline at end of file + + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (permanent == null) { + discard(); + return false; + } + switch (layer) { + case TypeChangingEffects_4: + permanent.removeAllCardTypes(game); + permanent.removeAllSubTypes(game); + permanent.addCardType(game, CardType.CREATURE); + permanent.addSubType(game, SubType.ELK); + return true; + case ColorChangingEffects_5: + permanent.getColor(game).setWhite(false); + permanent.getColor(game).setBlue(false); + permanent.getColor(game).setBlack(false); + permanent.getColor(game).setRed(false); + permanent.getColor(game).setGreen(true); + return true; + case AbilityAddingRemovingEffects_6: + permanent.removeAllAbilities(source.getSourceId(), game); + return true; + case PTChangingEffects_7: + if (sublayer == SubLayer.SetPT_7b) { + permanent.getPower().setValue(3); + permanent.getToughness().setValue(3); + return true; + } + } + return false; + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean hasLayer(Layer layer) { + switch (layer) { + case TypeChangingEffects_4: + case ColorChangingEffects_5: + case AbilityAddingRemovingEffects_6: + case PTChangingEffects_7: + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/o/OlagLudevicsHubris.java b/Mage.Sets/src/mage/cards/o/OlagLudevicsHubris.java new file mode 100644 index 00000000000..b3cf3cd498a --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OlagLudevicsHubris.java @@ -0,0 +1,135 @@ +package mage.cards.o; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.CopyEffect; +import mage.cards.*; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.permanent.PermanentCard; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInExile; +import mage.util.CardUtil; +import mage.util.functions.CopyApplier; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class OlagLudevicsHubris extends CardImpl { + + public OlagLudevicsHubris(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.ZOMBIE); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + this.color.setBlue(true); + this.color.setBlack(true); + this.nightCard = true; + + // As this creature transforms in Olag, Ludevic's Hubris, it becomes a copy of a creature card exiled with it, except its name is Olag, Ludevic's Hubris, it's 4/4, and it's a legendary blue and black Zombie in addition to its other colors and types. Put a number of +1/+1 counters on Olag equal to the number of creature cards exiled with it. + this.addAbility(new SimpleStaticAbility(new OlagLudevicsHubrisEffect())); + } + + private OlagLudevicsHubris(final OlagLudevicsHubris card) { + super(card); + } + + @Override + public OlagLudevicsHubris copy() { + return new OlagLudevicsHubris(this); + } +} + +class OlagLudevicsHubrisEffect extends ReplacementEffectImpl { + + OlagLudevicsHubrisEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "as this creature transforms into {this}, it becomes a copy of a creature card exiled with it, " + + "except its name is Olag, Ludevic's Hubris, it's 4/4, and it's a legendary blue and black " + + "Zombie in addition to its other colors and types. Put a number of +1/+1 counters on {this} " + + "equal to the number of creature cards exiled with it"; + } + + private OlagLudevicsHubrisEffect(final OlagLudevicsHubrisEffect effect) { + super(effect); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Cards cards = new CardsImpl(game.getExile().getExileZone(CardUtil.getExileZoneId(game, source))); + cards.removeIf(uuid -> !game.getCard(uuid).isCreature(game)); + if (cards.isEmpty()) { + return false; + } + Card copyFromCard = getCard(cards, source, game); + if (copyFromCard == null) { + return false; + } + Permanent newBluePrint = new PermanentCard(copyFromCard, source.getControllerId(), game); + newBluePrint.assignNewId(); + CopyApplier applier = new OlagLudevicsHubrisCopyApplier(); + applier.apply(game, newBluePrint, source, source.getSourceId()); + CopyEffect copyEffect = new CopyEffect(Duration.Custom, newBluePrint, source.getSourceId()); + copyEffect.newId(); + copyEffect.setApplier(applier); + Ability newAbility = source.copy(); + copyEffect.init(newAbility, game); + game.addEffect(copyEffect, newAbility); + return false; + } + + private Card getCard(Cards cards, Ability source, Game game) { + if (cards.size() == 1) { + return cards.getRandom(game); + } + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return cards.getRandom(game); + } + TargetCard target = new TargetCardInExile(StaticFilters.FILTER_CARD); + player.choose(outcome, target, source.getSourceId(), game); + return cards.get(target.getFirstTarget(), game); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.TRANSFORMING; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return source.getSourceId().equals(event.getTargetId()) + && source.getSourcePermanentIfItStillExists(game) != null; + } + + @Override + public OlagLudevicsHubrisEffect copy() { + return new OlagLudevicsHubrisEffect(this); + } +} + +class OlagLudevicsHubrisCopyApplier extends CopyApplier { + + @Override + public boolean apply(Game game, MageObject blueprint, Ability source, UUID copyToObjectId) { + blueprint.setName("Olag, Ludevic's Hubris"); + blueprint.addSuperType(SuperType.LEGENDARY); + blueprint.addSubType(SubType.ZOMBIE); + blueprint.getColor().setBlue(true); + blueprint.getColor().setBlack(true); + blueprint.getPower().modifyBaseValue(4); + blueprint.getToughness().modifyBaseValue(4); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/o/OldManOfTheSea.java b/Mage.Sets/src/mage/cards/o/OldManOfTheSea.java index 4e376e46b3d..26fe7a08990 100644 --- a/Mage.Sets/src/mage/cards/o/OldManOfTheSea.java +++ b/Mage.Sets/src/mage/cards/o/OldManOfTheSea.java @@ -1,41 +1,38 @@ - package mage.cards.o; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.StateTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SkipUntapOptionalAbility; -import mage.abilities.condition.CompoundCondition; -import mage.abilities.condition.Condition; -import mage.abilities.condition.common.SourceTappedCondition; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.decorator.ConditionalContinuousEffect; -import mage.abilities.effects.common.InfoEffect; -import mage.abilities.effects.common.continuous.GainControlTargetEffect; +import mage.abilities.effects.ContinuousEffectImpl; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.*; +import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.ObjectSourcePlayer; import mage.filter.predicate.ObjectSourcePlayerPredicate; import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.permanent.Permanent; -import mage.target.common.TargetCreaturePermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; /** - * - * @author LevelX2 + * @author TheElk801 */ public final class OldManOfTheSea extends CardImpl { + private static final FilterPermanent filter + = new FilterCreaturePermanent("creature with less than or equal power"); + + static { + filter.add(OldManOfTheSeaPredicate.instance); + } + public OldManOfTheSea(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{U}"); this.subtype.add(SubType.DJINN); this.power = new MageInt(2); @@ -45,16 +42,9 @@ public final class OldManOfTheSea extends CardImpl { this.addAbility(new SkipUntapOptionalAbility()); // {tap}: Gain control of target creature with power less than or equal to Old Man of the Sea's power for as long as Old Man of the Sea remains tapped and that creature's power remains less than or equal to Old Man of the Sea's power. - FilterCreaturePermanent controllableCreatures = new FilterCreaturePermanent("creature with power less than or equal to Old Man of the Sea's power"); - controllableCreatures.add(new PowerLowerEqualSourcePredicate(this.getId())); - ConditionalContinuousEffect effect = new ConditionalContinuousEffect( - new OldManOfTheSeaGainControlTargetEffect(Duration.Custom, true), new CompoundCondition(SourceTappedCondition.instance, new SourcePowerGreaterEqualTargetCondition()), - "Gain control of target creature with power less than or equal to {this}'s power for as long as {this} remains tapped and that creature's power remains less than or equal to {this}'s power"); - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new TapSourceCost()); - ability.addTarget(new TargetCreaturePermanent(controllableCreatures)); + Ability ability = new SimpleActivatedAbility(new OldManOfTheSeaEffect(), new TapSourceCost()); + ability.addTarget(new TargetPermanent(filter)); this.addAbility(ability); - // internal ability to check condition - this.addAbility(new OldManOfTheSeaStateBasedTriggeredAbility()); } private OldManOfTheSea(final OldManOfTheSea card) { @@ -67,109 +57,61 @@ public final class OldManOfTheSea extends CardImpl { } } -class OldManOfTheSeaGainControlTargetEffect extends GainControlTargetEffect { +enum OldManOfTheSeaPredicate implements ObjectSourcePlayerPredicate { + instance; - public OldManOfTheSeaGainControlTargetEffect(Duration duration, boolean fixedControl) { - super(duration, fixedControl); + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(input.getSourceId()); + return sourcePermanent != null + && input.getObject().getPower().getValue() <= sourcePermanent.getPower().getValue(); + } +} + +class OldManOfTheSeaEffect extends ContinuousEffectImpl { + + OldManOfTheSeaEffect() { + super(Duration.Custom, Outcome.GainControl); + staticText = "gain control of target creature with power less than or equal to {this}'s power for as long as {this} remains tapped and that creature's power remains less than or equal to {this}'s power"; } - public OldManOfTheSeaGainControlTargetEffect(final OldManOfTheSeaGainControlTargetEffect effect) { + private OldManOfTheSeaEffect(final OldManOfTheSeaEffect effect) { super(effect); } @Override - public void init(Ability source, Game game) { - super.init(source, game); - // save target id to be available for hidden state based triggered effect - game.getState().setValue("target" + source.getSourceId(), getTargetPointer().getFirst(game, source)); + public OldManOfTheSeaEffect copy() { + return new OldManOfTheSeaEffect(this); } @Override - public OldManOfTheSeaGainControlTargetEffect copy() { - return new OldManOfTheSeaGainControlTargetEffect(this); - } -} - -/* -used a state based triggered effect here (not going to stack, so running hidden) to compare power of the controlled -creature to Old Man of the seas power. It's not possible to do this as condition of continuous effect, because the -time the effect checks its condition, the layered effects that modify power are not applied yet. -result is save to a state value to be available for the condition of the continuous effect -*/ -class OldManOfTheSeaStateBasedTriggeredAbility extends StateTriggeredAbility { - - public OldManOfTheSeaStateBasedTriggeredAbility() { - super(Zone.BATTLEFIELD, new InfoEffect("")); - this.setRuleVisible(false); - this.usesStack = false; - } - - public OldManOfTheSeaStateBasedTriggeredAbility(final OldManOfTheSeaStateBasedTriggeredAbility ability) { - super(ability); - } - - @Override - public OldManOfTheSeaStateBasedTriggeredAbility copy() { - return new OldManOfTheSeaStateBasedTriggeredAbility(this); - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent sourcePermanent = game.getPermanent(getSourceId()); - if (sourcePermanent != null && sourcePermanent.isTapped()) { - UUID controlledCreatureId = (UUID) game.getState().getValue("target" + getSourceId()); - if (controlledCreatureId != null) { - Permanent controlledCreature = game.getPermanent(controlledCreatureId); - if (controlledCreature != null) { - if (controlledCreature.getPower().getValue() > sourcePermanent.getPower().getValue()) { - game.getState().setValue("powerCondition" + getSourceId(), Boolean.TRUE); - } + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game); + Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (sourcePermanent == null || permanent == null || !sourcePermanent.isTapped()) { + discard(); + return false; + } + switch (layer) { + case ControlChangingEffects_2: + permanent.changeControllerId(source.getControllerId(), game, source); + return true; + case RulesEffects: + if (permanent.getPower().getValue() > sourcePermanent.getPower().getValue()) { + discard(); + return false; } - } } return false; } -} - -class SourcePowerGreaterEqualTargetCondition implements Condition { - @Override public boolean apply(Game game, Ability source) { - Object object = game.getState().getValue("powerCondition" + source.getSourceId()); - if (object != null && (Boolean) object) { - // reset the values - game.getState().setValue("powerCondition" + source.getSourceId(), Boolean.FALSE); - game.getState().setValue("target" + source.getSourceId(), null); - // stop controlling target - return false; - } return true; } -} - -class PowerLowerEqualSourcePredicate implements ObjectSourcePlayerPredicate { - - UUID sourceId; - - public PowerLowerEqualSourcePredicate(UUID sourceId) { - this.sourceId = sourceId; - } @Override - public boolean apply(ObjectSourcePlayer input, Game game) { - Permanent sourcePermanent = game.getPermanent(sourceId); - Permanent permanent = input.getObject(); - if (permanent != null && sourcePermanent != null) { - if (permanent.getPower().getValue() <= sourcePermanent.getPower().getValue()) { - return true; - } - } - return false; - } - - @Override - public String toString() { - return "creature with power less than or equal to {this}'s power"; + public boolean hasLayer(Layer layer) { + return layer == Layer.ControlChangingEffects_2 || layer == Layer.RulesEffects; } } diff --git a/Mage.Sets/src/mage/cards/o/OldRutstein.java b/Mage.Sets/src/mage/cards/o/OldRutstein.java new file mode 100644 index 00000000000..b69e178e123 --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OldRutstein.java @@ -0,0 +1,119 @@ +package mage.cards.o; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.token.BloodToken; +import mage.game.permanent.token.InsectToken; +import mage.game.permanent.token.TreasureToken; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class OldRutstein extends CardImpl { + + public OldRutstein(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.PEASANT); + this.power = new MageInt(1); + this.toughness = new MageInt(4); + + // When Old Rutstein enters the battlefield or at the beginning of your upkeep, mill a card. If a land card is milled this way, create a Treasure token. If a creature card is milled this way, create a 1/1 green Insect creature token. If a noncreature, nonland card is milled this way, create a Blood token. + this.addAbility(new OldRutsteinTriggeredAbility()); + } + + private OldRutstein(final OldRutstein card) { + super(card); + } + + @Override + public OldRutstein copy() { + return new OldRutstein(this); + } +} + +class OldRutsteinTriggeredAbility extends TriggeredAbilityImpl { + + OldRutsteinTriggeredAbility() { + super(Zone.BATTLEFIELD, new OldRutsteinEffect()); + } + + private OldRutsteinTriggeredAbility(final OldRutsteinTriggeredAbility ability) { + super(ability); + } + + @Override + public OldRutsteinTriggeredAbility copy() { + return new OldRutsteinTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD + || event.getType() == GameEvent.EventType.UPKEEP_STEP_PRE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD) { + return event.getTargetId().equals(getSourceId()); + } + return game.isActivePlayer(getControllerId()); + } + + @Override + public String getRule() { + return "When {this} enters the battlefield or at the beginning of your upkeep, mill a card. " + + "If a land card is milled this way, create a Treasure token. " + + "If a creature card is milled this way, create a 1/1 green Insect creature token. " + + "If a noncreature, nonland card is milled this way, create a Blood token."; + } +} + +class OldRutsteinEffect extends OneShotEffect { + + OldRutsteinEffect() { + super(Outcome.Benefit); + } + + private OldRutsteinEffect(final OldRutsteinEffect effect) { + super(effect); + } + + @Override + public OldRutsteinEffect copy() { + return new OldRutsteinEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Cards cards = player.millCards(1, source, game); + if (cards.getCards(game).stream().anyMatch(card -> card.isLand(game))) { + new TreasureToken().putOntoBattlefield(1, game, source, source.getControllerId()); + } + if (cards.getCards(game).stream().anyMatch(card -> card.isCreature(game))) { + new InsectToken().putOntoBattlefield(1, game, source, source.getControllerId()); + } + if (cards.getCards(game).stream().anyMatch(card -> !card.isCreature(game) && !card.isLand(game))) { + new BloodToken().putOntoBattlefield(1, game, source, source.getControllerId()); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/o/OliviaCrimsonBride.java b/Mage.Sets/src/mage/cards/o/OliviaCrimsonBride.java new file mode 100644 index 00000000000..d0ec11c3163 --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OliviaCrimsonBride.java @@ -0,0 +1,134 @@ +package mage.cards.o; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.StateTriggeredAbility; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ExileSourceEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.HasteAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCardInYourGraveyard; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class OliviaCrimsonBride extends CardImpl { + + public OliviaCrimsonBride(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}{R}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.VAMPIRE); + this.subtype.add(SubType.NOBLE); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // Whenever Olivia, Crimson Bride attacks, return target creature card from your graveyard to the battlefield tapped and attacking. It gains "When you don't control a legendary Vampire, exile this creature." + Ability ability = new AttacksTriggeredAbility(new OliviaCrimsonBrideEffect()); + ability.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); + this.addAbility(ability); + } + + private OliviaCrimsonBride(final OliviaCrimsonBride card) { + super(card); + } + + @Override + public OliviaCrimsonBride copy() { + return new OliviaCrimsonBride(this); + } +} + +class OliviaCrimsonBrideEffect extends OneShotEffect { + + OliviaCrimsonBrideEffect() { + super(Outcome.Benefit); + staticText = "return target creature card from your graveyard to the battlefield tapped and attacking. " + + "It gains \"When you don't control a legendary Vampire, exile this creature.\""; + } + + private OliviaCrimsonBrideEffect(final OliviaCrimsonBrideEffect effect) { + super(effect); + } + + @Override + public OliviaCrimsonBrideEffect copy() { + return new OliviaCrimsonBrideEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + Card card = game.getCard(getTargetPointer().getFirst(game, source)); + if (card == null) { + return false; + } + controller.moveCards(card, Zone.BATTLEFIELD, source, game, true, false, false, null); + Permanent permanent = game.getPermanent(card.getId()); + if (permanent == null) { + return false; + } + game.getCombat().addAttackingCreature(permanent.getId(), game); + game.addEffect(new GainAbilityTargetEffect( + new OliviaCrimsonBrideAbility(), Duration.Custom + ).setTargetPointer(new FixedTarget(permanent, game)), source); + return true; + } +} + +class OliviaCrimsonBrideAbility extends StateTriggeredAbility { + + private static final FilterPermanent filter = new FilterPermanent(); + + static { + filter.add(SuperType.LEGENDARY.getPredicate()); + filter.add(SubType.VAMPIRE.getPredicate()); + } + + public OliviaCrimsonBrideAbility() { + super(Zone.BATTLEFIELD, new ExileSourceEffect()); + } + + public OliviaCrimsonBrideAbility(final OliviaCrimsonBrideAbility ability) { + super(ability); + } + + @Override + public OliviaCrimsonBrideAbility copy() { + return new OliviaCrimsonBrideAbility(this); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return game.getBattlefield().count(filter, getSourceId(), getControllerId(), game) < 1; + } + + @Override + public String getRule() { + return "When you don't control a legendary Vampire, exile this creature."; + } +} diff --git a/Mage.Sets/src/mage/cards/o/OliviaVoldaren.java b/Mage.Sets/src/mage/cards/o/OliviaVoldaren.java index 10417115381..fa6868dc958 100644 --- a/Mage.Sets/src/mage/cards/o/OliviaVoldaren.java +++ b/Mage.Sets/src/mage/cards/o/OliviaVoldaren.java @@ -1,12 +1,9 @@ - package mage.cards.o; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.Effect; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.continuous.AddCardSubTypeTargetEffect; @@ -15,18 +12,18 @@ import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; import mage.counters.CounterType; -import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.CardIdPredicate; import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; import java.util.UUID; /** - * * @author nantuko */ public final class OliviaVoldaren extends CardImpl { @@ -40,26 +37,20 @@ public final class OliviaVoldaren extends CardImpl { } public OliviaVoldaren(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{B}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{R}"); addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.VAMPIRE); this.power = new MageInt(3); this.toughness = new MageInt(3); - String rule = "Gain control of target Vampire for as long as you control {this}"; - - FilterPermanent filter2 = new FilterPermanent(); - filter2.add(TargetController.YOU.getControllerPredicate()); - filter2.add(new CardIdPredicate(this.getId())); - this.addAbility(FlyingAbility.getInstance()); // {1}{R}: Olivia Voldaren deals 1 damage to another target creature. That creature becomes a Vampire in addition to its other types. Put a +1/+1 counter on Olivia Voldaren. Ability ability = new SimpleActivatedAbility( - Zone.BATTLEFIELD, - new DamageTargetEffect(1).setText("{this} deals 1 damage to another target creature"), - new ManaCostsImpl("{1}{R}") + new DamageTargetEffect(1) + .setText("{this} deals 1 damage to another target creature"), + new ManaCostsImpl<>("{1}{R}") ); ability.addTarget(new TargetCreaturePermanent(filter)); Effect effect = new AddCardSubTypeTargetEffect(SubType.VAMPIRE, Duration.WhileOnBattlefield); @@ -69,9 +60,9 @@ public final class OliviaVoldaren extends CardImpl { this.addAbility(ability); // {3}{B}{B}: Gain control of target Vampire for as long as you control Olivia Voldaren. - Ability ability2 = new SimpleActivatedAbility(Zone.BATTLEFIELD, - new ConditionalContinuousEffect(new GainControlTargetEffect(Duration.Custom), new PermanentsOnTheBattlefieldCondition(filter2), rule), - new ManaCostsImpl("{3}{B}{B}")); + Ability ability2 = new SimpleActivatedAbility( + new GainControlTargetEffect(Duration.WhileControlled), new ManaCostsImpl<>("{3}{B}{B}") + ); ability2.addTarget(new TargetCreaturePermanent(vampireFilter)); this.addAbility(ability2); } diff --git a/Mage.Sets/src/mage/cards/o/OliviasAttendants.java b/Mage.Sets/src/mage/cards/o/OliviasAttendants.java new file mode 100644 index 00000000000..52aa890a53f --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OliviasAttendants.java @@ -0,0 +1,99 @@ +package mage.cards.o; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.keyword.MenaceAbility; +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.DamagedBatchEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.token.BloodToken; +import mage.target.common.TargetAnyTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class OliviasAttendants extends CardImpl { + + public OliviasAttendants(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}{R}"); + + this.subtype.add(SubType.VAMPIRE); + this.power = new MageInt(6); + this.toughness = new MageInt(6); + + // Menace + this.addAbility(new MenaceAbility(false)); + + // Whenever Olivia's Attendants deals damage, create that many Blood tokens. + this.addAbility(new OliviasAttendantsTriggeredAbility()); + + // {2}{R}: Olivia's Attendants deals 1 damage to any target. + Ability ability = new SimpleActivatedAbility(new DamageTargetEffect(1), new ManaCostsImpl<>("{2}{R}")); + ability.addTarget(new TargetAnyTarget()); + this.addAbility(ability); + } + + private OliviasAttendants(final OliviasAttendants card) { + super(card); + } + + @Override + public OliviasAttendants copy() { + return new OliviasAttendants(this); + } +} + +class OliviasAttendantsTriggeredAbility extends TriggeredAbilityImpl { + + OliviasAttendantsTriggeredAbility() { + super(Zone.BATTLEFIELD, null); + } + + private OliviasAttendantsTriggeredAbility(final OliviasAttendantsTriggeredAbility ability) { + super(ability); + } + + @Override + public OliviasAttendantsTriggeredAbility copy() { + return new OliviasAttendantsTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DAMAGED_PLAYER_BATCH + || event.getType() == GameEvent.EventType.DAMAGED_PERMANENT_BATCH; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + int amount = ((DamagedBatchEvent) event) + .getEvents() + .stream() + .filter(e -> e.getSourceId().equals(getSourceId())) + .mapToInt(GameEvent::getAmount) + .sum(); + if (amount < 1) { + return false; + } + this.getEffects().clear(); + this.addEffect(new CreateTokenEffect(new BloodToken(), amount)); + return true; + } + + @Override + public String getRule() { + return "Whenever {this} deals damage, create that many Blood tokens."; + } +} diff --git a/Mage.Sets/src/mage/cards/o/OliviasMidnightAmbush.java b/Mage.Sets/src/mage/cards/o/OliviasMidnightAmbush.java index 872ea104fa6..ca02f9b7920 100644 --- a/Mage.Sets/src/mage/cards/o/OliviasMidnightAmbush.java +++ b/Mage.Sets/src/mage/cards/o/OliviasMidnightAmbush.java @@ -4,7 +4,7 @@ import mage.abilities.Ability; import mage.abilities.condition.common.NightCondition; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; -import mage.abilities.hint.common.NightHint; +import mage.abilities.hint.common.DayNightHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -25,7 +25,7 @@ public final class OliviasMidnightAmbush extends CardImpl { // Target creature gets -2/-2 until end of turn. If it's night, that creature gets -13/-13 until end of turn instead. this.getSpellAbility().addEffect(new OliviasMidnightAmbushEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); - this.getSpellAbility().addHint(NightHint.instance); + this.getSpellAbility().addHint(DayNightHint.instance); } private OliviasMidnightAmbush(final OliviasMidnightAmbush card) { diff --git a/Mage.Sets/src/mage/cards/o/OliviasWrath.java b/Mage.Sets/src/mage/cards/o/OliviasWrath.java new file mode 100644 index 00000000000..d6bb58d2981 --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OliviasWrath.java @@ -0,0 +1,56 @@ +package mage.cards.o; + +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.continuous.BoostAllEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class OliviasWrath extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); + private static final FilterPermanent filter2 = new FilterControlledPermanent(SubType.VAMPIRE); + + static { + filter.add(Predicates.not(SubType.VAMPIRE.getPredicate())); + } + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter2, -1); + private static final Hint hint = new ValueHint( + "Vampires you control", new PermanentsOnBattlefieldCount(filter2) + ); + + public OliviasWrath(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{B}"); + + // Each non-Vampire creature gets -X/-X until end of turn, where X is the number of Vampires you control. + this.getSpellAbility().addEffect(new BoostAllEffect( + xValue, xValue, Duration.EndOfTurn, filter, false, + "each non-Vampire creature gets -X/-X until end of turn, " + + "where X is the number of Vampires you control", true + )); + } + + private OliviasWrath(final OliviasWrath card) { + super(card); + } + + @Override + public OliviasWrath copy() { + return new OliviasWrath(this); + } +} diff --git a/Mage.Sets/src/mage/cards/o/OllenbockEscort.java b/Mage.Sets/src/mage/cards/o/OllenbockEscort.java new file mode 100644 index 00000000000..247e7bb40a2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OllenbockEscort.java @@ -0,0 +1,56 @@ +package mage.cards.o; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.IndestructibleAbility; +import mage.abilities.keyword.LifelinkAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class OllenbockEscort extends CardImpl { + + public OllenbockEscort(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // Sacrifice Ollenbock Escort: Target creature you control with a +1/+1 counter on it gains lifelink and indestructible until end of turn. + Ability ability = new SimpleActivatedAbility(new GainAbilityTargetEffect( + LifelinkAbility.getInstance(), Duration.EndOfTurn + ).setText("target creature you control with a +1/+1 counter on it gains lifelink"), new SacrificeSourceCost()); + ability.addEffect(new GainAbilityTargetEffect( + IndestructibleAbility.getInstance(), Duration.EndOfTurn + ).setText("and indestructible until end of turn")); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CONTROLLED_CREATURE_P1P1)); + this.addAbility(ability); + } + + private OllenbockEscort(final OllenbockEscort card) { + super(card); + } + + @Override + public OllenbockEscort copy() { + return new OllenbockEscort(this); + } +} diff --git a/Mage.Sets/src/mage/cards/o/OminousRoost.java b/Mage.Sets/src/mage/cards/o/OminousRoost.java index 1c2d18c6760..527e5610053 100644 --- a/Mage.Sets/src/mage/cards/o/OminousRoost.java +++ b/Mage.Sets/src/mage/cards/o/OminousRoost.java @@ -2,16 +2,13 @@ package mage.cards.o; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.common.CreateTokenEffect; -import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.permanent.token.OminousRoostToken; -import mage.game.stack.Spell; -import mage.target.common.TargetNonlandPermanent; +import mage.game.permanent.token.OminousRoostBirdToken; import java.util.UUID; @@ -40,7 +37,7 @@ public final class OminousRoost extends CardImpl { class OminousRoostTriggeredAbility extends TriggeredAbilityImpl { OminousRoostTriggeredAbility() { - super(Zone.ALL, new CreateTokenEffect(new OminousRoostToken())); + super(Zone.ALL, new CreateTokenEffect(new OminousRoostBirdToken())); } private OminousRoostTriggeredAbility(final OminousRoostTriggeredAbility ability) { diff --git a/Mage.Sets/src/mage/cards/o/OminousSphinx.java b/Mage.Sets/src/mage/cards/o/OminousSphinx.java index 4cba9cce8a9..0ca9748423c 100644 --- a/Mage.Sets/src/mage/cards/o/OminousSphinx.java +++ b/Mage.Sets/src/mage/cards/o/OminousSphinx.java @@ -11,8 +11,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; /** @@ -21,12 +20,6 @@ import mage.target.common.TargetCreaturePermanent; */ public final class OminousSphinx extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public OminousSphinx(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{U}"); @@ -39,7 +32,7 @@ public final class OminousSphinx extends CardImpl { // Whenever you cycle or discard a card,target creature an opponent controls gets -2/-0 until end of turn. CycleOrDiscardControllerTriggeredAbility ability = new CycleOrDiscardControllerTriggeredAbility(new BoostTargetEffect(-2, -0, Duration.EndOfTurn)); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/o/OneOfThePack.java b/Mage.Sets/src/mage/cards/o/OneOfThePack.java index 9a47b281d50..40ce2562a2d 100644 --- a/Mage.Sets/src/mage/cards/o/OneOfThePack.java +++ b/Mage.Sets/src/mage/cards/o/OneOfThePack.java @@ -22,7 +22,6 @@ public final class OneOfThePack extends CardImpl { this.color.setGreen(true); this.nightCard = true; - this.transformable = true; // At the beginning of each upkeep, if a player cast two or more spells last turn, transform One of the Pack. this.addAbility(new WerewolfBackTriggeredAbility()); diff --git a/Mage.Sets/src/mage/cards/o/OneWithTheKami.java b/Mage.Sets/src/mage/cards/o/OneWithTheKami.java new file mode 100644 index 00000000000..b8b699132e9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OneWithTheKami.java @@ -0,0 +1,92 @@ +package mage.cards.o; + +import mage.abilities.Ability; +import mage.abilities.common.DiesCreatureTriggeredAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.abilities.keyword.FlashAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.permanent.ModifiedPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.SpiritToken; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class OneWithTheKami extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("enchanted creature or another modified creature you control"); + + static { + filter.add(ModifiedPredicate.instance); + } + + public OneWithTheKami(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{G}"); + + this.subtype.add(SubType.AURA); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // Enchant creature you control + TargetPermanent auraTarget = new TargetPermanent(StaticFilters.FILTER_CONTROLLED_CREATURE); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + this.addAbility(new EnchantAbility(auraTarget.getTargetName())); + + // Whenever enchanted creature or another modified creature you control dies, create X 1/1 colorless Spirit creature tokens, where X is that creature's power. + this.addAbility(new DiesCreatureTriggeredAbility(new CreateTokenEffect( + new SpiritToken(), OneWithTheKamiValue.instance + ), false, filter)); + } + + private OneWithTheKami(final OneWithTheKami card) { + super(card); + } + + @Override + public OneWithTheKami copy() { + return new OneWithTheKami(this); + } +} + +enum OneWithTheKamiValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + Permanent permanent = (Permanent) effect.getValue("creatureDied"); + return permanent != null ? permanent.getPower().getValue() : 0; + } + + @Override + public OneWithTheKamiValue copy() { + return this; + } + + @Override + public String getMessage() { + return "that creature's power"; + } + + @Override + public String toString() { + return "X"; + } +} diff --git a/Mage.Sets/src/mage/cards/o/OniCultAnvil.java b/Mage.Sets/src/mage/cards/o/OniCultAnvil.java new file mode 100644 index 00000000000..c00e4c46de5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OniCultAnvil.java @@ -0,0 +1,91 @@ +package mage.cards.o; + +import java.util.UUID; + +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DamagePlayersEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.ConstructToken; + +/** + * + * @author weirddan455 + */ +public final class OniCultAnvil extends CardImpl { + + public OniCultAnvil(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{B}{R}"); + + // Whenever one or more artifacts you control leave the battlefield during your turn, create a 1/1 colorless Construct artifact creature token. This ability triggers only once each turn. + this.addAbility(new OniCultAnvilTriggeredAbility()); + + // {T}, Sacrifice an artifact: Oni-Cult Anvil deals 1 damage to each opponent. You gain 1 life. + Ability ability = new SimpleActivatedAbility(new DamagePlayersEffect(1, TargetController.OPPONENT), new TapSourceCost()); + ability.addCost(new SacrificeTargetCost(StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT_AN)); + ability.addEffect(new GainLifeEffect(1)); + this.addAbility(ability); + } + + private OniCultAnvil(final OniCultAnvil card) { + super(card); + } + + @Override + public OniCultAnvil copy() { + return new OniCultAnvil(this); + } +} + +class OniCultAnvilTriggeredAbility extends TriggeredAbilityImpl { + + public OniCultAnvilTriggeredAbility() { + super(Zone.BATTLEFIELD, new CreateTokenEffect(new ConstructToken())); + this.setTriggersOnce(true); + } + + private OniCultAnvilTriggeredAbility(final OniCultAnvilTriggeredAbility ability) { + super(ability); + } + + @Override + public OniCultAnvilTriggeredAbility copy() { + return new OniCultAnvilTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (game.isActivePlayer(controllerId)) { + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + if (zEvent.getFromZone() == Zone.BATTLEFIELD) { + Permanent permanent = zEvent.getTarget(); + return permanent != null && permanent.isControlledBy(controllerId) && permanent.isArtifact(game); + } + } + return false; + } + + @Override + public String getTriggerPhrase() { + return "Whenever one or more artifacts you control leave the battlefield during your turn, "; + } +} diff --git a/Mage.Sets/src/mage/cards/o/OozeFlux.java b/Mage.Sets/src/mage/cards/o/OozeFlux.java index f106a54e148..86e08515b3a 100644 --- a/Mage.Sets/src/mage/cards/o/OozeFlux.java +++ b/Mage.Sets/src/mage/cards/o/OozeFlux.java @@ -14,7 +14,7 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; import mage.counters.CounterType; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.token.OozeToken; import mage.game.permanent.token.Token; @@ -30,7 +30,7 @@ public final class OozeFlux extends CardImpl { // {1}{G}, Remove one or more +1/+1 counters from among creatures you control: Create an X/X green Ooze creature token, where X is the number of +1/+1 counters removed this way. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new OozeFluxCreateTokenEffect(new OozeToken()),new ManaCostsImpl("{1}{G}")); - ability.addCost(new RemoveVariableCountersTargetCost(new FilterControlledCreaturePermanent("creatures you control"), CounterType.P1P1, "one or more", 1)); + ability.addCost(new RemoveVariableCountersTargetCost(StaticFilters.FILTER_CONTROLLED_CREATURES, CounterType.P1P1, "one or more", 1)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/o/OpalAcrolith.java b/Mage.Sets/src/mage/cards/o/OpalAcrolith.java index e6d512bed77..2844e57114a 100644 --- a/Mage.Sets/src/mage/cards/o/OpalAcrolith.java +++ b/Mage.Sets/src/mage/cards/o/OpalAcrolith.java @@ -38,7 +38,7 @@ public final class OpalAcrolith extends CardImpl { // Whenever an opponent casts a creature spell, if Opal Acrolith is an enchantment, Opal Acrolith becomes a 2/4 Soldier creature. TriggeredAbility ability = new SpellCastOpponentTriggeredAbility(new BecomesCreatureSourceEffect(new OpalAcrolithToken(), "", Duration.WhileOnBattlefield, true, false), filter, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_ENCHANTMENT_PERMANENT), + this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_PERMANENT_ENCHANTMENT), "Whenever an opponent casts a creature spell, if Opal Acrolith is an enchantment, Opal Acrolith becomes a 2/4 Soldier creature.")); // {0}: Opal Acrolith becomes an enchantment. diff --git a/Mage.Sets/src/mage/cards/o/OpalArchangel.java b/Mage.Sets/src/mage/cards/o/OpalArchangel.java index 00db4296079..c60880b8a2a 100644 --- a/Mage.Sets/src/mage/cards/o/OpalArchangel.java +++ b/Mage.Sets/src/mage/cards/o/OpalArchangel.java @@ -33,7 +33,7 @@ public final class OpalArchangel extends CardImpl { // When an opponent casts a creature spell, if Opal Archangel is an enchantment, Opal Archangel becomes a 5/5 Angel creature with flying and vigilance. TriggeredAbility ability = new SpellCastOpponentTriggeredAbility(new BecomesCreatureSourceEffect(new OpalArchangelToken(), "", Duration.WhileOnBattlefield, true, false), new FilterCreatureSpell(), false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_ENCHANTMENT_PERMANENT), + this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_PERMANENT_ENCHANTMENT), "When an opponent casts a creature spell, if {this} is an enchantment, {this} becomes a 5/5 Angel creature with flying and vigilance.")); } diff --git a/Mage.Sets/src/mage/cards/o/OpalCaryatid.java b/Mage.Sets/src/mage/cards/o/OpalCaryatid.java index babc5402df8..260197fb7d2 100644 --- a/Mage.Sets/src/mage/cards/o/OpalCaryatid.java +++ b/Mage.Sets/src/mage/cards/o/OpalCaryatid.java @@ -31,7 +31,7 @@ public final class OpalCaryatid extends CardImpl { // When an opponent casts a creature spell, if Opal Caryatid is an enchantment, Opal Caryatid becomes a 2/2 Soldier creature. TriggeredAbility ability = new SpellCastOpponentTriggeredAbility(new BecomesCreatureSourceEffect(new OpalCaryatidSoldierToken(), "", Duration.WhileOnBattlefield, true, false), new FilterCreatureSpell(), false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_ENCHANTMENT_PERMANENT), + this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_PERMANENT_ENCHANTMENT), "When an opponent casts a creature spell, if {this} is an enchantment, {this} becomes a 2/2 Soldier creature.")); } diff --git a/Mage.Sets/src/mage/cards/o/OpalChampion.java b/Mage.Sets/src/mage/cards/o/OpalChampion.java index b45823fe4c4..72b542a2f81 100644 --- a/Mage.Sets/src/mage/cards/o/OpalChampion.java +++ b/Mage.Sets/src/mage/cards/o/OpalChampion.java @@ -32,7 +32,7 @@ public final class OpalChampion extends CardImpl { // When an opponent casts a creature spell, if Opal Champion is an enchantment, Opal Champion becomes a 3/3 Knight creature with first strike. TriggeredAbility ability = new SpellCastOpponentTriggeredAbility(new BecomesCreatureSourceEffect(new OpalChampionKnight(), "", Duration.WhileOnBattlefield, true, false), new FilterCreatureSpell(), false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_ENCHANTMENT_PERMANENT), + this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_PERMANENT_ENCHANTMENT), "When an opponent casts a creature spell, if {this} is an enchantment, {this} becomes a 3/3 Knight creature with first strike.")); } diff --git a/Mage.Sets/src/mage/cards/o/OpalGargoyle.java b/Mage.Sets/src/mage/cards/o/OpalGargoyle.java index ca5f618b920..f0ecedee085 100644 --- a/Mage.Sets/src/mage/cards/o/OpalGargoyle.java +++ b/Mage.Sets/src/mage/cards/o/OpalGargoyle.java @@ -32,7 +32,7 @@ public final class OpalGargoyle extends CardImpl { // When an opponent casts a creature spell, if Opal Gargoyle is an enchantment, Opal Gargoyle becomes a 2/2 Gargoyle creature with flying. TriggeredAbility ability = new SpellCastOpponentTriggeredAbility(new BecomesCreatureSourceEffect(new OpalGargoyleToken(), "", Duration.WhileOnBattlefield, true, false), new FilterCreatureSpell(), false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_ENCHANTMENT_PERMANENT), + this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_PERMANENT_ENCHANTMENT), "When an opponent casts a creature spell, if {this} is an enchantment, {this} becomes a 2/2 Gargoyle creature with flying.")); } diff --git a/Mage.Sets/src/mage/cards/o/OpalGuardian.java b/Mage.Sets/src/mage/cards/o/OpalGuardian.java index 69dffac23ce..10cd59bb6c9 100644 --- a/Mage.Sets/src/mage/cards/o/OpalGuardian.java +++ b/Mage.Sets/src/mage/cards/o/OpalGuardian.java @@ -34,7 +34,7 @@ public final class OpalGuardian extends CardImpl { // When an opponent casts a creature spell, if Opal Guardian is an enchantment, Opal Guardian becomes a 3/4 Gargoyle creature with flying and protection from red. TriggeredAbility ability = new SpellCastOpponentTriggeredAbility(new BecomesCreatureSourceEffect(new OpalGuardianGargoyle(), "", Duration.WhileOnBattlefield, true, false), new FilterCreatureSpell(), false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_ENCHANTMENT_PERMANENT), + this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_PERMANENT_ENCHANTMENT), "When an opponent casts a creature spell, if {this} is an enchantment, {this} becomes a 3/4 Gargoyle creature with flying and protection from red.")); } diff --git a/Mage.Sets/src/mage/cards/o/OpalTitan.java b/Mage.Sets/src/mage/cards/o/OpalTitan.java index b6872753b97..acafa7b50ea 100644 --- a/Mage.Sets/src/mage/cards/o/OpalTitan.java +++ b/Mage.Sets/src/mage/cards/o/OpalTitan.java @@ -13,7 +13,6 @@ import mage.abilities.keyword.ProtectionAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.FilterSpell; import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; @@ -26,19 +25,13 @@ import java.util.UUID; */ public final class OpalTitan extends CardImpl { - private static final FilterSpell filter = new FilterSpell("creature spell"); - - static { - filter.add(CardType.CREATURE.getPredicate()); - } - public OpalTitan(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}{W}"); // When an opponent casts a creature spell, if Opal Titan is an enchantment, Opal Titan becomes a 4/4 Giant creature with protection from each of that spell's colors. TriggeredAbility ability = new SpellCastOpponentTriggeredAbility(Zone.BATTLEFIELD, new OpalTitanBecomesCreatureEffect(), - filter, false, SetTargetPointer.SPELL); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_ENCHANTMENT_PERMANENT), + StaticFilters.FILTER_SPELL_A_CREATURE, false, SetTargetPointer.SPELL); + this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_PERMANENT_ENCHANTMENT), "When an opponent casts a creature spell, if Opal Titan is an enchantment, Opal Titan becomes a 4/4 Giant creature with protection from each of that spell's colors.")); } @@ -87,27 +80,22 @@ class OpalTitanBecomesCreatureEffect extends ContinuousEffectImpl implements Sou if (permanent != null) { switch (layer) { case TypeChangingEffects_4: - if (sublayer == SubLayer.NA) { - permanent.removeAllCardTypes(game); - permanent.addCardType(game, CardType.CREATURE); - permanent.removeAllSubTypes(game); - permanent.addSubType(game, SubType.GIANT); - } + permanent.removeAllCardTypes(game); + permanent.addCardType(game, CardType.CREATURE); + permanent.removeAllSubTypes(game); + permanent.addSubType(game, SubType.GIANT); break; case AbilityAddingRemovingEffects_6: - if (sublayer == SubLayer.NA) { - if (game.getState().getValue("opalTitanColor" + source.getSourceId()) != null) { - for (ObjectColor color : ((ObjectColor) game.getState().getValue("opalTitanColor" + source.getSourceId())).getColors()) { - if (!permanent.getAbilities().contains(ProtectionAbility.from(color))) { - permanent.addAbility(ProtectionAbility.from(color)); - } + if (game.getState().getValue("opalTitanColor" + source.getSourceId()) != null) { + for (ObjectColor color : ((ObjectColor) game.getState().getValue("opalTitanColor" + source.getSourceId())).getColors()) { + if (!permanent.getAbilities().contains(ProtectionAbility.from(color))) { + permanent.addAbility(ProtectionAbility.from(color), source.getSourceId(), game); } } } break; case PTChangingEffects_7: - if ((sublayer == SubLayer.CharacteristicDefining_7a) - || (sublayer == SubLayer.SetPT_7b)) { + if (sublayer == SubLayer.SetPT_7b) { permanent.getPower().setValue(4); permanent.getToughness().setValue(4); } diff --git a/Mage.Sets/src/mage/cards/o/Opt.java b/Mage.Sets/src/mage/cards/o/Opt.java index 4d6c546724e..e138ba0532a 100644 --- a/Mage.Sets/src/mage/cards/o/Opt.java +++ b/Mage.Sets/src/mage/cards/o/Opt.java @@ -18,7 +18,7 @@ public final class Opt extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{U}"); // Scry 1. - this.getSpellAbility().addEffect(new ScryEffect(1)); + this.getSpellAbility().addEffect(new ScryEffect(1, false)); // Draw a card. this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("
")); diff --git a/Mage.Sets/src/mage/cards/o/OracleEnVec.java b/Mage.Sets/src/mage/cards/o/OracleEnVec.java index fed320eeed6..2a4658c0f3a 100644 --- a/Mage.Sets/src/mage/cards/o/OracleEnVec.java +++ b/Mage.Sets/src/mage/cards/o/OracleEnVec.java @@ -66,9 +66,9 @@ class OracleEnVecEffect extends OneShotEffect { OracleEnVecEffect() { super(Outcome.Benefit); - this.staticText = "Target opponent chooses any number of creatures they control. During that player's next turn, " + - "the chosen creatures attack if able, and other creatures can't attack. At the beginning of that turn's end step, " + - "destroy each of the chosen creatures that didn't attack this turn"; + this.staticText = "Target opponent chooses any number of creatures they control. During that player's next turn, " + + "the chosen creatures attack if able, and other creatures can't attack. At the beginning of that turn's end step, " + + "destroy each of the chosen creatures that didn't attack this turn"; } OracleEnVecEffect(final OracleEnVecEffect effect) { @@ -270,7 +270,7 @@ class OracleEnVecDestroyEffect extends OneShotEffect { Permanent permanent = game.getPermanent(targetId); if (permanent != null && !watcher.getAttackedThisTurnCreatures().contains(new MageObjectReference(permanent, game))) { Effect effect = new DestroyTargetEffect(); - effect.setTargetPointer(new FixedTarget(targetId)); + effect.setTargetPointer(new FixedTarget(targetId, game)); effect.apply(game, source); } } diff --git a/Mage.Sets/src/mage/cards/o/OraclesInsight.java b/Mage.Sets/src/mage/cards/o/OraclesInsight.java index 2f31a2b7007..4144be7f3ff 100644 --- a/Mage.Sets/src/mage/cards/o/OraclesInsight.java +++ b/Mage.Sets/src/mage/cards/o/OraclesInsight.java @@ -1,4 +1,3 @@ - package mage.cards.o; import java.util.UUID; @@ -30,18 +29,18 @@ import mage.target.common.TargetCreaturePermanent; public final class OraclesInsight extends CardImpl { public OraclesInsight(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{3}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{U}"); this.subtype.add(SubType.AURA); - // Enchant creature TargetPermanent auraTarget = new TargetCreaturePermanent(); this.getSpellAbility().addTarget(auraTarget); - this.getSpellAbility().addEffect(new AttachEffect(Outcome.AddAbility)); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.Benefit)); Ability ability = new EnchantAbility(auraTarget.getTargetName()); this.addAbility(ability); + // Enchanted creature has "{T}: Scry 1, then draw a card." - ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ScryEffect(1), new TapSourceCost()); + ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ScryEffect(1, false), new TapSourceCost()); Effect effect = new DrawCardSourceControllerEffect(1); effect.setText("then draw a card"); ability.addEffect(effect); diff --git a/Mage.Sets/src/mage/cards/o/OraclesVault.java b/Mage.Sets/src/mage/cards/o/OraclesVault.java index 20fda3923a3..f96e7121858 100644 --- a/Mage.Sets/src/mage/cards/o/OraclesVault.java +++ b/Mage.Sets/src/mage/cards/o/OraclesVault.java @@ -8,6 +8,7 @@ import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.decorator.ConditionalActivatedAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ExileTopXMayPlayUntilEndOfTurnEffect; import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.cards.CardImpl; @@ -27,13 +28,12 @@ public final class OraclesVault extends CardImpl { public OraclesVault(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); - // {2}, {T}: Exile the top card of your library. Until end of turn, you may play that card. Put a brick counter on Oracle's Vault. - Effect effect = new OraclesVaultEffect(); - effect.setText("Exile the top card of your library. Until end of turn, you may play that card"); - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new GenericManaCost(2)); + // {2}, {T}: Exile the top card of your library. Until end of turn, you may play that card. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileTopXMayPlayUntilEndOfTurnEffect(1), new GenericManaCost(2)); ability.addCost(new TapSourceCost()); + + // Put a brick counter on Oracle's Vault. Effect effect2 = new AddCountersSourceEffect(CounterType.BRICK.createInstance()); - effect2.setText("Put a brick counter on {this}"); ability.addEffect(effect2); this.addAbility(ability); @@ -55,32 +55,6 @@ public final class OraclesVault extends CardImpl { } } -class OraclesVaultEffect extends OneShotEffect { - - public OraclesVaultEffect() { - super(Outcome.Benefit); - } - - public OraclesVaultEffect(final OraclesVaultEffect effect) { - super(effect); - } - - @Override - public OraclesVaultEffect copy() { - return new OraclesVaultEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - return PlayFromNotOwnHandZoneTargetEffect.exileAndPlayFromExile(game, source, controller.getLibrary().getFromTop(game), - TargetController.YOU, Duration.EndOfTurn, false, false, false); - } - return false; - } -} - class OraclesVaultFreeEffect extends OneShotEffect { public OraclesVaultFreeEffect() { diff --git a/Mage.Sets/src/mage/cards/o/OrbOfDreams.java b/Mage.Sets/src/mage/cards/o/OrbOfDreams.java index 014d78f04b7..6eaf4353d37 100644 --- a/Mage.Sets/src/mage/cards/o/OrbOfDreams.java +++ b/Mage.Sets/src/mage/cards/o/OrbOfDreams.java @@ -1,32 +1,24 @@ - package mage.cards.o; -import java.util.UUID; -import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.PermanentsEnterBattlefieldTappedEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.EntersTheBattlefieldEvent; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class OrbOfDreams extends CardImpl { public OrbOfDreams(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); // Permanents enter the battlefield tapped. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new OrbOfDreamsEffect())); + this.addAbility(new SimpleStaticAbility(new PermanentsEnterBattlefieldTappedEffect(StaticFilters.FILTER_PERMANENTS))); } private OrbOfDreams(final OrbOfDreams card) { @@ -37,41 +29,4 @@ public final class OrbOfDreams extends CardImpl { public OrbOfDreams copy() { return new OrbOfDreams(this); } - - private static class OrbOfDreamsEffect extends ReplacementEffectImpl { - - OrbOfDreamsEffect() { - super(Duration.WhileOnBattlefield, Outcome.Tap, false); - staticText = "Permanents enter the battlefield tapped"; - } - - OrbOfDreamsEffect(final OrbOfDreamsEffect effect) { - super(effect); - } - - @Override - public OrbOfDreamsEffect copy() { - return new OrbOfDreamsEffect(this); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget(); - if (permanent != null) { - permanent.setTapped(true); - } - return false; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - return true; - } - - } } diff --git a/Mage.Sets/src/mage/cards/o/OrbweaverKumo.java b/Mage.Sets/src/mage/cards/o/OrbweaverKumo.java index f6b4fec3213..0fbaddcd627 100644 --- a/Mage.Sets/src/mage/cards/o/OrbweaverKumo.java +++ b/Mage.Sets/src/mage/cards/o/OrbweaverKumo.java @@ -27,7 +27,7 @@ public final class OrbweaverKumo extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(4); this.addAbility(ReachAbility.getInstance()); - this.addAbility(new SpellCastControllerTriggeredAbility(new GainAbilitySourceEffect(new ForestwalkAbility(), Duration.EndOfTurn), StaticFilters.SPIRIT_OR_ARCANE_CARD, false)); + this.addAbility(new SpellCastControllerTriggeredAbility(new GainAbilitySourceEffect(new ForestwalkAbility(), Duration.EndOfTurn), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false)); } private OrbweaverKumo(final OrbweaverKumo card) { diff --git a/Mage.Sets/src/mage/cards/o/OrcSureshot.java b/Mage.Sets/src/mage/cards/o/OrcSureshot.java index 6e8e7de87fd..0cd8a4e913d 100644 --- a/Mage.Sets/src/mage/cards/o/OrcSureshot.java +++ b/Mage.Sets/src/mage/cards/o/OrcSureshot.java @@ -11,10 +11,9 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.TargetController; import mage.constants.Zone; +import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; @@ -25,11 +24,9 @@ import mage.target.common.TargetCreaturePermanent; public final class OrcSureshot extends CardImpl { private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("another creature under your control"); - private static final FilterCreaturePermanent filterOpponentCreature = new FilterCreaturePermanent("creature an opponent controls"); static { filter.add(AnotherPredicate.instance); - filterOpponentCreature.add(TargetController.OPPONENT.getControllerPredicate()); } public OrcSureshot(UUID ownerId, CardSetInfo setInfo) { @@ -41,9 +38,8 @@ public final class OrcSureshot extends CardImpl { // Whenever another creature enters the battlefield under your control, target creature an opponent controls gets -1/-1 until end of turn. Ability ability = new EntersBattlefieldAllTriggeredAbility(Zone.BATTLEFIELD, new BoostTargetEffect(-1,-1, Duration.EndOfTurn),filter,false); - ability.addTarget(new TargetCreaturePermanent(filterOpponentCreature)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); this.addAbility(ability); - } private OrcSureshot(final OrcSureshot card) { diff --git a/Mage.Sets/src/mage/cards/o/OrcishSquatters.java b/Mage.Sets/src/mage/cards/o/OrcishSquatters.java index 50c37a93a80..84ad9b29a1c 100644 --- a/Mage.Sets/src/mage/cards/o/OrcishSquatters.java +++ b/Mage.Sets/src/mage/cards/o/OrcishSquatters.java @@ -1,11 +1,8 @@ package mage.cards.o; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksAndIsNotBlockedTriggeredAbility; -import mage.abilities.condition.common.SourceOnBattlefieldControlUnchangedCondition; -import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.common.continuous.AssignNoCombatDamageSourceEffect; import mage.abilities.effects.common.continuous.GainControlTargetEffect; import mage.cards.CardImpl; @@ -16,10 +13,10 @@ import mage.constants.SubType; import mage.filter.common.FilterLandPermanent; import mage.filter.predicate.permanent.DefendingPlayerControlsPredicate; import mage.target.TargetPermanent; -import mage.watchers.common.LostControlWatcher; + +import java.util.UUID; /** - * * @author TheElk801 */ public final class OrcishSquatters extends CardImpl { @@ -38,14 +35,9 @@ public final class OrcishSquatters extends CardImpl { this.toughness = new MageInt(3); // Whenever Orcish Squatters attacks and isn't blocked, you may gain control of target land defending player controls for as long as you control Orcish Squatters. If you do, Orcish Squatters assigns no combat damage this turn. - Ability ability = new AttacksAndIsNotBlockedTriggeredAbility(new ConditionalContinuousEffect( - new GainControlTargetEffect(Duration.Custom), - new SourceOnBattlefieldControlUnchangedCondition(), - "gain control of target land defending player controls for as long as you control {this}" - ), true); + Ability ability = new AttacksAndIsNotBlockedTriggeredAbility(new GainControlTargetEffect(Duration.WhileControlled), true); ability.addEffect(new AssignNoCombatDamageSourceEffect(Duration.EndOfTurn, true)); ability.addTarget(new TargetPermanent(filter)); - ability.addWatcher(new LostControlWatcher()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/o/OrderOfSuccession.java b/Mage.Sets/src/mage/cards/o/OrderOfSuccession.java index dca18beafa5..6534644522b 100644 --- a/Mage.Sets/src/mage/cards/o/OrderOfSuccession.java +++ b/Mage.Sets/src/mage/cards/o/OrderOfSuccession.java @@ -1,4 +1,3 @@ - package mage.cards.o; import java.util.HashMap; @@ -116,7 +115,7 @@ class OrderOfSuccessionEffect extends OneShotEffect { Permanent creature = game.getPermanent(entry.getValue()); if (creature != null) { ContinuousEffect effect = new GainControlTargetEffect(Duration.EndOfGame, player.getId()); - effect.setTargetPointer(new FixedTarget(creature.getId())); + effect.setTargetPointer(new FixedTarget(creature.getId(), game)); game.addEffect(effect, source); game.informPlayers(new StringBuilder(player.getLogName()).append(" gains control of ").append(creature.getName()).toString()); } diff --git a/Mage.Sets/src/mage/cards/o/OreGorger.java b/Mage.Sets/src/mage/cards/o/OreGorger.java index c2b5f8a3e0b..19a5014d5df 100644 --- a/Mage.Sets/src/mage/cards/o/OreGorger.java +++ b/Mage.Sets/src/mage/cards/o/OreGorger.java @@ -25,7 +25,7 @@ public final class OreGorger extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(1); - Ability ability = new SpellCastControllerTriggeredAbility(new DestroyTargetEffect(), StaticFilters.SPIRIT_OR_ARCANE_CARD, true); + Ability ability = new SpellCastControllerTriggeredAbility(new DestroyTargetEffect(), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true); ability.addTarget(new TargetNonBasicLandPermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/o/OrganHoarder.java b/Mage.Sets/src/mage/cards/o/OrganHoarder.java index 68116a867a7..e72aad0f934 100644 --- a/Mage.Sets/src/mage/cards/o/OrganHoarder.java +++ b/Mage.Sets/src/mage/cards/o/OrganHoarder.java @@ -29,7 +29,7 @@ public final class OrganHoarder extends CardImpl { this.addAbility(new EntersBattlefieldTriggeredAbility(new LookLibraryAndPickControllerEffect( StaticValue.get(3), false, StaticValue.get(1), StaticFilters.FILTER_CARD, Zone.GRAVEYARD, false, false, false, Zone.HAND, false - ))); + ).setText("look at the top three cards of your library, then put one of them into your hand and the rest into your graveyard"))); } private OrganHoarder(final OrganHoarder card) { diff --git a/Mage.Sets/src/mage/cards/o/OrganicExtinction.java b/Mage.Sets/src/mage/cards/o/OrganicExtinction.java new file mode 100644 index 00000000000..b4464f0bfb3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OrganicExtinction.java @@ -0,0 +1,43 @@ +package mage.cards.o; + +import mage.abilities.effects.common.DestroyAllEffect; +import mage.abilities.keyword.ImproviseAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class OrganicExtinction extends CardImpl { + + private static final FilterPermanent filter = new FilterCreaturePermanent("nonartifact creatures"); + + static { + filter.add(Predicates.not(CardType.ARTIFACT.getPredicate())); + } + + public OrganicExtinction(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{8}{W}{W}"); + + // Improvise + this.addAbility(new ImproviseAbility()); + + // Destroy all nonartifact creatures. + this.getSpellAbility().addEffect(new DestroyAllEffect(filter)); + } + + private OrganicExtinction(final OrganicExtinction card) { + super(card); + } + + @Override + public OrganicExtinction copy() { + return new OrganicExtinction(this); + } +} diff --git a/Mage.Sets/src/mage/cards/o/OrmendahlTheCorrupter.java b/Mage.Sets/src/mage/cards/o/OrmendahlTheCorrupter.java index 59c786aa5df..5fd1f827fc5 100644 --- a/Mage.Sets/src/mage/cards/o/OrmendahlTheCorrupter.java +++ b/Mage.Sets/src/mage/cards/o/OrmendahlTheCorrupter.java @@ -30,7 +30,6 @@ public final class OrmendahlTheCorrupter extends CardImpl { this.power = new MageInt(6); this.toughness = new MageInt(6); this.color.setBlack(true); - this.transformable = true; this.nightCard = true; // Flying diff --git a/Mage.Sets/src/mage/cards/o/OrochiMergeKeeper.java b/Mage.Sets/src/mage/cards/o/OrochiMergeKeeper.java new file mode 100644 index 00000000000..39b4429664f --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OrochiMergeKeeper.java @@ -0,0 +1,63 @@ +package mage.cards.o; + +import mage.MageInt; +import mage.Mana; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.SourceMatchesFilterCondition; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.mana.GreenManaAbility; +import mage.abilities.mana.SimpleManaAbility; +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.predicate.permanent.ModifiedPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class OrochiMergeKeeper extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent(); + + static { + filter.add(ModifiedPredicate.instance); + } + + private static final Condition condition = new SourceMatchesFilterCondition(filter); + + public OrochiMergeKeeper(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); + + this.subtype.add(SubType.SNAKE); + this.subtype.add(SubType.DRUID); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // {T}: Add {G}. + this.addAbility(new GreenManaAbility()); + + // As long as Orochi Merge-Keeper is modified, it has "{T}: Add {G}{G}." + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new GainAbilitySourceEffect(new SimpleManaAbility( + Zone.BATTLEFIELD, Mana.GreenMana(2), new TapSourceCost() + )), condition, "as long as {this} is modified, it has \"{T}: Add {G}{G}.\"" + ))); + } + + private OrochiMergeKeeper(final OrochiMergeKeeper card) { + super(card); + } + + @Override + public OrochiMergeKeeper copy() { + return new OrochiMergeKeeper(this); + } +} diff --git a/Mage.Sets/src/mage/cards/o/Ostracize.java b/Mage.Sets/src/mage/cards/o/Ostracize.java index 944baa651f9..b183fea9d13 100644 --- a/Mage.Sets/src/mage/cards/o/Ostracize.java +++ b/Mage.Sets/src/mage/cards/o/Ostracize.java @@ -6,7 +6,7 @@ import mage.abilities.effects.common.discard.DiscardCardYouChooseTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterCreatureCard; +import mage.filter.StaticFilters; import mage.target.common.TargetOpponent; /** @@ -21,7 +21,7 @@ public final class Ostracize extends CardImpl { // Target opponent reveals their hand. You choose a creature card from it. That player discards that card. this.getSpellAbility().addTarget(new TargetOpponent()); - this.getSpellAbility().addEffect(new DiscardCardYouChooseTargetEffect(new FilterCreatureCard("a creature card"))); + this.getSpellAbility().addEffect(new DiscardCardYouChooseTargetEffect(StaticFilters.FILTER_CARD_CREATURE_A)); } private Ostracize(final Ostracize card) { diff --git a/Mage.Sets/src/mage/cards/o/OtawaraSoaringCity.java b/Mage.Sets/src/mage/cards/o/OtawaraSoaringCity.java new file mode 100644 index 00000000000..5fe267e0fd2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OtawaraSoaringCity.java @@ -0,0 +1,60 @@ +package mage.cards.o; + +import mage.abilities.Ability; +import mage.abilities.costs.costadjusters.LegendaryCreatureCostAdjuster; +import mage.abilities.effects.common.InfoEffect; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.abilities.keyword.ChannelAbility; +import mage.abilities.mana.BlueManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SuperType; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicates; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class OtawaraSoaringCity extends CardImpl { + + private static final FilterPermanent filter + = new FilterPermanent("artifact, creature, enchantment, or planeswalker"); + + static { + filter.add(Predicates.or( + CardType.ARTIFACT.getPredicate(), + CardType.CREATURE.getPredicate(), + CardType.ENCHANTMENT.getPredicate(), + CardType.PLANESWALKER.getPredicate() + )); + } + + public OtawaraSoaringCity(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + this.addSuperType(SuperType.LEGENDARY); + + // {T}: Add {U}. + this.addAbility(new BlueManaAbility()); + + // Channel — {3}{U}, Discard Otawara, Soaring City: Return target artifact, creature, enchantment, or planeswalker to its owner's hand. This ability costs {1} less to activate for each legendary creature you control. + Ability ability = new ChannelAbility("{3}{U}", new ReturnToHandTargetEffect()); + ability.addEffect(new InfoEffect("This ability costs {1} less to activate for each legendary creature you control")); + ability.addTarget(new TargetPermanent(filter)); + ability.setCostAdjuster(LegendaryCreatureCostAdjuster.instance); + this.addAbility(ability); + } + + private OtawaraSoaringCity(final OtawaraSoaringCity card) { + super(card); + } + + @Override + public OtawaraSoaringCity copy() { + return new OtawaraSoaringCity(this); + } +} diff --git a/Mage.Sets/src/mage/cards/o/OthelmSigardianOutcast.java b/Mage.Sets/src/mage/cards/o/OthelmSigardianOutcast.java new file mode 100644 index 00000000000..362dfe73c08 --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OthelmSigardianOutcast.java @@ -0,0 +1,68 @@ +package mage.cards.o; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; +import mage.abilities.keyword.FriendsForeverAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterCard; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.card.PutIntoGraveFromBattlefieldThisTurnPredicate; +import mage.target.common.TargetCardInYourGraveyard; +import mage.watchers.common.CardsPutIntoGraveyardWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class OthelmSigardianOutcast extends CardImpl { + + private static final FilterCard filter = new FilterCreatureCard( + "creature card in your graveyard that was put there from the battlefield this turn" + ); + + static { + filter.add(PutIntoGraveFromBattlefieldThisTurnPredicate.instance); + } + + public OthelmSigardianOutcast(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // {2}, {T}: Choose target creature card in your graveyard that was put there from the battlefield this turn. Return it to the battlefield tapped. + Ability ability = new SimpleActivatedAbility( + new ReturnFromGraveyardToBattlefieldTargetEffect(true) + .setText("choose target creature card in your graveyard " + + "that was put there from the battlefield this turn. " + + "Return it to the battlefield tapped"), + new GenericManaCost(2) + ); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + this.addAbility(ability, new CardsPutIntoGraveyardWatcher()); + + // Friends forever + this.addAbility(FriendsForeverAbility.getInstance()); + } + + private OthelmSigardianOutcast(final OthelmSigardianOutcast card) { + super(card); + } + + @Override + public OthelmSigardianOutcast copy() { + return new OthelmSigardianOutcast(this); + } +} diff --git a/Mage.Sets/src/mage/cards/o/OtherworldlyGaze.java b/Mage.Sets/src/mage/cards/o/OtherworldlyGaze.java index 0463022624e..687b1aa3ef5 100644 --- a/Mage.Sets/src/mage/cards/o/OtherworldlyGaze.java +++ b/Mage.Sets/src/mage/cards/o/OtherworldlyGaze.java @@ -24,7 +24,7 @@ public final class OtherworldlyGaze extends CardImpl { this.getSpellAbility().addEffect(new LookLibraryAndPickControllerEffect( StaticValue.get(3), false, StaticValue.get(5), StaticFilters.FILTER_CARD_CARDS, Zone.LIBRARY, true, false, true, Zone.GRAVEYARD, false - )); + ).setText("look at the top three cards of your library. Put any number of them into your graveyard and the rest back on top of your library in any order")); // Flashback {1}{U} this.addAbility(new FlashbackAbility(this, new ManaCostsImpl<>("{1}{U}"))); diff --git a/Mage.Sets/src/mage/cards/o/OupheVandals.java b/Mage.Sets/src/mage/cards/o/OupheVandals.java index e6befc1402b..9da7376ba67 100644 --- a/Mage.Sets/src/mage/cards/o/OupheVandals.java +++ b/Mage.Sets/src/mage/cards/o/OupheVandals.java @@ -30,7 +30,7 @@ public final class OupheVandals extends CardImpl { private static final FilterStackObject filter = new FilterStackObject("ability from an artifact source"); static { - filter.add(new ArtifactSourcePredicate()); + filter.add(ArtifactSourcePredicate.instance); } public OupheVandals(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/o/OuterRimSlaver.java b/Mage.Sets/src/mage/cards/o/OuterRimSlaver.java index 4bfa3462e90..9e71e43fb55 100644 --- a/Mage.Sets/src/mage/cards/o/OuterRimSlaver.java +++ b/Mage.Sets/src/mage/cards/o/OuterRimSlaver.java @@ -31,7 +31,7 @@ public final class OuterRimSlaver extends CardImpl { // When Outer Rim Slaver enters the battlefield, you may put a bounty counter on target creature. If you do, another target creature fights that creature Ability ability = new EntersBattlefieldTriggeredAbility(new AddCountersTargetEffect(CounterType.BOUNTY.createInstance()) .setText("you may put a bounty counter on target creature"), true); - ability.addEffect(new FightTargetsEffect("If you do, another target creature fights that creature")); + ability.addEffect(new FightTargetsEffect().setText("If you do, another target creature fights that creature")); TargetCreaturePermanent target = new TargetCreaturePermanent(new FilterCreaturePermanent("creature to put a bounty counter on it")); target.setTargetTag(1); ability.addTarget(target); diff --git a/Mage.Sets/src/mage/cards/o/OutlandLiberator.java b/Mage.Sets/src/mage/cards/o/OutlandLiberator.java index 3e27af77112..bed09388e24 100644 --- a/Mage.Sets/src/mage/cards/o/OutlandLiberator.java +++ b/Mage.Sets/src/mage/cards/o/OutlandLiberator.java @@ -7,11 +7,12 @@ import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.keyword.DayboundAbility; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; import java.util.UUID; @@ -27,16 +28,15 @@ public final class OutlandLiberator extends CardImpl { this.subtype.add(SubType.WEREWOLF); this.power = new MageInt(2); this.toughness = new MageInt(2); - this.transformable = true; this.secondSideCardClazz = mage.cards.f.FrenziedTrapbreaker.class; // {1}, Sacrifice Outland Liberator: Destroy target artifact or enchantment. Ability ability = new SimpleActivatedAbility(new DestroyTargetEffect(), new GenericManaCost(1)); ability.addCost(new SacrificeSourceCost()); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_ENCHANTMENT)); this.addAbility(ability); // Daybound - this.addAbility(new TransformAbility()); this.addAbility(new DayboundAbility()); } diff --git a/Mage.Sets/src/mage/cards/o/Outmuscle.java b/Mage.Sets/src/mage/cards/o/Outmuscle.java index ee2dfabe1b5..5702c8a1899 100644 --- a/Mage.Sets/src/mage/cards/o/Outmuscle.java +++ b/Mage.Sets/src/mage/cards/o/Outmuscle.java @@ -51,7 +51,8 @@ class OutmuscleEffect extends OneShotEffect { OutmuscleEffect() { super(Outcome.Benefit); staticText = "Put a +1/+1 counter on target creature you control, " + - "then it fights target creature you don't control." + + "then it fights target creature you don't control. " + + "(Each deals damage equal to its power to the other.) " + "
Adamant — If at least three green mana was spent to cast this spell, " + "the creature you control gains indestructible until end of turn."; } diff --git a/Mage.Sets/src/mage/cards/o/OutpostSiege.java b/Mage.Sets/src/mage/cards/o/OutpostSiege.java index 66855187f38..b9b1e3ca737 100644 --- a/Mage.Sets/src/mage/cards/o/OutpostSiege.java +++ b/Mage.Sets/src/mage/cards/o/OutpostSiege.java @@ -1,4 +1,3 @@ - package mage.cards.o; import java.util.UUID; @@ -8,26 +7,16 @@ import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.common.ZoneChangeAllTriggeredAbility; import mage.abilities.condition.common.ModeChoiceSourceCondition; import mage.abilities.decorator.ConditionalTriggeredAbility; -import mage.abilities.effects.AsThoughEffectImpl; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ChooseModeEffect; import mage.abilities.effects.common.DamageTargetEffect; -import mage.cards.Card; +import mage.abilities.effects.common.ExileTopXMayPlayUntilEndOfTurnEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AsThoughEffectType; import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.common.TargetAnyTarget; -import mage.target.targetpointer.FixedTarget; /** * @@ -39,7 +28,7 @@ public final class OutpostSiege extends CardImpl { private static final String ruleTrigger2 = "&bull Dragons — Whenever a creature you control leaves the battlefield, {this} deals 1 damage to any target."; public OutpostSiege(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{3}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{R}"); // As Outpost Siege enters the battlefield, choose Khans or Dragons. this.addAbility(new EntersBattlefieldAbility(new ChooseModeEffect("Khans or Dragons?", "Khans", "Dragons"), null, @@ -47,7 +36,7 @@ public final class OutpostSiege extends CardImpl { // * Khans - At the beginning of your upkeep, exile the top card of your library. Until end of turn, you may play that card. this.addAbility(new ConditionalTriggeredAbility( - new BeginningOfUpkeepTriggeredAbility(new OutpostSiegeExileEffect(), TargetController.YOU, false), + new BeginningOfUpkeepTriggeredAbility(new ExileTopXMayPlayUntilEndOfTurnEffect(1), TargetController.YOU, false), new ModeChoiceSourceCondition("Khans"), ruleTrigger1)); @@ -71,74 +60,3 @@ public final class OutpostSiege extends CardImpl { return new OutpostSiege(this); } } - -class OutpostSiegeExileEffect extends OneShotEffect { - - public OutpostSiegeExileEffect() { - super(Outcome.Benefit); - this.staticText = "exile the top card of your library. Until end of turn, you may play that card"; - } - - public OutpostSiegeExileEffect(final OutpostSiegeExileEffect effect) { - super(effect); - } - - @Override - public OutpostSiegeExileEffect copy() { - return new OutpostSiegeExileEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); - if (controller != null && sourcePermanent != null) { - Card card = controller.getLibrary().getFromTop(game); - if (card != null) { - String exileName = sourcePermanent.getIdName() + " ("{3}{U}"))); + this.addAbility(new DisturbAbility(this, "{3}{U}")); } private OverwhelmedArchivist(final OverwhelmedArchivist card) { diff --git a/Mage.Sets/src/mage/cards/o/OyobiWhoSplitTheHeavens.java b/Mage.Sets/src/mage/cards/o/OyobiWhoSplitTheHeavens.java index 7d354936902..991a4249a5f 100644 --- a/Mage.Sets/src/mage/cards/o/OyobiWhoSplitTheHeavens.java +++ b/Mage.Sets/src/mage/cards/o/OyobiWhoSplitTheHeavens.java @@ -27,8 +27,9 @@ public final class OyobiWhoSplitTheHeavens extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(6); this.addAbility(FlyingAbility.getInstance()); + // Whenever you cast a Spirit or Arcane spell, create a 3/3 white Spirit creature token with flying. - this.addAbility(new SpellCastControllerTriggeredAbility(new CreateTokenEffect(new AnotherSpiritToken()), StaticFilters.SPIRIT_OR_ARCANE_CARD, false)); + this.addAbility(new SpellCastControllerTriggeredAbility(new CreateTokenEffect(new AnotherSpiritToken()), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false)); } private OyobiWhoSplitTheHeavens(final OyobiWhoSplitTheHeavens card) { diff --git a/Mage.Sets/src/mage/cards/p/PacksDisdain.java b/Mage.Sets/src/mage/cards/p/PacksDisdain.java index d1e9304561c..176e990b421 100644 --- a/Mage.Sets/src/mage/cards/p/PacksDisdain.java +++ b/Mage.Sets/src/mage/cards/p/PacksDisdain.java @@ -1,4 +1,3 @@ - package mage.cards.p; import mage.abilities.Ability; @@ -66,12 +65,13 @@ class PacksDisdainEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); Choice typeChoice = new ChoiceCreatureType(game.getObject(source.getSourceId())); - if (player != null && player.choose(Outcome.UnboostCreature, typeChoice, game)) { + if (player != null + && player.choose(Outcome.UnboostCreature, typeChoice, game)) { FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent(); filter.add(SubType.byDescription(typeChoice.getChoice()).getPredicate()); DynamicValue negativePermanentsCount = new PermanentsOnBattlefieldCount(filter, -1); ContinuousEffect effect = new BoostTargetEffect(negativePermanentsCount, negativePermanentsCount, Duration.EndOfTurn, true); - effect.setTargetPointer(new FixedTarget(source.getFirstTarget())); + effect.setTargetPointer(new FixedTarget(source.getFirstTarget(), game)); game.addEffect(effect, source); return true; } diff --git a/Mage.Sets/src/mage/cards/p/PacksongPup.java b/Mage.Sets/src/mage/cards/p/PacksongPup.java new file mode 100644 index 00000000000..d1910fd0947 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PacksongPup.java @@ -0,0 +1,75 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.common.BeginningOfCombatTriggeredAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.SourcePermanentPowerCount; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.AnotherPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PacksongPup extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent(); + + static { + filter.add(AnotherPredicate.instance); + filter.add(Predicates.or( + SubType.WEREWOLF.getPredicate(), + SubType.WOLF.getPredicate() + )); + } + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter); + private static final Hint hint = new ConditionHint(condition, "You control another Wolf or Werewolf"); + private static final DynamicValue xValue = new SourcePermanentPowerCount(); + + public PacksongPup(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); + + this.subtype.add(SubType.WOLF); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // At the beginning of combat on your turn, if you control another Wolf or Werewolf, put a +1/+1 counter on Packsong Pup. + this.addAbility(new ConditionalInterveningIfTriggeredAbility( + new BeginningOfCombatTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), + TargetController.YOU, false + ), condition, "At the beginning of combat on your turn," + + " if you control another Wolf or Werewolf, put a +1/+1 counter on {this}" + ).addHint(hint)); + + // When Packsong Pup dies, you gain life equal to its power. + this.addAbility(new DiesSourceTriggeredAbility(new GainLifeEffect(xValue).setText("you gain life equal to its power"))); + } + + private PacksongPup(final PacksongPup card) { + super(card); + } + + @Override + public PacksongPup copy() { + return new PacksongPup(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PadeemConsulOfInnovation.java b/Mage.Sets/src/mage/cards/p/PadeemConsulOfInnovation.java index 61bdbaf9c56..5811908ffe7 100644 --- a/Mage.Sets/src/mage/cards/p/PadeemConsulOfInnovation.java +++ b/Mage.Sets/src/mage/cards/p/PadeemConsulOfInnovation.java @@ -35,7 +35,7 @@ public final class PadeemConsulOfInnovation extends CardImpl { // Artifacts you control have hexproof. this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( HexproofAbility.getInstance(), Duration.WhileOnBattlefield, - StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACTS, false + StaticFilters.FILTER_PERMANENT_ARTIFACTS, false ))); // At the beginning of your upkeep, if you control the artifact with the highest converted mana cost or tied for the highest converted mana cost, draw a card. diff --git a/Mage.Sets/src/mage/cards/p/PalaceJailer.java b/Mage.Sets/src/mage/cards/p/PalaceJailer.java index 1e1ba8bd674..909b6d2c175 100644 --- a/Mage.Sets/src/mage/cards/p/PalaceJailer.java +++ b/Mage.Sets/src/mage/cards/p/PalaceJailer.java @@ -18,9 +18,8 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Outcome; -import mage.constants.TargetController; import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.ExileZone; import mage.game.Game; import mage.game.events.GameEvent; @@ -34,12 +33,6 @@ import mage.util.CardUtil; */ public final class PalaceJailer extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public PalaceJailer(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{W}"); @@ -53,7 +46,7 @@ public final class PalaceJailer extends CardImpl { // When Palace Jailer enters the battlefield, exile target creature an opponent controls until an opponent becomes the monarch. (That creature returns under its owner's control.) Ability ability = new EntersBattlefieldTriggeredAbility(new PalaceJailerExileEffect()); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); ability.addEffect(new CreateDelayedTriggeredAbilityEffect(new OnOpponentBecomesMonarchReturnExiledToBattlefieldAbility())); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/p/PalliationAccord.java b/Mage.Sets/src/mage/cards/p/PalliationAccord.java index 6e61d95acd5..923d3d22ea2 100644 --- a/Mage.Sets/src/mage/cards/p/PalliationAccord.java +++ b/Mage.Sets/src/mage/cards/p/PalliationAccord.java @@ -11,10 +11,9 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.TargetController; import mage.constants.Zone; import mage.counters.CounterType; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; /** * @@ -22,17 +21,11 @@ import mage.filter.common.FilterCreaturePermanent; */ public final class PalliationAccord extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public PalliationAccord(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{W}{U}"); // Whenever a creature an opponent controls becomes tapped, put a shield counter on Palliation Accord. - this.addAbility(new BecomesTappedTriggeredAbility(new AddCountersSourceEffect(CounterType.SHIELD.createInstance()), false, filter)); + this.addAbility(new BecomesTappedTriggeredAbility(new AddCountersSourceEffect(CounterType.SHIELD.createInstance()), false, StaticFilters.FILTER_OPPONENTS_PERMANENT_A_CREATURE)); // Remove a shield counter from Palliation Accord: Prevent the next 1 damage that would be dealt to you this turn. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, diff --git a/Mage.Sets/src/mage/cards/p/PanickedBystander.java b/Mage.Sets/src/mage/cards/p/PanickedBystander.java new file mode 100644 index 00000000000..3b16985cbee --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PanickedBystander.java @@ -0,0 +1,56 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.common.DiesThisOrAnotherCreatureTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.YouGainedLifeCondition; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.keyword.TransformAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.watchers.common.PlayerGainedLifeWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PanickedBystander extends CardImpl { + + private static final Condition condition = new YouGainedLifeCondition(ComparisonType.MORE_THAN, 2); + + public PanickedBystander(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.PEASANT); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + this.secondSideCardClazz = mage.cards.c.CacklingCulprit.class; + + // Whenever Panicked Bystander or another creature you control dies, you gain 1 life. + this.addAbility(new DiesThisOrAnotherCreatureTriggeredAbility( + new GainLifeEffect(1), false, StaticFilters.FILTER_CONTROLLED_CREATURE + )); + + // At the beginning of your end step, if you gained 3 or more life this turn, transform Panicked Bystander. + this.addAbility(new TransformAbility()); + this.addAbility(new BeginningOfEndStepTriggeredAbility( + Zone.BATTLEFIELD, new TransformSourceEffect(), + TargetController.YOU, condition, false + ), new PlayerGainedLifeWatcher()); + } + + private PanickedBystander(final PanickedBystander card) { + super(card); + } + + @Override + public PanickedBystander copy() { + return new PanickedBystander(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PapercraftDecoy.java b/Mage.Sets/src/mage/cards/p/PapercraftDecoy.java new file mode 100644 index 00000000000..7e917ed925a --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PapercraftDecoy.java @@ -0,0 +1,41 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.common.LeavesBattlefieldTriggeredAbility; +import mage.abilities.costs.mana.GenericManaCost; +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 java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PapercraftDecoy extends CardImpl { + + public PapercraftDecoy(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{2}"); + + this.subtype.add(SubType.FROG); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // When Papercraft Decoy leaves the battlefield, you may pay {2}. If you do, draw a card. + this.addAbility(new LeavesBattlefieldTriggeredAbility(new DoIfCostPaid( + new DrawCardSourceControllerEffect(1), new GenericManaCost(2) + ), false)); + } + + private PapercraftDecoy(final PapercraftDecoy card) { + super(card); + } + + @Override + public PapercraftDecoy copy() { + return new PapercraftDecoy(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/ParadiseDruid.java b/Mage.Sets/src/mage/cards/p/ParadiseDruid.java index 38b296e04df..13f1531bd90 100644 --- a/Mage.Sets/src/mage/cards/p/ParadiseDruid.java +++ b/Mage.Sets/src/mage/cards/p/ParadiseDruid.java @@ -34,7 +34,7 @@ public final class ParadiseDruid extends CardImpl { new GainAbilitySourceEffect( HexproofAbility.getInstance(), Duration.WhileOnBattlefield - ), new InvertCondition(SourceTappedCondition.instance), + ), SourceTappedCondition.UNTAPPED, "{this} has hexproof as long as it's untapped" ))); diff --git a/Mage.Sets/src/mage/cards/p/ParadoxEngine.java b/Mage.Sets/src/mage/cards/p/ParadoxEngine.java index 7f0757b8684..f93b153a515 100644 --- a/Mage.Sets/src/mage/cards/p/ParadoxEngine.java +++ b/Mage.Sets/src/mage/cards/p/ParadoxEngine.java @@ -1,28 +1,31 @@ package mage.cards.p; -import java.util.UUID; import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.effects.common.UntapAllControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SuperType; +import mage.filter.FilterPermanent; import mage.filter.common.FilterNonlandPermanent; +import java.util.UUID; + /** - * * @author fireshoes */ public final class ParadoxEngine extends CardImpl { + private static final FilterPermanent filter = new FilterNonlandPermanent("nonland permanents"); + public ParadoxEngine(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{5}"); this.addSuperType(SuperType.LEGENDARY); // Whenever you cast a spell, untap all nonland permanents you control. - this.addAbility(new SpellCastControllerTriggeredAbility(new UntapAllControllerEffect(new FilterNonlandPermanent()), false)); + this.addAbility(new SpellCastControllerTriggeredAbility(new UntapAllControllerEffect(filter), false)); } private ParadoxEngine(final ParadoxEngine card) { diff --git a/Mage.Sets/src/mage/cards/p/Paralyze.java b/Mage.Sets/src/mage/cards/p/Paralyze.java index 08d23301f3d..781c9ac6ea7 100644 --- a/Mage.Sets/src/mage/cards/p/Paralyze.java +++ b/Mage.Sets/src/mage/cards/p/Paralyze.java @@ -18,6 +18,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; +import mage.util.CardUtil; /** * @@ -81,6 +82,6 @@ class ParalyzeEffect extends DoIfCostPaid { @Override public String getText(Mode mode) { - return "that player may " + getCostText() + ". If they do, " + executingEffects.getText(mode); + return "that player may " + CardUtil.addCostVerb(cost.getText()) + ". If they do, " + executingEffects.getText(mode); } } diff --git a/Mage.Sets/src/mage/cards/p/Paraselene.java b/Mage.Sets/src/mage/cards/p/Paraselene.java index ea2234bfa05..457fe3ef54d 100644 --- a/Mage.Sets/src/mage/cards/p/Paraselene.java +++ b/Mage.Sets/src/mage/cards/p/Paraselene.java @@ -51,7 +51,7 @@ class ParaseleneEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { int count = 0; - for (Permanent permanent : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_ENCHANTMENT_PERMANENT, source.getControllerId(), source.getSourceId(), game)) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_ENCHANTMENT, source.getControllerId(), source.getSourceId(), game)) { if (permanent.destroy(source, game, false)) { count++; } diff --git a/Mage.Sets/src/mage/cards/p/ParasiticGrasp.java b/Mage.Sets/src/mage/cards/p/ParasiticGrasp.java new file mode 100644 index 00000000000..02c0c381b73 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/ParasiticGrasp.java @@ -0,0 +1,48 @@ +package mage.cards.p; + +import mage.abilities.Ability; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.keyword.CleaveAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ParasiticGrasp extends CardImpl { + + private static final FilterPermanent filter = new FilterCreaturePermanent(SubType.HUMAN, "[Human] creature"); + + public ParasiticGrasp(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{B}"); + + // Cleave {1}{B}{B} + Ability ability = new CleaveAbility(this, new DamageTargetEffect(3), "{1}{B}{B}"); + ability.addEffect(new GainLifeEffect(3)); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + + // Parasitic Grasp deals 3 damage to target [Human] creature. You gain 3 life. + this.getSpellAbility().addEffect(new DamageTargetEffect(3)); + this.getSpellAbility().addEffect(new GainLifeEffect(3)); + this.getSpellAbility().addTarget(new TargetPermanent(filter)); + } + + private ParasiticGrasp(final ParasiticGrasp card) { + super(card); + } + + @Override + public ParasiticGrasp copy() { + return new ParasiticGrasp(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/ParasiticImpetus.java b/Mage.Sets/src/mage/cards/p/ParasiticImpetus.java index 9a0dad6d1e8..1967a6ffcc4 100644 --- a/Mage.Sets/src/mage/cards/p/ParasiticImpetus.java +++ b/Mage.Sets/src/mage/cards/p/ParasiticImpetus.java @@ -2,10 +2,11 @@ package mage.cards.p; import mage.abilities.Ability; import mage.abilities.common.AttacksAttachedTriggeredAbility; -import mage.abilities.common.GoadAttachedAbility; +import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.LoseLifeControllerAttachedEffect; +import mage.abilities.effects.common.combat.GoadAttachedEffect; import mage.abilities.effects.common.continuous.BoostEnchantedEffect; import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; @@ -37,7 +38,9 @@ public final class ParasiticImpetus extends CardImpl { this.addAbility(ability); // Enchanted creature gets +2/+2 and is goaded. - this.addAbility(new GoadAttachedAbility(new BoostEnchantedEffect(2, 2))); + ability = new SimpleStaticAbility(new BoostEnchantedEffect(2, 2)); + ability.addEffect(new GoadAttachedEffect()); + this.addAbility(ability); // Whenever enchanted creature attacks, its controller loses 2 life and you gain 2 life. ability = new AttacksAttachedTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/p/ParishBladeTrainee.java b/Mage.Sets/src/mage/cards/p/ParishBladeTrainee.java new file mode 100644 index 00000000000..996ecaac37f --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/ParishBladeTrainee.java @@ -0,0 +1,81 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.TrainingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ParishBladeTrainee extends CardImpl { + + public ParishBladeTrainee(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(1); + this.toughness = new MageInt(2); + + // Training + this.addAbility(new TrainingAbility()); + + // When Parish-Blade Trainee dies, put its counters on target creature you control. + Ability ability = new DiesSourceTriggeredAbility(new ParishBladeTraineeEffect()); + ability.addTarget(new TargetControlledCreaturePermanent()); + this.addAbility(ability); + } + + private ParishBladeTrainee(final ParishBladeTrainee card) { + super(card); + } + + @Override + public ParishBladeTrainee copy() { + return new ParishBladeTrainee(this); + } +} + +class ParishBladeTraineeEffect extends OneShotEffect { + + ParishBladeTraineeEffect() { + super(Outcome.Benefit); + staticText = "put its counters on target creature you control"; + } + + private ParishBladeTraineeEffect(final ParishBladeTraineeEffect effect) { + super(effect); + } + + @Override + public ParishBladeTraineeEffect copy() { + return new ParishBladeTraineeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent sourcePermanent = (Permanent) getValue("permanentLeftBattlefield"); + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (sourcePermanent == null || permanent == null) { + return false; + } + sourcePermanent + .getCounters(game) + .values() + .stream() + .forEach(counter -> permanent.addCounters(counter, source.getControllerId(), source, game)); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/p/Paroxysm.java b/Mage.Sets/src/mage/cards/p/Paroxysm.java index c3b1a0228d1..1a7a9cf730e 100644 --- a/Mage.Sets/src/mage/cards/p/Paroxysm.java +++ b/Mage.Sets/src/mage/cards/p/Paroxysm.java @@ -97,7 +97,7 @@ class ParoxysmEffect extends OneShotEffect { creatureAttachedTo.destroy(source, game, false); } else { ContinuousEffect effect = new BoostTargetEffect(3, 3, Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(creatureAttachedTo.getId())); + effect.setTargetPointer(new FixedTarget(creatureAttachedTo.getId(), game)); game.addEffect(effect, source); } return true; diff --git a/Mage.Sets/src/mage/cards/p/PatchworkAutomaton.java b/Mage.Sets/src/mage/cards/p/PatchworkAutomaton.java new file mode 100644 index 00000000000..cdec5818298 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PatchworkAutomaton.java @@ -0,0 +1,49 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.WardAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.FilterSpell; +import mage.filter.common.FilterArtifactSpell; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PatchworkAutomaton extends CardImpl { + + private static final FilterSpell filter = new FilterArtifactSpell("an artifact spell"); + + public PatchworkAutomaton(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{2}"); + + this.subtype.add(SubType.CONSTRUCT); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Ward {2} + this.addAbility(new WardAbility(new ManaCostsImpl<>("{2}"))); + + // Whenever you cast an artifact spell, put a +1/+1 counter on Patchwork Automaton. + this.addAbility(new SpellCastControllerTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), filter, false + )); + } + + private PatchworkAutomaton(final PatchworkAutomaton card) { + super(card); + } + + @Override + public PatchworkAutomaton copy() { + return new PatchworkAutomaton(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PatchworkCrawler.java b/Mage.Sets/src/mage/cards/p/PatchworkCrawler.java new file mode 100644 index 00000000000..680fa682921 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PatchworkCrawler.java @@ -0,0 +1,91 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.ActivatedAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.common.ExileTargetForSourceEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.game.ExileZone; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCardInYourGraveyard; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PatchworkCrawler extends CardImpl { + + public PatchworkCrawler(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); + + this.subtype.add(SubType.ZOMBIE); + this.subtype.add(SubType.HORROR); + this.power = new MageInt(1); + this.toughness = new MageInt(2); + + // {2}{U}: Exile target creature card from your graveyard and put a +1/+1 counter on Patchwork Crawler. + Ability ability = new SimpleActivatedAbility(new ExileTargetForSourceEffect(), new ManaCostsImpl<>("{2}{U}")); + ability.addEffect(new AddCountersSourceEffect(CounterType.P1P1.createInstance()).concatBy("and")); + ability.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); + this.addAbility(ability); + + // Patchwork Crawler has all activated abilities of all creature cards exiled with it. + this.addAbility(new SimpleStaticAbility(new PatchworkCrawlerEffect())); + } + + private PatchworkCrawler(final PatchworkCrawler card) { + super(card); + } + + @Override + public PatchworkCrawler copy() { + return new PatchworkCrawler(this); + } +} + +class PatchworkCrawlerEffect extends ContinuousEffectImpl { + + PatchworkCrawlerEffect() { + super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); + staticText = "{this} has all activated abilities of all creature cards exiled with it"; + } + + private PatchworkCrawlerEffect(final PatchworkCrawlerEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source)); + if (permanent == null || exileZone == null || exileZone.isEmpty()) { + return false; + } + for (Card card : exileZone.getCards(StaticFilters.FILTER_CARD_CREATURE, game)) { + for (Ability ability : card.getAbilities(game)) { + if (ability instanceof ActivatedAbility) { + permanent.addAbility(ability, source.getSourceId(), game); + } + } + } + return true; + } + + @Override + public PatchworkCrawlerEffect copy() { + return new PatchworkCrawlerEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PathOfAncestry.java b/Mage.Sets/src/mage/cards/p/PathOfAncestry.java index 6350f97c25c..f825b9bbd0a 100644 --- a/Mage.Sets/src/mage/cards/p/PathOfAncestry.java +++ b/Mage.Sets/src/mage/cards/p/PathOfAncestry.java @@ -120,6 +120,8 @@ class PathOfAncestryTriggeredAbility extends DelayedTriggeredAbility { @Override public String getRule() { - return "When that mana is spent to cast a creature spell that shares a creature type with your commander, scry 1."; + return "When that mana is spent to cast a creature spell that shares a creature type with your commander, " + + "scry 1. " + + "(Look at the top card of your library. You may put that card on the bottom of your library.)"; } } diff --git a/Mage.Sets/src/mage/cards/p/PathOfMettle.java b/Mage.Sets/src/mage/cards/p/PathOfMettle.java index 78af4037d56..daca6c0fcfd 100644 --- a/Mage.Sets/src/mage/cards/p/PathOfMettle.java +++ b/Mage.Sets/src/mage/cards/p/PathOfMettle.java @@ -1,9 +1,8 @@ - package mage.cards.p; import java.util.UUID; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.AttacksWithCreaturesTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.common.DamageAllEffect; import mage.abilities.effects.common.TransformSourceEffect; @@ -16,28 +15,33 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SuperType; -import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.AbilityPredicate; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; /** * @author LevelX2 */ public final class PathOfMettle extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature that doesn't have first strike, double strike, vigilance, or haste"); + private static final FilterCreaturePermanent filterDamage = new FilterCreaturePermanent("creature that doesn't have first strike, double strike, vigilance, or haste"); + private static final FilterCreaturePermanent filterTransform = new FilterCreaturePermanent("creatures that have first strike, double strike, vigilance, and/or haste"); + private static final String triggerPhrase = "Whenever you attack with at least two " + filterTransform.getMessage() + ", "; static { - filter.add(Predicates.not(Predicates.or( + filterDamage.add(Predicates.not(Predicates.or( new AbilityPredicate(FirstStrikeAbility.class), new AbilityPredicate(DoubleStrikeAbility.class), new AbilityPredicate(VigilanceAbility.class), new AbilityPredicate(HasteAbility.class) ))); + + filterTransform.add(Predicates.or( + new AbilityPredicate(FirstStrikeAbility.class), + new AbilityPredicate(DoubleStrikeAbility.class), + new AbilityPredicate(VigilanceAbility.class), + new AbilityPredicate(HasteAbility.class) + )); } public PathOfMettle(UUID ownerId, CardSetInfo setInfo) { @@ -45,15 +49,14 @@ public final class PathOfMettle extends CardImpl { this.addSuperType(SuperType.LEGENDARY); - this.transformable = true; this.secondSideCardClazz = mage.cards.m.MetzaliTowerOfTriumph.class; // When Path of Mettle enters the battlefield, it deals 1 damage to each creature that doesn't have first strike, double strike, vigilance, or haste. - this.addAbility(new EntersBattlefieldTriggeredAbility(new DamageAllEffect(1, filter))); + this.addAbility(new EntersBattlefieldTriggeredAbility(new DamageAllEffect(1, "it", filterDamage))); // Whenever you attack with at least two creatures that have first strike, double strike, vigilance, and/or haste, transform Path of Mettle. this.addAbility(new TransformAbility()); - this.addAbility(new PathOfMettleTriggeredAbility()); + this.addAbility(new AttacksWithCreaturesTriggeredAbility(new TransformSourceEffect(), 2, filterTransform).setTriggerPhrase(triggerPhrase)); } private PathOfMettle(final PathOfMettle card) { @@ -65,58 +68,3 @@ public final class PathOfMettle extends CardImpl { return new PathOfMettle(this); } } - -class PathOfMettleTriggeredAbility extends TriggeredAbilityImpl { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature that doesn't have first strike, double strike, vigilance, or haste"); - - static { - filter.add(Predicates.or( - new AbilityPredicate(FirstStrikeAbility.class), - new AbilityPredicate(DoubleStrikeAbility.class), - new AbilityPredicate(VigilanceAbility.class), - new AbilityPredicate(HasteAbility.class) - )); - } - - public PathOfMettleTriggeredAbility() { - super(Zone.BATTLEFIELD, new TransformSourceEffect(true)); - } - - public PathOfMettleTriggeredAbility(final PathOfMettleTriggeredAbility ability) { - super(ability); - } - - @Override - public PathOfMettleTriggeredAbility copy() { - return new PathOfMettleTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DECLARED_ATTACKERS; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - int attackerCount = 0; - if (game.getCombat() != null) { - if (isControlledBy(game.getCombat().getAttackingPlayerId())) { - for (UUID attackerId : game.getCombat().getAttackers()) { - Permanent attacker = game.getPermanent(attackerId); - if (attacker != null && filter.match(attacker, game)) { - attackerCount++; - } - } - return attackerCount >= 2; - } - } - return false; - - } - - @Override - public String getRule() { - return "Whenever you attack with at least two creatures that have first strike, double strike, vigilance, and/or haste, transform Path of Mettle"; - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/p/PathOfPeril.java b/Mage.Sets/src/mage/cards/p/PathOfPeril.java new file mode 100644 index 00000000000..b62971a1162 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PathOfPeril.java @@ -0,0 +1,48 @@ +package mage.cards.p; + +import mage.abilities.effects.common.DestroyAllEffect; +import mage.abilities.keyword.CleaveAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.ManaValuePredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PathOfPeril extends CardImpl { + + private static final FilterPermanent filter + = new FilterCreaturePermanent("creatures [with mana value 2 or less]"); + + static { + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 2)); + } + + public PathOfPeril(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}{B}"); + + // Cleave {4}{W}{B} + this.addAbility(new CleaveAbility( + this, new DestroyAllEffect(StaticFilters.FILTER_PERMANENT_CREATURES), "{4}{W}{B}" + )); + + // Destroy all creatures [with mana value 2 or less]. + this.getSpellAbility().addEffect(new DestroyAllEffect(filter)); + } + + private PathOfPeril(final PathOfPeril card) { + super(card); + } + + @Override + public PathOfPeril copy() { + return new PathOfPeril(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PathToTheFestival.java b/Mage.Sets/src/mage/cards/p/PathToTheFestival.java index 3964b46d1ec..58cd3c1babb 100644 --- a/Mage.Sets/src/mage/cards/p/PathToTheFestival.java +++ b/Mage.Sets/src/mage/cards/p/PathToTheFestival.java @@ -34,7 +34,9 @@ public final class PathToTheFestival extends CardImpl { )); this.getSpellAbility().addEffect(new ConditionalOneShotEffect( new ScryEffect(1), PathToTheFestivalCondition.instance, - "Then if there are three or more basic land types among lands you control, scry 1" + "Then if there are three or more basic land types among lands you control, scry 1 " + + "(Look at the top card of your library. " + + "You may put that card on the bottom of your library.)" )); this.getSpellAbility().addHint(DomainHint.instance); diff --git a/Mage.Sets/src/mage/cards/p/PatriciansScorn.java b/Mage.Sets/src/mage/cards/p/PatriciansScorn.java index ff4ab3be71a..3cbb5106cda 100644 --- a/Mage.Sets/src/mage/cards/p/PatriciansScorn.java +++ b/Mage.Sets/src/mage/cards/p/PatriciansScorn.java @@ -15,7 +15,6 @@ import mage.filter.StaticFilters; import mage.filter.predicate.mageobject.ColorPredicate; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.stack.Spell; import mage.watchers.Watcher; @@ -34,7 +33,7 @@ public final class PatriciansScorn extends CardImpl { // If you've cast another white spell this turn, you may cast this spell without paying its mana cost. this.addAbility(new AlternativeCostSourceAbility(new CastWhiteSpellThisTurnCondition()), new PatriciansScornWatcher()); // Destroy all enchantments. - this.getSpellAbility().addEffect(new DestroyAllEffect(StaticFilters.FILTER_ENCHANTMENT_PERMANENT)); + this.getSpellAbility().addEffect(new DestroyAllEffect(StaticFilters.FILTER_PERMANENT_ENCHANTMENT)); } private PatriciansScorn(final PatriciansScorn card) { diff --git a/Mage.Sets/src/mage/cards/p/PatronOfTheValiant.java b/Mage.Sets/src/mage/cards/p/PatronOfTheValiant.java index fa2d06ab14b..e5fe8257585 100644 --- a/Mage.Sets/src/mage/cards/p/PatronOfTheValiant.java +++ b/Mage.Sets/src/mage/cards/p/PatronOfTheValiant.java @@ -3,21 +3,15 @@ package mage.cards.p; import java.util.UUID; import mage.MageInt; -import mage.MageObject; -import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.counter.AddCountersAllEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Outcome; import mage.counters.CounterType; -import mage.filter.common.FilterCreaturePermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; +import mage.filter.StaticFilters; /** * @@ -33,9 +27,12 @@ public final class PatronOfTheValiant extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); - + // When Patron of the Valiant enters the battlefield, put a +1/+1 counter on each creature you control with a +1/+1 counter on it. - this.addAbility(new EntersBattlefieldTriggeredAbility(new PatronOfTheValiantEffect())); + this.addAbility(new EntersBattlefieldTriggeredAbility(new AddCountersAllEffect( + CounterType.P1P1.createInstance(), + StaticFilters.FILTER_CONTROLLED_CREATURE_P1P1 + ))); } private PatronOfTheValiant(final PatronOfTheValiant card) { @@ -47,39 +44,3 @@ public final class PatronOfTheValiant extends CardImpl { return new PatronOfTheValiant(this); } } - -class PatronOfTheValiantEffect extends OneShotEffect { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); - - static { - filter.add(CounterType.P1P1.getPredicate()); - } - - public PatronOfTheValiantEffect() { - super(Outcome.Benefit); - this.staticText = "put a +1/+1 counter on each creature you control with a +1/+1 counter on it."; - } - - public PatronOfTheValiantEffect(final PatronOfTheValiantEffect effect) { - super(effect); - } - - @Override - public PatronOfTheValiantEffect copy() { - return new PatronOfTheValiantEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = source.getSourceObject(game); - if (controller != null && sourceObject != null) { - for(Permanent permanent: game.getState().getBattlefield().getAllActivePermanents(filter , controller.getId(), game)) { - permanent.addCounters(CounterType.P1P1.createInstance(), source.getControllerId(), source, game); - game.informPlayers(sourceObject.getName() + ": Put a +1/+1 counter on " + permanent.getLogName()); - } - } - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/p/PatronOfTheVein.java b/Mage.Sets/src/mage/cards/p/PatronOfTheVein.java index df94f64b771..36d44d7c029 100644 --- a/Mage.Sets/src/mage/cards/p/PatronOfTheVein.java +++ b/Mage.Sets/src/mage/cards/p/PatronOfTheVein.java @@ -1,4 +1,3 @@ - package mage.cards.p; import java.util.UUID; @@ -18,11 +17,10 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.TargetController; import mage.constants.Zone; import mage.counters.CounterType; +import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; @@ -37,12 +35,6 @@ import mage.target.targetpointer.FixedTarget; */ public final class PatronOfTheVein extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public PatronOfTheVein(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}{B}"); @@ -56,7 +48,7 @@ public final class PatronOfTheVein extends CardImpl { // When Patron of the Vein enters the battlefield, destroy target creature an opponent controls. Ability ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect(), false); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); this.addAbility(ability); // Whenever a creature an opponent controls dies, exile it and put a +1/+1 counter on each Vampire you control. @@ -99,9 +91,10 @@ class PatronOfTheVeinCreatureDiesTriggeredAbility extends TriggeredAbilityImpl { if (((ZoneChangeEvent) event).isDiesEvent()) { if (game.getOpponents(this.controllerId).contains(event.getPlayerId())) { Card creature = game.getPermanentOrLKIBattlefield(event.getTargetId()); - if (creature != null && creature.isCreature(game)) { + if (creature != null + && creature.isCreature(game)) { for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(creature.getId())); + effect.setTargetPointer(new FixedTarget(creature.getId(), game)); } return true; } @@ -112,7 +105,7 @@ class PatronOfTheVeinCreatureDiesTriggeredAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "Whenever a creature an opponent controls dies, exile it and put a +1/+1 counter on each Vampire you control"; + return "Whenever a creature an opponent controls dies, exile it and put a +1/+1 counter on each Vampire you control."; } } @@ -141,7 +134,7 @@ class PatronOfTheVeinExileCreatureEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if(controller == null){ + if (controller == null) { return false; } MageObject sourceObject = source.getSourceObject(game); @@ -149,7 +142,7 @@ class PatronOfTheVeinExileCreatureEffect extends OneShotEffect { if (card != null) { Effect effect = new ExileTargetEffect(); - effect.setTargetPointer(new FixedTarget(card.getId())); + effect.setTargetPointer(new FixedTarget(card.getId(), game)); effect.apply(game, source); } diff --git a/Mage.Sets/src/mage/cards/p/PeaceAndQuiet.java b/Mage.Sets/src/mage/cards/p/PeaceAndQuiet.java index cfbea65323c..1d4894d2005 100644 --- a/Mage.Sets/src/mage/cards/p/PeaceAndQuiet.java +++ b/Mage.Sets/src/mage/cards/p/PeaceAndQuiet.java @@ -22,7 +22,7 @@ public final class PeaceAndQuiet extends CardImpl { // Destroy two target enchantments. this.getSpellAbility().addEffect(new DestroyTargetEffect()); - this.getSpellAbility().addTarget(new TargetPermanent(2, StaticFilters.FILTER_ENCHANTMENT_PERMANENT)); + this.getSpellAbility().addTarget(new TargetPermanent(2, StaticFilters.FILTER_PERMANENT_ENCHANTMENT)); } private PeaceAndQuiet(final PeaceAndQuiet card) { diff --git a/Mage.Sets/src/mage/cards/p/PearlspearCourier.java b/Mage.Sets/src/mage/cards/p/PearlspearCourier.java index ceb4a006f0f..5c341c1067b 100644 --- a/Mage.Sets/src/mage/cards/p/PearlspearCourier.java +++ b/Mage.Sets/src/mage/cards/p/PearlspearCourier.java @@ -45,10 +45,10 @@ public final class PearlspearCourier extends CardImpl { this.addAbility(new SkipUntapOptionalAbility()); // {2}{W}, {tap}: Target Soldier creature gets +2/+2 and has vigilance for as long as Pearlspear Courier remains tapped. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect( - new BoostTargetEffect(2, 2, Duration.Custom), SourceTappedCondition.instance, + new BoostTargetEffect(2, 2, Duration.Custom), SourceTappedCondition.TAPPED, "target Soldier creature gets +2/+2"), new ManaCostsImpl("{2}{W}")); ability.addEffect(new ConditionalContinuousEffect(new GainAbilityTargetEffect(VigilanceAbility.getInstance(), - Duration.Custom), SourceTappedCondition.instance,"and has vigilance for as long as {this} remains tapped")); + Duration.Custom), SourceTappedCondition.TAPPED,"and has vigilance for as long as {this} remains tapped")); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent(filter)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/p/PeerlessSamurai.java b/Mage.Sets/src/mage/cards/p/PeerlessSamurai.java new file mode 100644 index 00000000000..94a7b4d4b43 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PeerlessSamurai.java @@ -0,0 +1,101 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.common.AttacksAloneControlledTriggeredAbility; +import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.util.CardUtil; +import mage.watchers.common.CastSpellLastTurnWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PeerlessSamurai extends CardImpl { + + public PeerlessSamurai(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SAMURAI); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Menace + this.addAbility(new MenaceAbility()); + + // Whenever a Samurai or Warrior you control attacks alone, the next spell you cast this turn costs {1} less to cast. + this.addAbility(new AttacksAloneControlledTriggeredAbility( + new PeerlessSamuraiEffect(), + StaticFilters.FILTER_CONTROLLED_SAMURAI_OR_WARRIOR, + false, false + )); + } + + private PeerlessSamurai(final PeerlessSamurai card) { + super(card); + } + + @Override + public PeerlessSamurai copy() { + return new PeerlessSamurai(this); + } +} + +class PeerlessSamuraiEffect extends CostModificationEffectImpl { + + int spellsCast; + + public PeerlessSamuraiEffect() { + super(Duration.EndOfTurn, Outcome.Benefit, CostModificationType.REDUCE_COST); + staticText = "the next spell you cast this turn costs {1} less to cast"; + } + + protected PeerlessSamuraiEffect(final PeerlessSamuraiEffect effect) { + super(effect); + this.spellsCast = effect.spellsCast; + } + + @Override + public void init(Ability source, Game game) { + super.init(source, game); + CastSpellLastTurnWatcher watcher = game.getState().getWatcher(CastSpellLastTurnWatcher.class); + if (watcher != null) { + spellsCast = watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(source.getControllerId()); + } + } + + @Override + public boolean apply(Game game, Ability source, Ability abilityToModify) { + CardUtil.reduceCost(abilityToModify, 1); + return true; + } + + @Override + public boolean applies(Ability abilityToModify, Ability source, Game game) { + CastSpellLastTurnWatcher watcher = game.getState().getWatcher(CastSpellLastTurnWatcher.class); + if (watcher != null) { + if (watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(source.getControllerId()) > spellsCast) { + discard(); // only one use + return false; + } + } + if (abilityToModify instanceof SpellAbility) { + return abilityToModify.isControlledBy(source.getControllerId()); + } + return false; + } + + @Override + public PeerlessSamuraiEffect copy() { + return new PeerlessSamuraiEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PelakkaPredation.java b/Mage.Sets/src/mage/cards/p/PelakkaPredation.java index bd09c73c039..8c7d186c490 100644 --- a/Mage.Sets/src/mage/cards/p/PelakkaPredation.java +++ b/Mage.Sets/src/mage/cards/p/PelakkaPredation.java @@ -8,7 +8,6 @@ import mage.cards.ModalDoubleFacesCard; import mage.constants.CardType; import mage.constants.ComparisonType; import mage.constants.SubType; -import mage.constants.TargetController; import mage.filter.FilterCard; import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetOpponent; @@ -36,8 +35,8 @@ public final class PelakkaPredation extends ModalDoubleFacesCard { // Pelakka Predation // Sorcery - // Target opponent reveals their hand. You may choose a card from it with converted mana cost 3 or greater. That player discards that card. - this.getLeftHalfCard().getSpellAbility().addEffect(new DiscardCardYouChooseTargetEffect(filter, TargetController.OPPONENT)); + // Target opponent reveals their hand. You may choose a card from it with mana value 3 or greater. That player discards that card. + this.getLeftHalfCard().getSpellAbility().addEffect(new DiscardCardYouChooseTargetEffect(filter)); this.getLeftHalfCard().getSpellAbility().addTarget(new TargetOpponent()); // 2. diff --git a/Mage.Sets/src/mage/cards/p/PersistentNightmare.java b/Mage.Sets/src/mage/cards/p/PersistentNightmare.java index 13511fdea1a..d1825f3f44b 100644 --- a/Mage.Sets/src/mage/cards/p/PersistentNightmare.java +++ b/Mage.Sets/src/mage/cards/p/PersistentNightmare.java @@ -1,7 +1,5 @@ - package mage.cards.p; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; import mage.abilities.effects.common.ReturnToHandSourceEffect; @@ -11,14 +9,15 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class PersistentNightmare extends CardImpl { public PersistentNightmare(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},""); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); this.subtype.add(SubType.NIGHTMARE); this.power = new MageInt(1); this.toughness = new MageInt(1); @@ -31,7 +30,9 @@ public final class PersistentNightmare extends CardImpl { this.addAbility(new SkulkAbility()); // When Persistent Nightmare deals combat damage to a player, return it to its owner's hand. - this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new ReturnToHandSourceEffect(), false)); + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility( + new ReturnToHandSourceEffect(), false + ).setTriggerPhrase("When {this} deals combat damage to a player, ")); } private PersistentNightmare(final PersistentNightmare card) { diff --git a/Mage.Sets/src/mage/cards/p/PersistentSpecimen.java b/Mage.Sets/src/mage/cards/p/PersistentSpecimen.java new file mode 100644 index 00000000000..b1432a02790 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PersistentSpecimen.java @@ -0,0 +1,43 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Zone; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PersistentSpecimen extends CardImpl { + + public PersistentSpecimen(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}"); + + this.subtype.add(SubType.SKELETON); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // {2}{B}: Return Persistent Specimen from your graveyard to the battlefield tapped. + this.addAbility(new SimpleActivatedAbility( + Zone.GRAVEYARD, + new ReturnSourceFromGraveyardToBattlefieldEffect(true, false), + new ManaCostsImpl<>("{2}{B}") + )); + } + + private PersistentSpecimen(final PersistentSpecimen card) { + super(card); + } + + @Override + public PersistentSpecimen copy() { + return new PersistentSpecimen(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PestilentSouleater.java b/Mage.Sets/src/mage/cards/p/PestilentSouleater.java index d9b448d4428..7f631d8a1a4 100644 --- a/Mage.Sets/src/mage/cards/p/PestilentSouleater.java +++ b/Mage.Sets/src/mage/cards/p/PestilentSouleater.java @@ -1,37 +1,34 @@ - package mage.cards.p; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.mana.PhyrexianManaCost; +import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; import mage.abilities.keyword.InfectAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.ColoredManaSymbol; import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.SubType; + +import java.util.UUID; /** - * * @author North */ public final class PestilentSouleater extends CardImpl { public PestilentSouleater(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{5}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{5}"); this.subtype.add(SubType.PHYREXIAN); this.subtype.add(SubType.INSECT); this.power = new MageInt(3); this.toughness = new MageInt(3); - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, - new GainAbilitySourceEffect(InfectAbility.getInstance(), Duration.EndOfTurn), - new PhyrexianManaCost(ColoredManaSymbol.B))); + this.addAbility(new SimpleActivatedAbility(new GainAbilitySourceEffect( + InfectAbility.getInstance(), Duration.EndOfTurn + ), new ManaCostsImpl<>("{B/P}"))); } private PestilentSouleater(final PestilentSouleater card) { diff --git a/Mage.Sets/src/mage/cards/p/PestilentSpirit.java b/Mage.Sets/src/mage/cards/p/PestilentSpirit.java index 2edb9ec3292..c051388cc0e 100644 --- a/Mage.Sets/src/mage/cards/p/PestilentSpirit.java +++ b/Mage.Sets/src/mage/cards/p/PestilentSpirit.java @@ -37,7 +37,7 @@ public final class PestilentSpirit extends CardImpl { this.toughness = new MageInt(2); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // Deathtouch this.addAbility(DeathtouchAbility.getInstance()); diff --git a/Mage.Sets/src/mage/cards/p/PetalmaneBaku.java b/Mage.Sets/src/mage/cards/p/PetalmaneBaku.java index 7274c36cc9f..6bf0e934267 100644 --- a/Mage.Sets/src/mage/cards/p/PetalmaneBaku.java +++ b/Mage.Sets/src/mage/cards/p/PetalmaneBaku.java @@ -1,4 +1,3 @@ - package mage.cards.p; import java.util.UUID; @@ -7,7 +6,7 @@ import mage.Mana; import mage.abilities.Ability; import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.costs.common.RemoveVariableCountersSourceCost; -import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.dynamicvalue.common.CountersSourceCount; import mage.abilities.dynamicvalue.common.RemovedCountersForCostValue; import mage.abilities.effects.common.counter.AddCountersSourceEffect; @@ -31,18 +30,17 @@ public final class PetalmaneBaku extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(2); - // Whenever you cast a Spirit or Arcane spell, you may put a ki counter on Skullmane Baku. - this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.KI.createInstance()), StaticFilters.SPIRIT_OR_ARCANE_CARD, true)); + // Whenever you cast a Spirit or Arcane spell, you may put a ki counter on Petalmane Baku. + this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.KI.createInstance()), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true)); // {1}, Remove X ki counters from Petalmane Baku: Add X mana of any one color. Ability ability = new DynamicManaAbility( new Mana(0, 0, 0, 0, 0, 0, 1, 0), RemovedCountersForCostValue.instance, - new ManaCostsImpl<>("{1}"), + new GenericManaCost(1), "Add X mana of any one color", true, new CountersSourceCount(CounterType.KI)); - ability.addCost(new RemoveVariableCountersSourceCost(CounterType.KI.createInstance(), - "Remove X ki counters from {this}")); + ability.addCost(new RemoveVariableCountersSourceCost(CounterType.KI.createInstance())); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/p/Petradon.java b/Mage.Sets/src/mage/cards/p/Petradon.java index 7eabfd4c745..b35324d3a75 100644 --- a/Mage.Sets/src/mage/cards/p/Petradon.java +++ b/Mage.Sets/src/mage/cards/p/Petradon.java @@ -15,7 +15,7 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.common.FilterLandPermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetLandPermanent; import java.util.UUID; @@ -34,7 +34,7 @@ public final class Petradon extends CardImpl { // When Petradon enters the battlefield, exile two target lands. Ability ability = new EntersBattlefieldTriggeredAbility(new ExileTargetForSourceEffect(), false); - ability.addTarget(new TargetLandPermanent(2, 2, new FilterLandPermanent("lands"), false)); + ability.addTarget(new TargetLandPermanent(2, 2, StaticFilters.FILTER_LANDS, false)); this.addAbility(ability); // When Petradon leaves the battlefield, return the exiled cards to the battlefield under their owners' control. diff --git a/Mage.Sets/src/mage/cards/p/PhantasmalMount.java b/Mage.Sets/src/mage/cards/p/PhantasmalMount.java index a5eb8aed96d..6778075b0ad 100644 --- a/Mage.Sets/src/mage/cards/p/PhantasmalMount.java +++ b/Mage.Sets/src/mage/cards/p/PhantasmalMount.java @@ -25,7 +25,6 @@ import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.ToughnessPredicate; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.targetpointer.FixedTarget; @@ -88,15 +87,15 @@ class PhantasmalMountEffect extends OneShotEffect { Permanent targetCreature = game.getPermanent(source.getFirstTarget()); if (targetCreature != null) { ContinuousEffect effect = new BoostTargetEffect(1, 1, Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(source.getFirstTarget())); + effect.setTargetPointer(new FixedTarget(source.getFirstTarget(), game)); game.addEffect(effect, source); Effect sacrificeCreatureEffect = new SacrificeTargetEffect(); Effect sacrificePhantasmalMountEffect = new SacrificeTargetEffect(); ContinuousEffect gainAbility = new GainAbilityTargetEffect(FlyingAbility.getInstance(), Duration.EndOfTurn); - gainAbility.setTargetPointer(new FixedTarget(source.getFirstTarget())); + gainAbility.setTargetPointer(new FixedTarget(source.getFirstTarget(), game)); game.addEffect(gainAbility, source); - sacrificeCreatureEffect.setTargetPointer(new FixedTarget(source.getFirstTarget())); - sacrificePhantasmalMountEffect.setTargetPointer(new FixedTarget(source.getSourceId())); + sacrificeCreatureEffect.setTargetPointer(new FixedTarget(source.getFirstTarget(), game)); + sacrificePhantasmalMountEffect.setTargetPointer(new FixedTarget(source.getSourceId(), game)); DelayedTriggeredAbility dTA = new PhantasmalMountDelayedTriggeredAbility( sacrificeCreatureEffect, source.getSourceId()); diff --git a/Mage.Sets/src/mage/cards/p/PharikasLibation.java b/Mage.Sets/src/mage/cards/p/PharikasLibation.java index af543ea74d8..1be69411217 100644 --- a/Mage.Sets/src/mage/cards/p/PharikasLibation.java +++ b/Mage.Sets/src/mage/cards/p/PharikasLibation.java @@ -27,7 +27,7 @@ public final class PharikasLibation extends CardImpl { // • Target opponent sacrifices an enchantment. Mode mode = new Mode(new SacrificeEffect( - StaticFilters.FILTER_ENCHANTMENT_PERMANENT, 1, "Target opponent" + StaticFilters.FILTER_PERMANENT_ENCHANTMENT, 1, "Target opponent" )); mode.addTarget(new TargetOpponent()); this.getSpellAbility().addMode(mode); diff --git a/Mage.Sets/src/mage/cards/p/PheresBandBrawler.java b/Mage.Sets/src/mage/cards/p/PheresBandBrawler.java index b4a3693517b..ec66b0dd977 100644 --- a/Mage.Sets/src/mage/cards/p/PheresBandBrawler.java +++ b/Mage.Sets/src/mage/cards/p/PheresBandBrawler.java @@ -28,7 +28,8 @@ public final class PheresBandBrawler extends CardImpl { // When Pheres-Band Brawler enters the battlefield, it fights up to one target creature you don't control. Ability ability = new EntersBattlefieldTriggeredAbility(new FightTargetSourceEffect() - .setText("it fights up to one target creature you don't control")); + .setText("it fights up to one target creature you don't control. " + + "(Each deals damage equal to its power to the other.)")); ability.addTarget(new TargetPermanent(0, 1, StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL, false)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/p/PhyrexianMetamorph.java b/Mage.Sets/src/mage/cards/p/PhyrexianMetamorph.java index 8acb010d794..a6509f1a5a0 100644 --- a/Mage.Sets/src/mage/cards/p/PhyrexianMetamorph.java +++ b/Mage.Sets/src/mage/cards/p/PhyrexianMetamorph.java @@ -1,4 +1,3 @@ - package mage.cards.p; import java.util.UUID; @@ -44,9 +43,7 @@ public final class PhyrexianMetamorph extends CardImpl { CopyApplier phyrexianMetamorphCopyApplier = new CopyApplier() { @Override public boolean apply(Game game, MageObject blueprint, Ability source, UUID copyToObjectId) { - if (!blueprint.isArtifact(game)) { - blueprint.addCardType(game, CardType.ARTIFACT); - } + blueprint.addCardType(CardType.ARTIFACT); return true; } }; diff --git a/Mage.Sets/src/mage/cards/p/PhyrexianScriptures.java b/Mage.Sets/src/mage/cards/p/PhyrexianScriptures.java index 5f9b54963c3..5ce2a79045e 100644 --- a/Mage.Sets/src/mage/cards/p/PhyrexianScriptures.java +++ b/Mage.Sets/src/mage/cards/p/PhyrexianScriptures.java @@ -42,7 +42,7 @@ public final class PhyrexianScriptures extends CardImpl { this.subtype.add(SubType.SAGA); // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) - SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); + SagaAbility sagaAbility = new SagaAbility(this); // I — Put a +1/+1 counter on up to one target creature. That creature becomes an artifact in addition to its other types. Effects effects = new Effects(); diff --git a/Mage.Sets/src/mage/cards/p/PiannaNomadCaptain.java b/Mage.Sets/src/mage/cards/p/PiannaNomadCaptain.java index 682d11fc34f..2f6a8f8b72f 100644 --- a/Mage.Sets/src/mage/cards/p/PiannaNomadCaptain.java +++ b/Mage.Sets/src/mage/cards/p/PiannaNomadCaptain.java @@ -1,4 +1,3 @@ - package mage.cards.p; import java.util.UUID; @@ -11,20 +10,13 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; import mage.constants.SuperType; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AttackingPredicate; +import mage.filter.StaticFilters; /** * * @author cbt33 */ public final class PiannaNomadCaptain extends CardImpl { - - static final FilterCreaturePermanent filter = new FilterCreaturePermanent("attacking creatures"); - - static { - filter.add(AttackingPredicate.instance); - } public PiannaNomadCaptain(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{W}{W}"); @@ -36,7 +28,7 @@ public final class PiannaNomadCaptain extends CardImpl { this.toughness = new MageInt(2); // Whenever Pianna, Nomad Captain attacks, attacking creatures get +1/+1 until end of turn. - this.addAbility(new AttacksTriggeredAbility(new BoostAllEffect(1, 1, Duration.EndOfTurn, filter, false), false)); + this.addAbility(new AttacksTriggeredAbility(new BoostAllEffect(1, 1, Duration.EndOfTurn, StaticFilters.FILTER_ATTACKING_CREATURES, false), false)); } private PiannaNomadCaptain(final PiannaNomadCaptain card) { diff --git a/Mage.Sets/src/mage/cards/p/PiercingLight.java b/Mage.Sets/src/mage/cards/p/PiercingLight.java new file mode 100644 index 00000000000..425d097133f --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PiercingLight.java @@ -0,0 +1,38 @@ +package mage.cards.p; + +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.keyword.ScryEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterAttackingOrBlockingCreature; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PiercingLight extends CardImpl { + + private static final FilterPermanent filter = new FilterAttackingOrBlockingCreature(); + + public PiercingLight(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{W}"); + + // Piercing Light deals 2 damage to target attacking or blocking creature. Scry 1. + this.getSpellAbility().addEffect(new DamageTargetEffect(2)); + this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addEffect(new ScryEffect(1)); + } + + private PiercingLight(final PiercingLight card) { + super(card); + } + + @Override + public PiercingLight copy() { + return new PiercingLight(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PietyCharm.java b/Mage.Sets/src/mage/cards/p/PietyCharm.java index 6d1c99fc043..7f9df2e9135 100644 --- a/Mage.Sets/src/mage/cards/p/PietyCharm.java +++ b/Mage.Sets/src/mage/cards/p/PietyCharm.java @@ -13,7 +13,7 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; import mage.filter.FilterPermanent; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.permanent.AttachedToPredicate; import mage.target.TargetPermanent; @@ -47,7 +47,7 @@ public final class PietyCharm extends CardImpl { this.getSpellAbility().addMode(mode); // or creatures you control gain vigilance until end of turn. mode = new Mode(); - mode.addEffect(new GainAbilityAllEffect(VigilanceAbility.getInstance(), Duration.EndOfTurn, new FilterControlledCreaturePermanent("creatures you control"))); + mode.addEffect(new GainAbilityAllEffect(VigilanceAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_CONTROLLED_CREATURES)); this.getSpellAbility().addMode(mode); } diff --git a/Mage.Sets/src/mage/cards/p/PillardropRescuer.java b/Mage.Sets/src/mage/cards/p/PillardropRescuer.java index 0cf5fce3980..b2e76c89aa6 100644 --- a/Mage.Sets/src/mage/cards/p/PillardropRescuer.java +++ b/Mage.Sets/src/mage/cards/p/PillardropRescuer.java @@ -10,7 +10,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.ComparisonType; import mage.constants.SubType; -import mage.filter.FilterCard; +import mage.filter.common.FilterCreatureCard; import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInYourGraveyard; @@ -21,8 +21,8 @@ import java.util.UUID; */ public final class PillardropRescuer extends CardImpl { - private static final FilterCard filter - = new FilterCard("creature card with mana value 3 or less from your graveyard"); + private static final FilterCreatureCard filter + = new FilterCreatureCard("creature card with mana value 3 or less from your graveyard"); static { filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); diff --git a/Mage.Sets/src/mage/cards/p/PiousEvangel.java b/Mage.Sets/src/mage/cards/p/PiousEvangel.java index e4dd6ae851d..c7ac5cb8e30 100644 --- a/Mage.Sets/src/mage/cards/p/PiousEvangel.java +++ b/Mage.Sets/src/mage/cards/p/PiousEvangel.java @@ -43,7 +43,6 @@ public final class PiousEvangel extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); - this.transformable = true; this.secondSideCardClazz = mage.cards.w.WaywardDisciple.class; // Whenever Pious Evangel or another creature enters the battlefield under your control, you gain 1 life. @@ -51,7 +50,7 @@ public final class PiousEvangel extends CardImpl { // {2}, {T}, Sacrifice another permanent: Transform Pious Evangel. this.addAbility(new TransformAbility()); - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new TransformSourceEffect(true), new GenericManaCost(2)); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new TransformSourceEffect(), new GenericManaCost(2)); ability.addCost(new TapSourceCost()); ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(filter2))); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/p/PiperOfTheSwarm.java b/Mage.Sets/src/mage/cards/p/PiperOfTheSwarm.java index 028bd0c5c15..4268f5cbb61 100644 --- a/Mage.Sets/src/mage/cards/p/PiperOfTheSwarm.java +++ b/Mage.Sets/src/mage/cards/p/PiperOfTheSwarm.java @@ -43,7 +43,7 @@ public final class PiperOfTheSwarm extends CardImpl { // Rats you control have menace. this.addAbility(new SimpleStaticAbility( - new GainAbilityControlledEffect(new MenaceAbility(), Duration.WhileOnBattlefield, filter) + new GainAbilityControlledEffect(new MenaceAbility(false), Duration.WhileOnBattlefield, filter) )); // {1}{B}, {T}: Create a 1/1 black Rat creature token. diff --git a/Mage.Sets/src/mage/cards/p/PipersMelody.java b/Mage.Sets/src/mage/cards/p/PipersMelody.java index 824f1a10833..6e4101121d3 100644 --- a/Mage.Sets/src/mage/cards/p/PipersMelody.java +++ b/Mage.Sets/src/mage/cards/p/PipersMelody.java @@ -1,4 +1,3 @@ - package mage.cards.p; import java.util.UUID; @@ -10,7 +9,7 @@ import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; -import mage.filter.common.FilterCreatureCard; +import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; @@ -26,7 +25,7 @@ public final class PipersMelody extends CardImpl { // Shuffle any number of target creature cards from your graveyard into your library. this.getSpellAbility().addEffect(new PipersMelodyShuffleEffect()); - this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, Integer.MAX_VALUE, new FilterCreatureCard("creature cards from your graveyard"))); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, Integer.MAX_VALUE, StaticFilters.FILTER_CARD_CREATURES_YOUR_GRAVEYARD)); } private PipersMelody(final PipersMelody card) { @@ -43,7 +42,7 @@ class PipersMelodyShuffleEffect extends OneShotEffect { PipersMelodyShuffleEffect() { super(Outcome.Neutral); - this.staticText = "Shuffle any number of target cards from your graveyard into your library"; + this.staticText = "Shuffle any number of target creature cards from your graveyard into your library"; } PipersMelodyShuffleEffect(final PipersMelodyShuffleEffect effect) { diff --git a/Mage.Sets/src/mage/cards/p/PiratesPillage.java b/Mage.Sets/src/mage/cards/p/PiratesPillage.java index 7a76d184298..f116d9ce1ef 100644 --- a/Mage.Sets/src/mage/cards/p/PiratesPillage.java +++ b/Mage.Sets/src/mage/cards/p/PiratesPillage.java @@ -1,7 +1,5 @@ - package mage.cards.p; -import java.util.UUID; import mage.abilities.costs.common.DiscardCardCost; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; @@ -10,8 +8,9 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.game.permanent.token.TreasureToken; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class PiratesPillage extends CardImpl { @@ -24,8 +23,7 @@ public final class PiratesPillage extends CardImpl { // Draw two cards and create two colorless Treasure artifacts with "{T}, Sacrifice this artifact: Add one mana of any color." this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(2)); - this.getSpellAbility().addEffect(new CreateTokenEffect(new TreasureToken(), 2) - .setText("and create two colorless Treasure artifacts with \"{T}, Sacrifice this artifact: Add one mana of any color")); + this.getSpellAbility().addEffect(new CreateTokenEffect(new TreasureToken(), 2).concatBy("and")); } private PiratesPillage(final PiratesPillage card) { diff --git a/Mage.Sets/src/mage/cards/p/PitFight.java b/Mage.Sets/src/mage/cards/p/PitFight.java index 5db874c4030..a34dbd719f5 100644 --- a/Mage.Sets/src/mage/cards/p/PitFight.java +++ b/Mage.Sets/src/mage/cards/p/PitFight.java @@ -1,24 +1,22 @@ - package mage.cards.p; -import java.util.UUID; import mage.abilities.effects.common.FightTargetsEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.other.AnotherTargetPredicate; +import mage.filter.StaticFilters; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class PitFight extends CardImpl { public PitFight(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{R/G}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{R/G}"); // Target creature you control fights another target creature. this.getSpellAbility().addEffect(new FightTargetsEffect()); @@ -26,9 +24,7 @@ public final class PitFight extends CardImpl { target.setTargetTag(1); this.getSpellAbility().addTarget(target); - FilterCreaturePermanent filter = new FilterCreaturePermanent("another creature to fight"); - filter.add(new AnotherTargetPredicate(2)); - TargetCreaturePermanent target2 = new TargetCreaturePermanent(filter); + TargetCreaturePermanent target2 = new TargetCreaturePermanent(StaticFilters.FILTER_ANOTHER_CREATURE_TARGET_2); target2.setTargetTag(2); this.getSpellAbility().addTarget(target2); } diff --git a/Mage.Sets/src/mage/cards/p/PitTrap.java b/Mage.Sets/src/mage/cards/p/PitTrap.java index 787a06747aa..3204e9eda03 100644 --- a/Mage.Sets/src/mage/cards/p/PitTrap.java +++ b/Mage.Sets/src/mage/cards/p/PitTrap.java @@ -1,10 +1,10 @@ - package mage.cards.p; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.keyword.FlyingAbility; @@ -34,6 +34,7 @@ public final class PitTrap extends CardImpl { // {2}, {tap}, Sacrifice Pit Trap: Destroy target attacking creature without flying. It can't be regenerated. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(true), new GenericManaCost(2)); + ability.addCost(new TapSourceCost()); ability.addCost(new SacrificeSourceCost()); ability.addTarget(new TargetAttackingCreature(1, 1, filter, false)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/p/PlagueBelcher.java b/Mage.Sets/src/mage/cards/p/PlagueBelcher.java index 822d4429dfb..311546467f2 100644 --- a/Mage.Sets/src/mage/cards/p/PlagueBelcher.java +++ b/Mage.Sets/src/mage/cards/p/PlagueBelcher.java @@ -42,7 +42,7 @@ public final class PlagueBelcher extends CardImpl { this.toughness = new MageInt(4); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // When Plague Belcher enters the battlefield, put two -1/-1 counters on target creature you control. Ability ability = new EntersBattlefieldTriggeredAbility(new AddCountersTargetEffect(CounterType.M1M1.createInstance(2))); diff --git a/Mage.Sets/src/mage/cards/p/PlagueSpores.java b/Mage.Sets/src/mage/cards/p/PlagueSpores.java index d5914c6cf9e..bda4db4a9c0 100644 --- a/Mage.Sets/src/mage/cards/p/PlagueSpores.java +++ b/Mage.Sets/src/mage/cards/p/PlagueSpores.java @@ -1,16 +1,12 @@ - package mage.cards.p; import java.util.UUID; -import mage.ObjectColor; import mage.abilities.effects.Effect; import mage.abilities.effects.common.DestroyTargetEffect; 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 mage.target.common.TargetLandPermanent; @@ -21,12 +17,6 @@ import mage.target.common.TargetLandPermanent; */ public final class PlagueSpores extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public PlagueSpores(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{4}{B}{R}"); @@ -34,7 +24,7 @@ public final class PlagueSpores extends CardImpl { Effect effect = new DestroyTargetEffect(true, true); effect.setText("Destroy target nonblack creature and target land. They can't be regenerated."); this.getSpellAbility().addEffect(effect); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); this.getSpellAbility().addTarget(new TargetLandPermanent()); } diff --git a/Mage.Sets/src/mage/cards/p/PlanarIncision.java b/Mage.Sets/src/mage/cards/p/PlanarIncision.java new file mode 100644 index 00000000000..8dc6ceab064 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PlanarIncision.java @@ -0,0 +1,84 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.cards.p; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.counters.Counters; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.util.CardUtil; + +/** + * @author jeffwadsworth + */ +public final class PlanarIncision extends CardImpl { + + public PlanarIncision(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}"); + + // Exile target artifact or creature, then return it to the battlefield under its owner’s control with a +1/+1 counter on it. + this.getSpellAbility().addEffect(new PlanarIncisionEffect()); + this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_CREATURE)); + } + + private PlanarIncision(final PlanarIncision card) { + super(card); + } + + @Override + public PlanarIncision copy() { + return new PlanarIncision(this); + } +} + +class PlanarIncisionEffect extends OneShotEffect { + + PlanarIncisionEffect() { + super(Outcome.Benefit); + staticText = "Exile target artifact or creature, then return it to the battlefield under its owner's control with a +1/+1 counter on it"; + } + + private PlanarIncisionEffect(final PlanarIncisionEffect effect) { + super(effect); + } + + @Override + public PlanarIncisionEffect copy() { + return new PlanarIncisionEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + Player controller = game.getPlayer(source.getControllerId()); + if (permanent != null + && controller != null) { + UUID exileId = CardUtil.getExileZoneId("planarIncisionExile" + source.toString(), game); + if (controller.moveCardsToExile(permanent, source, game, true, exileId, "")) { + Card exiledCard = game.getExile().getExileZone(exileId).get(permanent.getId(), game); + if (exiledCard != null) { + Counters countersToAdd = new Counters(); + countersToAdd.addCounter(CounterType.P1P1.createInstance()); + game.setEnterWithCounters(exiledCard.getId(), countersToAdd); + return controller.moveCards(exiledCard, Zone.BATTLEFIELD, source, game, false, false, true, null); + } + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/p/PlatedSliver.java b/Mage.Sets/src/mage/cards/p/PlatedSliver.java index 9e888a8f2a3..6511c894db6 100644 --- a/Mage.Sets/src/mage/cards/p/PlatedSliver.java +++ b/Mage.Sets/src/mage/cards/p/PlatedSliver.java @@ -27,7 +27,7 @@ public final class PlatedSliver extends CardImpl { this.toughness = new MageInt(1); // All Sliver creatures get +0/+1. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostAllEffect(0, 1, Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS, false))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostAllEffect(0, 1, Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_ALL_SLIVERS, false))); } private PlatedSliver(final PlatedSliver card) { diff --git a/Mage.Sets/src/mage/cards/p/PlaxcasterFrogling.java b/Mage.Sets/src/mage/cards/p/PlaxcasterFrogling.java index a9c28bf5168..0b7ab75fdc6 100644 --- a/Mage.Sets/src/mage/cards/p/PlaxcasterFrogling.java +++ b/Mage.Sets/src/mage/cards/p/PlaxcasterFrogling.java @@ -15,8 +15,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Zone; -import mage.counters.CounterType; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; /** @@ -24,11 +23,6 @@ import mage.target.common.TargetCreaturePermanent; * @author JotaPeRL */ public final class PlaxcasterFrogling extends CardImpl { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with a +1/+1 counter on it"); - static { - filter.add(CounterType.P1P1.getPredicate()); - } public PlaxcasterFrogling(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{G}{U}"); @@ -42,7 +36,7 @@ public final class PlaxcasterFrogling extends CardImpl { // {2}: Target creature with a +1/+1 counter on it gains shroud until end of turn. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainAbilityTargetEffect(ShroudAbility.getInstance(), Duration.EndOfTurn), new GenericManaCost(2)); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_P1P1)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/p/PlayWithFire.java b/Mage.Sets/src/mage/cards/p/PlayWithFire.java index 481c849c06f..aac03809c13 100644 --- a/Mage.Sets/src/mage/cards/p/PlayWithFire.java +++ b/Mage.Sets/src/mage/cards/p/PlayWithFire.java @@ -2,6 +2,7 @@ package mage.cards.p; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.keyword.ScryEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -40,7 +41,8 @@ class PlayWithFireEffect extends OneShotEffect { PlayWithFireEffect() { super(Outcome.Benefit); - staticText = "{this} deals 2 damage to any target. If a player is dealt damage this way, scry 1"; + staticText = "{this} deals 2 damage to any target. If a player is dealt damage this way, scry 1. " + + "(Look at the top card of your library. You may put that card on the bottom of your library.)"; } private PlayWithFireEffect(final PlayWithFireEffect effect) { diff --git a/Mage.Sets/src/mage/cards/p/PledgeOfLoyalty.java b/Mage.Sets/src/mage/cards/p/PledgeOfLoyalty.java index c0bfd6bd50b..1fd372e25b6 100644 --- a/Mage.Sets/src/mage/cards/p/PledgeOfLoyalty.java +++ b/Mage.Sets/src/mage/cards/p/PledgeOfLoyalty.java @@ -1,56 +1,54 @@ package mage.cards.p; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - import mage.MageObject; import mage.ObjectColor; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.Effect; -import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; -import mage.abilities.keyword.ProtectionAbility; -import mage.constants.*; -import mage.filter.Filter; -import mage.filter.FilterCard; -import mage.filter.FilterObject; -import mage.filter.predicate.Predicate; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ColorPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.target.common.TargetCreaturePermanent; -import mage.abilities.Ability; import mage.abilities.effects.common.AttachEffect; -import mage.target.TargetPermanent; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; import mage.abilities.keyword.EnchantAbility; +import mage.abilities.keyword.ProtectionAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.game.Game; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; /** - * * @author noahg */ public final class PledgeOfLoyalty extends CardImpl { + private static final FilterCard filter = new FilterCard("the colors of permanents you control"); + + static { + filter.add(PledgeOfLoyaltyPredicate.instance); + } + public PledgeOfLoyalty(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); - + this.subtype.add(SubType.AURA); // Enchant creature TargetPermanent auraTarget = new TargetCreaturePermanent(); this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); - Ability ability = new EnchantAbility(auraTarget.getTargetName()); - this.addAbility(ability); + this.addAbility(new EnchantAbility(auraTarget.getTargetName())); // Enchanted creature has protection from the colors of permanents you control. This effect doesn't remove Pledge of Loyalty. - ProtectionAbility gainedAbility = new PledgeOfLoyaltyProtectionAbility(); - gainedAbility.setAuraIdNotToBeRemoved(this.getId()); - Effect effect = new GainAbilityAttachedEffect(gainedAbility, AttachmentType.AURA); - effect.setText("Enchanted creature has protection from the colors of permanents you control. This effect doesn't remove {this}."); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); + this.addAbility(new SimpleStaticAbility(new GainAbilityAttachedEffect( + new ProtectionAbility(filter), AttachmentType.AURA + ).setDoesntRemoveItself(true))); } private PledgeOfLoyalty(final PledgeOfLoyalty card) { @@ -61,73 +59,21 @@ public final class PledgeOfLoyalty extends CardImpl { public PledgeOfLoyalty copy() { return new PledgeOfLoyalty(this); } +} - class PledgeOfLoyaltyProtectionAbility extends ProtectionAbility { +enum PledgeOfLoyaltyPredicate implements ObjectSourcePlayerPredicate { + instance; - public PledgeOfLoyaltyProtectionAbility() { - super(new FilterCard()); - } - - public PledgeOfLoyaltyProtectionAbility(final PledgeOfLoyaltyProtectionAbility ability) { - super(ability); - } - - @Override - public PledgeOfLoyaltyProtectionAbility copy() { - return new PledgeOfLoyaltyProtectionAbility(this); - } - - - @Override - public boolean canTarget(MageObject source, Game game) { - ObjectColor color = new ObjectColor(); - for (Permanent permanent: game.getBattlefield().getAllActivePermanents(controllerId)) { - ObjectColor permanentColor = permanent.getColor(game); - if (permanentColor.isColorless()) { - continue; - } - if (permanentColor.isBlack()) { - color.setBlack(true); - } - if (permanentColor.isBlue()) { - color.setBlue(true); - } - if (permanentColor.isGreen()) { - color.setGreen(true); - } - if (permanentColor.isRed()) { - color.setRed(true); - } - if (permanentColor.isWhite()) { - color.setWhite(true); - } - } - - List> colorPredicates = new ArrayList<>(); - if (color.isBlack()) { - colorPredicates.add(new ColorPredicate(ObjectColor.BLACK)); - } - if (color.isBlue()) { - colorPredicates.add(new ColorPredicate(ObjectColor.BLUE)); - } - if (color.isGreen()) { - colorPredicates.add(new ColorPredicate(ObjectColor.GREEN)); - } - if (color.isRed()) { - colorPredicates.add(new ColorPredicate(ObjectColor.RED)); - } - if (color.isWhite()) { - colorPredicates.add(new ColorPredicate(ObjectColor.WHITE)); - } - Filter protectionFilter = new FilterObject("the colors of permanents you control"); - protectionFilter.add(Predicates.or(colorPredicates)); - this.filter = protectionFilter; - return super.canTarget(source, game); - } - - @Override - public String getRule() { - return "{this} has protection from the colors of permanents you control."; - } + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + ObjectColor color = input.getObject().getColor(game); + return color.hasColor() + && game + .getBattlefield() + .getActivePermanents( + StaticFilters.FILTER_CONTROLLED_PERMANENT, + input.getPlayerId(), input.getSourceId(), game + ).stream() + .anyMatch(permanent -> permanent.getColor(game).shares(color)); } } diff --git a/Mage.Sets/src/mage/cards/p/PointedDiscussion.java b/Mage.Sets/src/mage/cards/p/PointedDiscussion.java new file mode 100644 index 00000000000..244c6f72635 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PointedDiscussion.java @@ -0,0 +1,35 @@ +package mage.cards.p; + +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.LoseLifeSourceControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.game.permanent.token.BloodToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PointedDiscussion extends CardImpl { + + public PointedDiscussion(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}"); + + // You draw two cards, lose 2 life, then create a Blood token. + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(2).setText("you draw two cards")); + this.getSpellAbility().addEffect(new LoseLifeSourceControllerEffect(2).setText(", lose 2 life")); + this.getSpellAbility().addEffect(new CreateTokenEffect(new BloodToken()).concatBy(", then")); + } + + private PointedDiscussion(final PointedDiscussion card) { + super(card); + } + + @Override + public PointedDiscussion copy() { + return new PointedDiscussion(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PoisonArrow.java b/Mage.Sets/src/mage/cards/p/PoisonArrow.java index d05ac2bd613..ce1bad3cf7f 100644 --- a/Mage.Sets/src/mage/cards/p/PoisonArrow.java +++ b/Mage.Sets/src/mage/cards/p/PoisonArrow.java @@ -1,16 +1,12 @@ - package mage.cards.p; import java.util.UUID; -import mage.ObjectColor; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.GainLifeEffect; 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; /** @@ -19,19 +15,13 @@ import mage.target.common.TargetCreaturePermanent; */ public final class PoisonArrow extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public PoisonArrow(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{4}{B}{B}"); // Destroy target nonblack creature. You gain 3 life. this.getSpellAbility().addEffect(new DestroyTargetEffect()); this.getSpellAbility().addEffect(new GainLifeEffect(3)); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); } private PoisonArrow(final PoisonArrow card) { diff --git a/Mage.Sets/src/mage/cards/p/PolukranosUnchained.java b/Mage.Sets/src/mage/cards/p/PolukranosUnchained.java index 5831803e518..01457a08f40 100644 --- a/Mage.Sets/src/mage/cards/p/PolukranosUnchained.java +++ b/Mage.Sets/src/mage/cards/p/PolukranosUnchained.java @@ -32,7 +32,7 @@ import java.util.UUID; */ public final class PolukranosUnchained extends CardImpl { - private static final FilterPermanent filter = new FilterCreaturePermanent(); + private static final FilterPermanent filter = new FilterCreaturePermanent("another target creature"); static { filter.add(AnotherPredicate.instance); diff --git a/Mage.Sets/src/mage/cards/p/PoppetFactory.java b/Mage.Sets/src/mage/cards/p/PoppetFactory.java index b4a7985bb24..0dd45685704 100644 --- a/Mage.Sets/src/mage/cards/p/PoppetFactory.java +++ b/Mage.Sets/src/mage/cards/p/PoppetFactory.java @@ -26,14 +26,13 @@ public final class PoppetFactory extends CardImpl { this.color.setBlue(true); this.nightCard = true; - this.transformable = true; // Creature tokens you control lose all abilities and have base power and toughness 3/3. this.addAbility(new SimpleStaticAbility(new PoppetFactoryEffect())); // At the beginning of your upkeep, you may transform Poppet Factory. this.addAbility(new BeginningOfUpkeepTriggeredAbility( - new TransformSourceEffect(false), TargetController.YOU, true + new TransformSourceEffect(), TargetController.YOU, true )); } diff --git a/Mage.Sets/src/mage/cards/p/PoppetStitcher.java b/Mage.Sets/src/mage/cards/p/PoppetStitcher.java index 7cd11c4d79d..07e4dab0ae6 100644 --- a/Mage.Sets/src/mage/cards/p/PoppetStitcher.java +++ b/Mage.Sets/src/mage/cards/p/PoppetStitcher.java @@ -43,7 +43,6 @@ public final class PoppetStitcher extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(3); - this.transformable = true; this.secondSideCardClazz = mage.cards.p.PoppetFactory.class; // Whenever you cast an instant or sorcery spell, create a 2/2 black Zombie creature token with decayed. @@ -56,7 +55,7 @@ public final class PoppetStitcher extends CardImpl { this.addAbility(new TransformAbility()); this.addAbility(new ConditionalInterveningIfTriggeredAbility( new BeginningOfUpkeepTriggeredAbility( - new TransformSourceEffect(true), + new TransformSourceEffect(), TargetController.YOU, true ), condition, "At the beginning of your upkeep, " + "if you control three or more creature tokens, you may transform {this}." diff --git a/Mage.Sets/src/mage/cards/p/Porcuparrot.java b/Mage.Sets/src/mage/cards/p/Porcuparrot.java index 1f8bf3b988c..a6a5fed909b 100644 --- a/Mage.Sets/src/mage/cards/p/Porcuparrot.java +++ b/Mage.Sets/src/mage/cards/p/Porcuparrot.java @@ -32,9 +32,12 @@ public final class Porcuparrot extends CardImpl { this.addAbility(new MutateAbility(this, "{2}{R}")); // {T}: This creature deals X damage to any target, where X is the number of times this creature has mutated. - Ability ability = new SimpleActivatedAbility(new DamageTargetEffect( - SourceMutatedCount.instance, "this creature" - ), new TapSourceCost()); + Ability ability = new SimpleActivatedAbility( + new DamageTargetEffect(SourceMutatedCount.instance) + .setText("this creature deals X damage to any target, " + + "where X is the number of times this creature has mutated"), + new TapSourceCost() + ); ability.addTarget(new TargetAnyTarget()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/p/PortInspector.java b/Mage.Sets/src/mage/cards/p/PortInspector.java index e72afaea0c9..815e68de1a9 100644 --- a/Mage.Sets/src/mage/cards/p/PortInspector.java +++ b/Mage.Sets/src/mage/cards/p/PortInspector.java @@ -2,18 +2,12 @@ package mage.cards.p; import java.util.UUID; import mage.MageInt; -import mage.MageObject; -import mage.abilities.Ability; import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; -import mage.abilities.effects.OneShotEffect; -import mage.constants.SubType; +import mage.abilities.effects.common.LookAtTargetPlayerHandEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; -import mage.players.Player; +import mage.constants.SubType; /** * @@ -30,7 +24,8 @@ public final class PortInspector extends CardImpl { // Whenever Port Inspector becomes blocked, you may look at defending player's hand. this.addAbility(new BecomesBlockedSourceTriggeredAbility( - Zone.BATTLEFIELD, new LookAtDefendingPlayersHandEffect(), true, true)); + new LookAtTargetPlayerHandEffect().setText("look at defending player's hand"), + true, true)); } private PortInspector(final PortInspector card) { @@ -42,36 +37,3 @@ public final class PortInspector extends CardImpl { return new PortInspector(this); } } - -class LookAtDefendingPlayersHandEffect extends OneShotEffect { - - public LookAtDefendingPlayersHandEffect() { - super(Outcome.Benefit); - this.staticText = "look at defending player's hand"; - } - - public LookAtDefendingPlayersHandEffect(final LookAtDefendingPlayersHandEffect effect) { - super(effect); - } - - @Override - public LookAtDefendingPlayersHandEffect copy() { - return new LookAtDefendingPlayersHandEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - Player defendingPlayer = game.getPlayer(targetPointer.getFirst(game, source)); - MageObject sourceObject = game.getObject(source.getSourceId()); - if (controller != null - && defendingPlayer != null) { - controller.lookAtCards(sourceObject != null - ? sourceObject.getIdName() : null, - defendingPlayer.getHand(), game); - return true; - } - return false; - } - -} diff --git a/Mage.Sets/src/mage/cards/p/PortalMage.java b/Mage.Sets/src/mage/cards/p/PortalMage.java index 1dc20e2c301..2ba54f086a7 100644 --- a/Mage.Sets/src/mage/cards/p/PortalMage.java +++ b/Mage.Sets/src/mage/cards/p/PortalMage.java @@ -109,7 +109,6 @@ class PortalMageEffect extends OneShotEffect { } // Select the new defender TargetDefender target = new TargetDefender(defenders, null); - target.setNotTarget(true); // player or planswalker hexproof does not prevent attacking a player if (controller.chooseTarget(Outcome.Damage, target, source, game)) { if (!combatGroupTarget.getDefenderId().equals(target.getFirstTarget())) { if (combatGroupTarget.changeDefenderPostDeclaration(target.getFirstTarget(), game)) { diff --git a/Mage.Sets/src/mage/cards/p/Portcullis.java b/Mage.Sets/src/mage/cards/p/Portcullis.java index d57e9555e5e..22519f5e767 100644 --- a/Mage.Sets/src/mage/cards/p/Portcullis.java +++ b/Mage.Sets/src/mage/cards/p/Portcullis.java @@ -13,10 +13,10 @@ import mage.abilities.effects.common.ReturnToBattlefieldUnderOwnerControlTargetE import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; import mage.players.Player; @@ -30,8 +30,6 @@ import java.util.UUID; */ public final class Portcullis extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature"); - public Portcullis(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); @@ -40,7 +38,7 @@ public final class Portcullis extends CardImpl { String rule = "Whenever a creature enters the battlefield, if there are two or more other creatures on the battlefield, exile that creature."; String rule2 = " Return that card to the battlefield under its owner's control when {this} leaves the battlefield."; TriggeredAbility ability = new EntersBattlefieldAllTriggeredAbility(Zone.BATTLEFIELD, new PortcullisExileEffect(), - filter, false, SetTargetPointer.PERMANENT, rule); + StaticFilters.FILTER_PERMANENT_A_CREATURE, false, SetTargetPointer.PERMANENT, rule); MoreThanXCreaturesOnBFCondition condition = new MoreThanXCreaturesOnBFCondition(2); this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, condition, rule + rule2)); diff --git a/Mage.Sets/src/mage/cards/p/PortraitOfMichiko.java b/Mage.Sets/src/mage/cards/p/PortraitOfMichiko.java new file mode 100644 index 00000000000..305f77ab110 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PortraitOfMichiko.java @@ -0,0 +1,49 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PortraitOfMichiko extends CardImpl { + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount( + StaticFilters.FILTER_PERMANENT_CONTROLLED_ARTIFACT_OR_ENCHANTMENT + ); + + public PortraitOfMichiko(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, ""); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.NOBLE); + this.power = new MageInt(0); + this.toughness = new MageInt(0); + this.color.setWhite(true); + this.nightCard = true; + + // Portrait of Michiko gets +1/+1 for each artifact and/or enchantment you control. + this.addAbility(new SimpleStaticAbility(new BoostSourceEffect(xValue, xValue, Duration.WhileOnBattlefield) + .setText("{this} gets +1/+1 for each artifact and/or enchantment you control"))); + } + + private PortraitOfMichiko(final PortraitOfMichiko card) { + super(card); + } + + @Override + public PortraitOfMichiko copy() { + return new PortraitOfMichiko(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PoulticeSliver.java b/Mage.Sets/src/mage/cards/p/PoulticeSliver.java index 4f3e3ef12ea..b1a3629dffc 100644 --- a/Mage.Sets/src/mage/cards/p/PoulticeSliver.java +++ b/Mage.Sets/src/mage/cards/p/PoulticeSliver.java @@ -35,11 +35,11 @@ public final class PoulticeSliver extends CardImpl { // All Slivers have "{2}, {tap}: Regenerate target Sliver." Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateTargetEffect(), new GenericManaCost(2)); ability.addCost(new TapSourceCost()); - ability.addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS)); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_ALL_SLIVERS)); 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}, {T}: Regenerate target Sliver.\""))); } diff --git a/Mage.Sets/src/mage/cards/p/Preacher.java b/Mage.Sets/src/mage/cards/p/Preacher.java index 7894f9c48fb..9a17d4ccf09 100644 --- a/Mage.Sets/src/mage/cards/p/Preacher.java +++ b/Mage.Sets/src/mage/cards/p/Preacher.java @@ -75,12 +75,12 @@ class PreacherEffect extends OneShotEffect { Permanent targetPermanent = game.getPermanent(source.getFirstTarget()); Player controller = game.getPlayer(source.getControllerId()); if (controller != null && sourcePermanent != null && targetPermanent != null) { - SourceTappedCondition sourceTappedCondition = SourceTappedCondition.instance; + SourceTappedCondition sourceTappedCondition = SourceTappedCondition.TAPPED; ConditionalContinuousEffect effect = new ConditionalContinuousEffect( new GainControlTargetEffect(Duration.Custom), sourceTappedCondition, "Gain control of target creature of an opponent's choice that they control for as long as {this} remains tapped"); - effect.setTargetPointer(new FixedTarget(targetPermanent.getId())); + effect.setTargetPointer(new FixedTarget(targetPermanent.getId(), game)); game.addEffect(effect, source); return true; } diff --git a/Mage.Sets/src/mage/cards/p/PredatorsGambit.java b/Mage.Sets/src/mage/cards/p/PredatorsGambit.java index af9c3c6deaa..c7116ad88eb 100644 --- a/Mage.Sets/src/mage/cards/p/PredatorsGambit.java +++ b/Mage.Sets/src/mage/cards/p/PredatorsGambit.java @@ -1,4 +1,3 @@ - package mage.cards.p; import java.util.UUID; @@ -6,7 +5,6 @@ import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.CreatureCountCondition; import mage.abilities.decorator.ConditionalContinuousEffect; -import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.continuous.BoostEnchantedEffect; import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; @@ -23,7 +21,8 @@ import mage.target.common.TargetCreaturePermanent; */ public final class PredatorsGambit extends CardImpl { - private static final String rule = "Enchanted creature has intimidate as long as its controller controls no other creatures"; + private static final String rule = "Enchanted creature has intimidate as long as its controller controls no other creatures. " + + "(It can't be blocked except by artifact creatures and/or creatures that share a color with it.)"; public PredatorsGambit(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{B}"); @@ -38,12 +37,14 @@ public final class PredatorsGambit extends CardImpl { this.addAbility(ability); // Enchanted creature gets +2/+1. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(2, 1, Duration.WhileOnBattlefield))); + this.addAbility(new SimpleStaticAbility(new BoostEnchantedEffect(2, 1))); // Enchanted creature has intimidate as long as its controller controls no other creatures. - ContinuousEffect effect = new GainAbilityAttachedEffect(IntimidateAbility.getInstance(), AttachmentType.AURA); - ConditionalContinuousEffect intimidate = new ConditionalContinuousEffect(effect, new CreatureCountCondition(1, TargetController.YOU), rule); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, intimidate)); + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new GainAbilityAttachedEffect(IntimidateAbility.getInstance(), AttachmentType.AURA), + new CreatureCountCondition(1, TargetController.YOU), + rule + ))); } diff --git a/Mage.Sets/src/mage/cards/p/PredatorsHour.java b/Mage.Sets/src/mage/cards/p/PredatorsHour.java new file mode 100644 index 00000000000..2d1e4785a1f --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PredatorsHour.java @@ -0,0 +1,239 @@ +package mage.cards.p; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.AsThoughManaEffect; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.*; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.players.ManaPoolItem; +import mage.players.Player; +import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** + * @author Alex-Vasile + */ +public final class PredatorsHour extends CardImpl { + + public PredatorsHour(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{B}"); + + // Until end of turn, creatures you control gain menace + this.getSpellAbility().addEffect(new GainAbilityControlledEffect( + new MenaceAbility(false), + Duration.EndOfTurn, + StaticFilters.FILTER_CONTROLLED_CREATURES + ).setText("Until end of turn, creatures you control gain menace") + ); + + // and “Whenever this creature deals combat damage to a player, + // exile the top card of that player's library face down. + // You may look at and play that card for as long as it remains exiled, + // and you may spend mana as though it were mana of any color to cast that spell.” + this.getSpellAbility().addEffect(new GainAbilityControlledEffect( + new DealsCombatDamageToAPlayerTriggeredAbility( + new PredatorsHourEffect(), + false, + true), + Duration.EndOfTurn, + StaticFilters.FILTER_CONTROLLED_CREATURES + ).setText("\"Whenever this creature deals combat damage to a player, " + + "exile the top card of that player's library face down. " + + "You may look at and play that card for as long as it remains exiled, " + + "and you may spend mana as though it were mana of any color to cast that spell.\"") + .concatBy("and") + ); + } + + private PredatorsHour(final PredatorsHour card) { super(card); } + + @Override + public PredatorsHour copy() { return new PredatorsHour(this); } +} + +// Based on Gonti, Lord of Luxury +class PredatorsHourEffect extends OneShotEffect { + + private static final String VALUE_PREFIX = "ExileZones"; + + public PredatorsHourEffect() { + super(Outcome.Benefit); + this.staticText = "exile the top card of that player's library face down. " + + "You may look at and play that card for as long as it remains exiled, " + + "and you may spend mana as though it were mana of any color to cast that spell."; + } + + private PredatorsHourEffect(final PredatorsHourEffect effect) { super(effect); } + + @Override + public PredatorsHourEffect copy() { return new PredatorsHourEffect(this); } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { return false; } + + Player opponent = game.getPlayer(targetPointer.getFirst(game, source)); + if (opponent == null) { return false; } + + MageObject sourceObject = source.getSourceObject(game); + if (sourceObject == null) { return false; } + + Card topCard = opponent.getLibrary().getFromTop(game); + if (topCard == null) { return false; } + + UUID exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + topCard.setFaceDown(true, game); + + // Move card to exile + if (controller.moveCardsToExile(topCard, source, game, false, exileZoneId, sourceObject.getIdName())) { + topCard.setFaceDown(true, game); + + Set exileZones = (Set) game.getState().getValue(VALUE_PREFIX + source.getSourceId().toString()); + if (exileZones == null) { + exileZones = new HashSet<>(); + game.getState().setValue(VALUE_PREFIX + source.getSourceId().toString(), exileZones); + } + exileZones.add(exileZoneId); + + // You may play the card + ContinuousEffect effect = new PredatorsHourPlayFromExileEffect(); + effect.setTargetPointer(new FixedTarget(topCard.getId(), game)); + game.addEffect(effect, source); + + // And you may spend mana as though it were mana of any color to cast it + effect = new PredatorsHourSpendAnyManaEffect(); + effect.setTargetPointer(new FixedTarget(topCard.getId(), game)); + game.addEffect(effect, source); + + // For as long as that card remains exiled, you may look at it + effect = new PredatorsHourLookEffect(controller.getId()); + effect.setTargetPointer(new FixedTarget(topCard.getId(), game)); + game.addEffect(effect, source); + } + + return true; + } +} + +class PredatorsHourPlayFromExileEffect extends AsThoughEffectImpl { + + public PredatorsHourPlayFromExileEffect() { + super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.Custom, Outcome.Benefit); + staticText = "You may look at and play that card for as long as it remains exiled, " + + "and you may spend mana as though it were mana of any color to cast that spell."; + } + + private PredatorsHourPlayFromExileEffect(final PredatorsHourPlayFromExileEffect effect) { super(effect); } + + @Override + public boolean apply(Game game, Ability source) { return true; } + + @Override + public PredatorsHourPlayFromExileEffect copy() { return new PredatorsHourPlayFromExileEffect(this); } + + @Override + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + UUID targetId = getTargetPointer().getFirst(game, source); + if (targetId == null) { + // card is no longer in the origin zone, effect can be discarded + this.discard(); + return false; + } + Card theCard = game.getCard(objectId); + if (theCard == null ) { return false; } + + // for split cards + objectId = theCard.getMainCard().getId(); + + if (objectId.equals(targetId) && affectedControllerId.equals(source.getControllerId())) { + Card card = game.getCard(objectId); + return card != null; + } + return false; + } +} + +class PredatorsHourSpendAnyManaEffect extends AsThoughEffectImpl implements AsThoughManaEffect { + + public PredatorsHourSpendAnyManaEffect() { + super(AsThoughEffectType.SPEND_OTHER_MANA, Duration.Custom, Outcome.Benefit); + staticText = "you may spend mana as though it were mana of any color to cast that spell"; + } + + private PredatorsHourSpendAnyManaEffect(final PredatorsHourSpendAnyManaEffect effect) { super(effect); } + + @Override + public boolean apply(Game game, Ability source) { return true; } + + @Override + public PredatorsHourSpendAnyManaEffect copy() { return new PredatorsHourSpendAnyManaEffect(this); } + + @Override + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + Card theCard = game.getCard(objectId); + if (theCard == null) { return false; } + + // for split cards + objectId = theCard.getMainCard().getId(); + + if (objectId.equals(((FixedTarget) getTargetPointer()).getTarget()) + && game.getState().getZoneChangeCounter(objectId) <= ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1) { + // if the card moved from exile to spell the zone change counter is increased by 1 (effect must applies before and on stack, use isCheckPlayableMode?) + return source.isControlledBy(affectedControllerId); + } else if (((FixedTarget) getTargetPointer()).getTarget().equals(objectId)) { + // object has moved zone so effect can be discarded + this.discard(); + } + return false; + } + + @Override + public ManaType getAsThoughManaType(ManaType manaType, ManaPoolItem mana, UUID affectedControllerId, Ability source, Game game) { + return mana.getFirstAvailable(); + } +} + +class PredatorsHourLookEffect extends AsThoughEffectImpl { + + private final UUID authorizedPlayerId; + + public PredatorsHourLookEffect(UUID authorizedPlayerId) { + super(AsThoughEffectType.LOOK_AT_FACE_DOWN, Duration.EndOfGame, Outcome.Benefit); + this.authorizedPlayerId = authorizedPlayerId; + staticText = "You may look at the cards exiled with {this}"; + } + + private PredatorsHourLookEffect(final PredatorsHourLookEffect effect) { + super(effect); + this.authorizedPlayerId = effect.authorizedPlayerId; + } + + @Override + public boolean apply(Game game, Ability source) { return true; } + + @Override + public PredatorsHourLookEffect copy() { return new PredatorsHourLookEffect(this); } + + @Override + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + UUID cardId = getTargetPointer().getFirst(game, source); + + // card is no longer in the origin zone, effect can be discarded + if (cardId == null) { this.discard(); } + + return affectedControllerId.equals(authorizedPlayerId) && objectId.equals(cardId); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PredatoryImpetus.java b/Mage.Sets/src/mage/cards/p/PredatoryImpetus.java index 76dcd0113f3..101fa288773 100644 --- a/Mage.Sets/src/mage/cards/p/PredatoryImpetus.java +++ b/Mage.Sets/src/mage/cards/p/PredatoryImpetus.java @@ -1,9 +1,10 @@ package mage.cards.p; import mage.abilities.Ability; -import mage.abilities.common.GoadAttachedAbility; +import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.RequirementEffect; import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.combat.GoadAttachedEffect; import mage.abilities.effects.common.continuous.BoostEnchantedEffect; import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; @@ -37,11 +38,10 @@ public final class PredatoryImpetus extends CardImpl { this.addAbility(ability); // Enchanted creature gets +3/+3, must be blocked if able, and is goaded. - this.addAbility(new GoadAttachedAbility( - new BoostEnchantedEffect(3, 3) - .setText("Enchanted creature gets +3/+3"), - new PredatoryImpetusEffect() - )); + ability = new SimpleStaticAbility(new BoostEnchantedEffect(3, 3)); + ability.addEffect(new PredatoryImpetusEffect()); + ability.addEffect(new GoadAttachedEffect().concatBy(",")); + this.addAbility(ability); } private PredatoryImpetus(final PredatoryImpetus card) { diff --git a/Mage.Sets/src/mage/cards/p/PredatorySliver.java b/Mage.Sets/src/mage/cards/p/PredatorySliver.java index 43a8747cfba..d642c6e97ff 100644 --- a/Mage.Sets/src/mage/cards/p/PredatorySliver.java +++ b/Mage.Sets/src/mage/cards/p/PredatorySliver.java @@ -28,7 +28,7 @@ public final class PredatorySliver extends CardImpl { // Sliver creatures you control get +1/+1. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, - new BoostControlledEffect(1, 1, Duration.WhileInGraveyard, StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS))); + new BoostControlledEffect(1, 1, Duration.WhileInGraveyard, StaticFilters.FILTER_PERMANENT_SLIVERS))); } private PredatorySliver(final PredatorySliver card) { diff --git a/Mage.Sets/src/mage/cards/p/PrematureBurial.java b/Mage.Sets/src/mage/cards/p/PrematureBurial.java index 2539369d82b..cdf917f8900 100644 --- a/Mage.Sets/src/mage/cards/p/PrematureBurial.java +++ b/Mage.Sets/src/mage/cards/p/PrematureBurial.java @@ -2,16 +2,14 @@ package mage.cards.p; import mage.MageObject; import mage.MageObjectReference; -import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.WatcherScope; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ColorPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -25,18 +23,12 @@ import java.util.*; */ public final class PrematureBurial extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature that entered the battlefield since your last turn ended"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public PrematureBurial(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}"); // Destroy target nonblack creature that entered the battlefield since your last turn ended. - this.getSpellAbility().addEffect(new DestroyTargetEffect().setText("Destroy target nonblack creature that entered the battlefield since your last turn ended.")); - this.getSpellAbility().addTarget(new ETBSinceYourLastTurnTarget(filter)); + this.getSpellAbility().addEffect(new DestroyTargetEffect()); + this.getSpellAbility().addTarget(new ETBSinceYourLastTurnTarget(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); this.getSpellAbility().addWatcher(new ETBSinceYourLastTurnWatcher()); } @@ -123,4 +115,4 @@ class ETBSinceYourLastTurnWatcher extends Watcher { public boolean enteredSinceLastTurn(UUID player, MageObjectReference mor) { return playerToETBMap.get(player).contains(mor); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/p/PrepareFight.java b/Mage.Sets/src/mage/cards/p/PrepareFight.java index 2901114f772..b7622777ca9 100644 --- a/Mage.Sets/src/mage/cards/p/PrepareFight.java +++ b/Mage.Sets/src/mage/cards/p/PrepareFight.java @@ -12,9 +12,9 @@ import mage.cards.SplitCard; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SpellAbilityType; -import mage.filter.StaticFilters; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; +import mage.target.common.TargetOpponentsCreaturePermanent; import java.util.UUID; @@ -43,9 +43,9 @@ public final class PrepareFight extends SplitCard { // Fight // Target creature you control fights target creature you don't control. getRightHalfCard().addAbility(new AftermathAbility().setRuleAtTheTop(true)); - getRightHalfCard().getSpellAbility().addEffect(new FightTargetsEffect()); + getRightHalfCard().getSpellAbility().addEffect(new FightTargetsEffect(false)); getRightHalfCard().getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); - getRightHalfCard().getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); + getRightHalfCard().getSpellAbility().addTarget(new TargetOpponentsCreaturePermanent()); } private PrepareFight(final PrepareFight card) { diff --git a/Mage.Sets/src/mage/cards/p/PressurePoint.java b/Mage.Sets/src/mage/cards/p/PressurePoint.java index 3f68eb73972..d4e4b946749 100644 --- a/Mage.Sets/src/mage/cards/p/PressurePoint.java +++ b/Mage.Sets/src/mage/cards/p/PressurePoint.java @@ -23,7 +23,7 @@ public final class PressurePoint extends CardImpl { this.getSpellAbility().addTarget(new TargetCreaturePermanent()); // Draw a card. - this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("
")); } private PressurePoint(final PressurePoint card) { diff --git a/Mage.Sets/src/mage/cards/p/PriceOfFame.java b/Mage.Sets/src/mage/cards/p/PriceOfFame.java index 0cb68827b8e..a05d3bfaebe 100644 --- a/Mage.Sets/src/mage/cards/p/PriceOfFame.java +++ b/Mage.Sets/src/mage/cards/p/PriceOfFame.java @@ -44,7 +44,7 @@ public final class PriceOfFame extends CardImpl { this.getSpellAbility().addTarget(new TargetCreaturePermanent()); // Surveil 2. - this.getSpellAbility().addEffect(new SurveilEffect(2)); + this.getSpellAbility().addEffect(new SurveilEffect(2).concatBy("
")); } private PriceOfFame(final PriceOfFame card) { diff --git a/Mage.Sets/src/mage/cards/p/Pridemalkin.java b/Mage.Sets/src/mage/cards/p/Pridemalkin.java index f5c2ee961c8..98b9fbd9838 100644 --- a/Mage.Sets/src/mage/cards/p/Pridemalkin.java +++ b/Mage.Sets/src/mage/cards/p/Pridemalkin.java @@ -13,8 +13,7 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; import mage.counters.CounterType; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetControlledCreaturePermanent; import java.util.UUID; @@ -24,13 +23,6 @@ import java.util.UUID; */ public final class Pridemalkin extends CardImpl { - private static final FilterPermanent filter - = new FilterControlledCreaturePermanent("each creature you control with a +1/+1 counter on it"); - - static { - filter.add(CounterType.P1P1.getPredicate()); - } - public Pridemalkin(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); @@ -45,7 +37,7 @@ public final class Pridemalkin extends CardImpl { // Each creature you control with a +1/+1 counter on it has trample. this.addAbility(new SimpleStaticAbility(new GainAbilityAllEffect( - TrampleAbility.getInstance(), Duration.WhileOnBattlefield, filter + TrampleAbility.getInstance(), Duration.WhileOnBattlefield, StaticFilters.FILTER_EACH_CONTROLLED_CREATURE_P1P1 ))); } diff --git a/Mage.Sets/src/mage/cards/p/PriestOfTheBlessedGraf.java b/Mage.Sets/src/mage/cards/p/PriestOfTheBlessedGraf.java new file mode 100644 index 00000000000..c7f0ff1ae68 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PriestOfTheBlessedGraf.java @@ -0,0 +1,115 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.hint.Hint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.StaticFilters; +import mage.game.Controllable; +import mage.game.Game; +import mage.game.permanent.token.SpiritWhiteToken; +import mage.players.Player; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.UUID; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class PriestOfTheBlessedGraf extends CardImpl { + + public PriestOfTheBlessedGraf(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(1); + this.toughness = new MageInt(2); + + // At the beginning of your end step, create X 1/1 white Spirit creature tokens with flying, where X is the number of opponents who control more lands than you. + this.addAbility(new BeginningOfEndStepTriggeredAbility(new CreateTokenEffect( + new SpiritWhiteToken(), PriestOfTheBlessedGrafValue.instance + ), TargetController.YOU, false).addHint(PriestOfTheBlessedGrafHint.instance)); + } + + private PriestOfTheBlessedGraf(final PriestOfTheBlessedGraf card) { + super(card); + } + + @Override + public PriestOfTheBlessedGraf copy() { + return new PriestOfTheBlessedGraf(this); + } +} + +enum PriestOfTheBlessedGrafValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + return makeStream(game, sourceAbility).size(); + } + + @Override + public PriestOfTheBlessedGrafValue copy() { + return this; + } + + @Override + public String getMessage() { + return "the number of opponents who control more lands than you"; + } + + @Override + public String toString() { + return "X"; + } + + static List makeStream(Game game, Ability source) { + Map map = game + .getBattlefield() + .getActivePermanents( + StaticFilters.FILTER_LAND, + source.getControllerId(), + source.getSourceId(), game + ).stream() + .map(Controllable::getControllerId) + .collect(Collectors.toMap(Function.identity(), u -> 1, Integer::sum)); + int myLands = map.getOrDefault(source.getControllerId(), 0); + return game.getOpponents(source.getControllerId()) + .stream() + .filter(playerId -> map.getOrDefault(playerId, 0) > myLands) + .map(game::getPlayer) + .filter(Objects::nonNull) + .map(Player::getName) + .collect(Collectors.toList()); + } +} + +enum PriestOfTheBlessedGrafHint implements Hint { + instance; + + @Override + public String getText(Game game, Ability ability) { + List names = PriestOfTheBlessedGrafValue.makeStream(game, ability); + return "Players who control more lands than you: " + names.size() + + (names.size() > 0 ? " (" + String.join(", ", names) + ')' : ""); + } + + @Override + public PriestOfTheBlessedGrafHint copy() { + return this; + } +} diff --git a/Mage.Sets/src/mage/cards/p/PrimalAmulet.java b/Mage.Sets/src/mage/cards/p/PrimalAmulet.java index d8e17789c65..b15a482046b 100644 --- a/Mage.Sets/src/mage/cards/p/PrimalAmulet.java +++ b/Mage.Sets/src/mage/cards/p/PrimalAmulet.java @@ -39,7 +39,6 @@ public final class PrimalAmulet extends CardImpl { public PrimalAmulet(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); - this.transformable = true; this.secondSideCardClazz = PrimalWellspring.class; // Instant and sorcery spells you cast cost {1} less to cast. @@ -87,7 +86,7 @@ class PrimalAmuletEffect extends OneShotEffect { int counters = permanent.getCounters(game).getCount(CounterType.CHARGE); if (counters > 3 && player.chooseUse(Outcome.Benefit, "Transform this?", source, game)) { permanent.removeCounters("charge", counters, source, game); - new TransformSourceEffect(true).apply(game, source); + new TransformSourceEffect().apply(game, source); } return true; } diff --git a/Mage.Sets/src/mage/cards/p/PrimevalBounty.java b/Mage.Sets/src/mage/cards/p/PrimevalBounty.java index 8cf0a13121d..ee18f97570e 100644 --- a/Mage.Sets/src/mage/cards/p/PrimevalBounty.java +++ b/Mage.Sets/src/mage/cards/p/PrimevalBounty.java @@ -46,7 +46,7 @@ public final class PrimevalBounty extends CardImpl { // Whenever a land enters the battlefield under your control, you gain 3 life. effect = new GainLifeEffect(3); - ability = new EntersBattlefieldControlledTriggeredAbility(effect, new FilterLandPermanent("a land")); + ability = new EntersBattlefieldControlledTriggeredAbility(effect, StaticFilters.FILTER_LAND_A); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/p/PrimordialMist.java b/Mage.Sets/src/mage/cards/p/PrimordialMist.java index ca77430dc39..aed0a8ee77e 100644 --- a/Mage.Sets/src/mage/cards/p/PrimordialMist.java +++ b/Mage.Sets/src/mage/cards/p/PrimordialMist.java @@ -7,7 +7,6 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.Cost; import mage.abilities.costs.CostImpl; import mage.abilities.effects.AsThoughEffectImpl; -import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.keyword.ManifestEffect; import mage.cards.Card; import mage.cards.CardImpl; @@ -98,12 +97,13 @@ class PrimordialMistCost extends CostImpl { if (sourcePermanent != null) { Permanent targetPermanent = game.getPermanent(target.getFirstTarget()); Card targetCard = game.getCard(target.getFirstTarget()); - if (targetPermanent != null && targetCard != null) { + if (targetPermanent != null + && targetCard != null) { String exileName = sourcePermanent.getIdName() + " "; controller.moveCardsToExile(targetPermanent, source, game, true, source.getSourceId(), exileName); targetPermanent.setFaceDown(false, game); - ContinuousEffect effect = new PrimordialMistCastFromExileEffect(); - effect.setTargetPointer(new FixedTarget(targetCard.getId(), targetCard.getZoneChangeCounter(game))); + PrimordialMistCastFromExileEffect effect = new PrimordialMistCastFromExileEffect(); + effect.setTargetPointer(new FixedTarget(targetCard.getId())); game.addEffect(effect, ability); this.setPaid(); } @@ -141,6 +141,7 @@ class PrimordialMistCastFromExileEffect extends AsThoughEffectImpl { @Override public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { return source.isControlledBy(affectedControllerId) - && (game.getCard(getTargetPointer().getFirst(game, source)) != null); + && (game.getCard(targetPointer.getFirst(game, source)) != null) + && objectId == targetPointer.getFirst(game, source); } } diff --git a/Mage.Sets/src/mage/cards/p/PrismariCampus.java b/Mage.Sets/src/mage/cards/p/PrismariCampus.java index a41977a39d9..e95c8c9ead6 100644 --- a/Mage.Sets/src/mage/cards/p/PrismariCampus.java +++ b/Mage.Sets/src/mage/cards/p/PrismariCampus.java @@ -30,7 +30,7 @@ public final class PrismariCampus extends CardImpl { this.addAbility(new RedManaAbility()); // {4}, {T}: Scry 1. - Ability ability = new SimpleActivatedAbility(new ScryEffect(1), new GenericManaCost(4)); + Ability ability = new SimpleActivatedAbility(new ScryEffect(1, false), new GenericManaCost(4)); ability.addCost(new TapSourceCost()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/p/PrisonRealm.java b/Mage.Sets/src/mage/cards/p/PrisonRealm.java index f4ba5c514a7..757d986d241 100644 --- a/Mage.Sets/src/mage/cards/p/PrisonRealm.java +++ b/Mage.Sets/src/mage/cards/p/PrisonRealm.java @@ -40,7 +40,7 @@ public final class PrisonRealm extends CardImpl { this.addAbility(ability); // When Prison Realm enters the battlefield, scry 1. - this.addAbility(new EntersBattlefieldTriggeredAbility(new ScryEffect(1))); + this.addAbility(new EntersBattlefieldTriggeredAbility(new ScryEffect(1, false))); } private PrisonRealm(final PrisonRealm card) { diff --git a/Mage.Sets/src/mage/cards/p/PristineAngel.java b/Mage.Sets/src/mage/cards/p/PristineAngel.java index b67c696a836..51ac8ae3a7f 100644 --- a/Mage.Sets/src/mage/cards/p/PristineAngel.java +++ b/Mage.Sets/src/mage/cards/p/PristineAngel.java @@ -1,4 +1,3 @@ - package mage.cards.p; import java.util.UUID; @@ -55,7 +54,7 @@ public final class PristineAngel extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect( new GainAbilitySourceEffect(new ProtectionAbility(filter), Duration.WhileOnBattlefield), - new InvertCondition(SourceTappedCondition.instance), + SourceTappedCondition.UNTAPPED, "As long as {this} is untapped, it has protection from artifacts and from all colors"))); // Whenever you cast a spell, you may untap Pristine Angel. this.addAbility(new SpellCastControllerTriggeredAbility(new UntapSourceEffect(), true)); diff --git a/Mage.Sets/src/mage/cards/p/Probe.java b/Mage.Sets/src/mage/cards/p/Probe.java index b7c63cd7288..a21c1aa04c3 100644 --- a/Mage.Sets/src/mage/cards/p/Probe.java +++ b/Mage.Sets/src/mage/cards/p/Probe.java @@ -32,7 +32,7 @@ public final class Probe extends CardImpl { this.getSpellAbility().addEffect(new ConditionalOneShotEffect( new DiscardTargetEffect(2), KickedCondition.instance, - "

If this spell was kicked, target player discards two cards")); + "If this spell was kicked, target player discards two cards")); this.getSpellAbility().setTargetAdjuster(ProbeAdjuster.instance); } diff --git a/Mage.Sets/src/mage/cards/p/ProdigysPrototype.java b/Mage.Sets/src/mage/cards/p/ProdigysPrototype.java new file mode 100644 index 00000000000..12ab190664d --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/ProdigysPrototype.java @@ -0,0 +1,47 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.common.AttacksWithCreaturesTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.CrewAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.permanent.token.PilotToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ProdigysPrototype extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(SubType.VEHICLE, ""); + + public ProdigysPrototype(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}{W}{U}"); + + this.subtype.add(SubType.VEHICLE); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // Whenever one or more Vehicles you control attack, create a 1/1 colorless Pilot creature token with "This creature crews Vehicles as though its power were 2 greater." + this.addAbility(new AttacksWithCreaturesTriggeredAbility( + new CreateTokenEffect(new PilotToken()), 1, filter + ).setTriggerPhrase("Whenever one or more Vehicles you control attack, ")); + + // Crew 2 + this.addAbility(new CrewAbility(2)); + } + + private ProdigysPrototype(final ProdigysPrototype card) { + super(card); + } + + @Override + public ProdigysPrototype copy() { + return new ProdigysPrototype(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/ProfaneProcession.java b/Mage.Sets/src/mage/cards/p/ProfaneProcession.java index 3c0baffc3eb..cdf06c4a1aa 100644 --- a/Mage.Sets/src/mage/cards/p/ProfaneProcession.java +++ b/Mage.Sets/src/mage/cards/p/ProfaneProcession.java @@ -33,7 +33,6 @@ public final class ProfaneProcession extends CardImpl { this.addSuperType(SuperType.LEGENDARY); - this.transformable = true; this.secondSideCardClazz = mage.cards.t.TombOfTheDuskRose.class; // {3}{W}{B}: Exile target creature. Then if there are three or more cards exiled with Profane Procession, transform it. @@ -79,7 +78,7 @@ class ProfaneProcessionEffect extends OneShotEffect { game.getState().processAction(game); ExileZone exileZone = game.getExile().getExileZone(exileId); if (exileZone != null && exileZone.size() > 2) { - new TransformSourceEffect(true).apply(game, source); + new TransformSourceEffect().apply(game, source); } return true; } diff --git a/Mage.Sets/src/mage/cards/p/ProfessorOnyx.java b/Mage.Sets/src/mage/cards/p/ProfessorOnyx.java index 090d468a5ef..2ed5ca66be3 100644 --- a/Mage.Sets/src/mage/cards/p/ProfessorOnyx.java +++ b/Mage.Sets/src/mage/cards/p/ProfessorOnyx.java @@ -5,7 +5,6 @@ import mage.MageObject; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.common.MagecraftAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.*; @@ -45,7 +44,7 @@ public final class ProfessorOnyx extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.LILIANA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // Magecraft — Whenever you cast or copy an instant or sorcery spell, each opponent loses 2 life and you gain 2 life. Ability ability = new MagecraftAbility(new LoseLifeOpponentsEffect(2), false); diff --git a/Mage.Sets/src/mage/cards/p/PromiseOfBunrei.java b/Mage.Sets/src/mage/cards/p/PromiseOfBunrei.java index 917810c0021..fb3ae63fc8b 100644 --- a/Mage.Sets/src/mage/cards/p/PromiseOfBunrei.java +++ b/Mage.Sets/src/mage/cards/p/PromiseOfBunrei.java @@ -1,7 +1,5 @@ - package mage.cards.p; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.DiesCreatureTriggeredAbility; import mage.abilities.effects.OneShotEffect; @@ -10,30 +8,26 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.token.SpiritToken; import mage.players.Player; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class PromiseOfBunrei extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature you control"); - - static { - filter.add(TargetController.YOU.getControllerPredicate()); - } - public PromiseOfBunrei(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); // When a creature you control dies, sacrifice Promise of Bunrei. If you do, create four 1/1 colorless Spirit creature tokens. - this.addAbility(new DiesCreatureTriggeredAbility(new PromiseOfBunreiEffect(), false, filter)); + this.addAbility(new DiesCreatureTriggeredAbility( + new PromiseOfBunreiEffect(), false, StaticFilters.FILTER_CONTROLLED_A_CREATURE + ).setTriggerPhrase("When a creature you control dies, ")); } private PromiseOfBunrei(final PromiseOfBunrei card) { @@ -47,21 +41,21 @@ public final class PromiseOfBunrei extends CardImpl { } class PromiseOfBunreiEffect extends OneShotEffect { - + public PromiseOfBunreiEffect() { super(Outcome.Benefit); this.staticText = "sacrifice {this}. If you do, create four 1/1 colorless Spirit creature tokens"; } - + public PromiseOfBunreiEffect(final PromiseOfBunreiEffect effect) { super(effect); } - + @Override public PromiseOfBunreiEffect copy() { return new PromiseOfBunreiEffect(this); } - + @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); diff --git a/Mage.Sets/src/mage/cards/p/ProsperousThief.java b/Mage.Sets/src/mage/cards/p/ProsperousThief.java new file mode 100644 index 00000000000..075be9d3f52 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/ProsperousThief.java @@ -0,0 +1,102 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.NinjutsuAbility; +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.DamagedPlayerEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.TreasureToken; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ProsperousThief extends CardImpl { + + public ProsperousThief(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.NINJA); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // Ninjutsu {1}{U} + this.addAbility(new NinjutsuAbility("{1}{U}")); + + // Whenever one or more Ninja or Rogue creatures you control deal combat damage to a player, create a Treasure token. + this.addAbility(new ProsperousThiefTriggeredAbility()); + } + + private ProsperousThief(final ProsperousThief card) { + super(card); + } + + @Override + public ProsperousThief copy() { + return new ProsperousThief(this); + } +} + +class ProsperousThiefTriggeredAbility extends TriggeredAbilityImpl { + + private final List damagedPlayerIds = new ArrayList<>(); + + ProsperousThiefTriggeredAbility() { + super(Zone.BATTLEFIELD, new CreateTokenEffect(new TreasureToken()), false); + } + + private ProsperousThiefTriggeredAbility(final ProsperousThiefTriggeredAbility ability) { + super(ability); + } + + @Override + public ProsperousThiefTriggeredAbility copy() { + return new ProsperousThiefTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DAMAGED_PLAYER + || event.getType() == GameEvent.EventType.COMBAT_DAMAGE_STEP_POST; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.COMBAT_DAMAGE_STEP_POST) { + damagedPlayerIds.clear(); + return false; + } + if (event.getType() != GameEvent.EventType.DAMAGED_PLAYER + || !((DamagedPlayerEvent) event).isCombatDamage() + || damagedPlayerIds.contains(event.getTargetId())) { + return false; + } + Permanent creature = game.getPermanent(event.getSourceId()); + if (creature == null + || !isControlledBy(creature.getControllerId()) + || (!creature.hasSubtype(SubType.NINJA, game) + && !creature.hasSubtype(SubType.ROGUE, game))) { + return false; + } + damagedPlayerIds.add(event.getTargetId()); + return true; + } + + @Override + public String getRule() { + return "Whenever one or more Ninja or Rogue creatures you control " + + "deal combat damage to a player, create a Treasure token."; + } +} diff --git a/Mage.Sets/src/mage/cards/p/ProwlingGeistcatcher.java b/Mage.Sets/src/mage/cards/p/ProwlingGeistcatcher.java index 18dd75e861b..43359ed16d8 100644 --- a/Mage.Sets/src/mage/cards/p/ProwlingGeistcatcher.java +++ b/Mage.Sets/src/mage/cards/p/ProwlingGeistcatcher.java @@ -79,7 +79,7 @@ class ProwlingGeistcatcherExileEffect extends OneShotEffect { player.moveCardsToExile( card, source, game, true, CardUtil.getExileZoneId(game, source), - CardUtil.getSourceLogName(game, source) + CardUtil.getSourceName(game, source) ); } Permanent exiled = (Permanent) getValue("sacrificedPermanent"); diff --git a/Mage.Sets/src/mage/cards/p/Psychatog.java b/Mage.Sets/src/mage/cards/p/Psychatog.java index 61572d5d847..011fec63897 100644 --- a/Mage.Sets/src/mage/cards/p/Psychatog.java +++ b/Mage.Sets/src/mage/cards/p/Psychatog.java @@ -13,7 +13,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Zone; -import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.target.common.TargetCardInYourGraveyard; /** @@ -33,7 +33,7 @@ public final class Psychatog extends CardImpl { this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1,1,Duration.EndOfTurn), new DiscardCardCost())); // Exile two cards from your graveyard: Psychatog gets +1/+1 until end of turn. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1,1,Duration.EndOfTurn), new ExileFromGraveCost(new TargetCardInYourGraveyard(2, new FilterCard("cards from your graveyard"))))); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1,1,Duration.EndOfTurn), new ExileFromGraveCost(new TargetCardInYourGraveyard(2, StaticFilters.FILTER_CARDS_FROM_YOUR_GRAVEYARD)))); } diff --git a/Mage.Sets/src/mage/cards/p/PsychicImpetus.java b/Mage.Sets/src/mage/cards/p/PsychicImpetus.java index bbbedc9098c..0c477f1680e 100644 --- a/Mage.Sets/src/mage/cards/p/PsychicImpetus.java +++ b/Mage.Sets/src/mage/cards/p/PsychicImpetus.java @@ -2,8 +2,9 @@ package mage.cards.p; import mage.abilities.Ability; import mage.abilities.common.AttacksAttachedTriggeredAbility; -import mage.abilities.common.GoadAttachedAbility; +import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.combat.GoadAttachedEffect; import mage.abilities.effects.common.continuous.BoostEnchantedEffect; import mage.abilities.effects.keyword.ScryEffect; import mage.abilities.keyword.EnchantAbility; @@ -36,7 +37,9 @@ public final class PsychicImpetus extends CardImpl { this.addAbility(ability); // Enchanted creature gets +2/+2 and is goaded. - this.addAbility(new GoadAttachedAbility(new BoostEnchantedEffect(2, 2))); + ability = new SimpleStaticAbility(new BoostEnchantedEffect(2, 2)); + ability.addEffect(new GoadAttachedEffect()); + this.addAbility(ability); // Whenever enchanted creature attacks, you scry 2. this.addAbility(new AttacksAttachedTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/p/PsychicSpear.java b/Mage.Sets/src/mage/cards/p/PsychicSpear.java index 6a127e9733a..8b0bea766a8 100644 --- a/Mage.Sets/src/mage/cards/p/PsychicSpear.java +++ b/Mage.Sets/src/mage/cards/p/PsychicSpear.java @@ -18,7 +18,7 @@ import mage.target.TargetPlayer; */ public final class PsychicSpear extends CardImpl { - private static final FilterCard filter = new FilterCard("a Spirit or Arcane card to discard"); + private static final FilterCard filter = new FilterCard("Spirit or Arcane card"); static { filter.add(Predicates.or(SubType.SPIRIT.getPredicate(),SubType.ARCANE.getPredicate())); diff --git a/Mage.Sets/src/mage/cards/p/PsychicTheft.java b/Mage.Sets/src/mage/cards/p/PsychicTheft.java index a69ee956d9d..55f39792136 100644 --- a/Mage.Sets/src/mage/cards/p/PsychicTheft.java +++ b/Mage.Sets/src/mage/cards/p/PsychicTheft.java @@ -1,6 +1,5 @@ package mage.cards.p; -import mage.MageObject; import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; @@ -71,11 +70,10 @@ class PsychicTheftEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player opponent = game.getPlayer(targetPointer.getFirst(game, source)); - MageObject sourceObject = game.getObject(source.getSourceId()); - if (opponent == null || sourceObject == null) { + if (opponent == null) { return false; } - opponent.revealCards(sourceObject.getName(), opponent.getHand(), game); + opponent.revealCards(CardUtil.getSourceName(game, source), opponent.getHand(), game); Player controller = game.getPlayer(source.getControllerId()); if (controller == null) { return false; @@ -91,8 +89,7 @@ class PsychicTheftEffect extends OneShotEffect { if (chosenCard == null) { return false; } - UUID exileId = CardUtil.getExileZoneId(game, source); - controller.moveCardToExileWithInfo(chosenCard, exileId, sourceObject.getIdName(), source, game, Zone.HAND, true); + controller.moveCardToExileWithInfo(chosenCard, CardUtil.getExileZoneId(game, source), CardUtil.getSourceName(game, source), source, game, Zone.HAND, true); CardUtil.makeCardPlayable(game, source, chosenCard, Duration.Custom, false); diff --git a/Mage.Sets/src/mage/cards/p/PsychicVortex.java b/Mage.Sets/src/mage/cards/p/PsychicVortex.java index 59f1e74bb03..f66bc97c059 100644 --- a/Mage.Sets/src/mage/cards/p/PsychicVortex.java +++ b/Mage.Sets/src/mage/cards/p/PsychicVortex.java @@ -1,43 +1,38 @@ package mage.cards.p; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.BeginningOfEndStepTriggeredAbility; import mage.abilities.costs.Cost; import mage.abilities.costs.CostImpl; -import mage.abilities.effects.Effect; -import mage.abilities.effects.common.SacrificeTargetEffect; +import mage.abilities.effects.common.SacrificeControllerEffect; import mage.abilities.effects.common.discard.DiscardHandControllerEffect; import mage.abilities.keyword.CumulativeUpkeepAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.TargetController; -import mage.filter.common.FilterControlledLandPermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; -import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; /** - * * @author fireshoes */ public final class PsychicVortex extends CardImpl { public PsychicVortex(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}{U}"); // Cumulative upkeep-Draw a card. this.addAbility(new CumulativeUpkeepAbility(new PsychicVortexCost())); - + // At the beginning of your end step, sacrifice a land and discard your hand. - Effect effect = new SacrificeTargetEffect(); - effect.setText("sacrifice a land"); - Ability ability = new BeginningOfEndStepTriggeredAbility(effect, TargetController.YOU, false); - effect = new DiscardHandControllerEffect(); - effect.setText("and discard your hand"); - ability.addEffect(effect); - ability.addTarget(new TargetControlledPermanent(new FilterControlledLandPermanent())); + Ability ability = new BeginningOfEndStepTriggeredAbility(new SacrificeControllerEffect( + StaticFilters.FILTER_CONTROLLED_LAND_SHORT_TEXT, 1, null + ), TargetController.YOU, false); + ability.addEffect(new DiscardHandControllerEffect().concatBy("and")); this.addAbility(ability); } @@ -52,7 +47,7 @@ public final class PsychicVortex extends CardImpl { } class PsychicVortexCost extends CostImpl { - + PsychicVortexCost() { this.text = "Draw a card"; } @@ -68,18 +63,18 @@ class PsychicVortexCost extends CostImpl { controller.drawCards(1, source, game); this.paid = true; return true; - } + } return false; } @Override public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) { Player controller = game.getPlayer(controllerId); - return controller != null && controller.getLibrary().hasCards(); + return controller != null; } - + @Override public PsychicVortexCost copy() { return new PsychicVortexCost(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/p/PublicExecution.java b/Mage.Sets/src/mage/cards/p/PublicExecution.java index e17e53f3ab6..22c47fe259c 100644 --- a/Mage.Sets/src/mage/cards/p/PublicExecution.java +++ b/Mage.Sets/src/mage/cards/p/PublicExecution.java @@ -8,6 +8,7 @@ import mage.abilities.effects.common.continuous.BoostAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.ControllerIdPredicate; @@ -23,18 +24,12 @@ import java.util.UUID; */ public final class PublicExecution extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public PublicExecution(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{5}{B}"); // Destroy target creature an opponent controls. Each other creature that player controls gets -2/-0 until end of turn. this.getSpellAbility().addEffect(new DestroyTargetEffect()); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); this.getSpellAbility().addEffect(new PublicExecutionEffect()); } diff --git a/Mage.Sets/src/mage/cards/p/PursueGlory.java b/Mage.Sets/src/mage/cards/p/PursueGlory.java index 57f958fd214..15e64358792 100644 --- a/Mage.Sets/src/mage/cards/p/PursueGlory.java +++ b/Mage.Sets/src/mage/cards/p/PursueGlory.java @@ -1,15 +1,14 @@ - package mage.cards.p; import java.util.UUID; -import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.common.continuous.BoostAllEffect; import mage.abilities.keyword.CyclingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.common.FilterAttackingCreature; +import mage.filter.StaticFilters; /** * @@ -21,10 +20,10 @@ public final class PursueGlory extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{R}"); // Attacking creatures get +2/+0 until end of turn. - getSpellAbility().addEffect(new BoostAllEffect(2, 0, Duration.EndOfTurn, new FilterAttackingCreature("Attacking creatures"), false)); + getSpellAbility().addEffect(new BoostAllEffect(2, 0, Duration.EndOfTurn, StaticFilters.FILTER_ATTACKING_CREATURES, false)); // Cycling {2} - this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}"))); + this.addAbility(new CyclingAbility(new GenericManaCost(2))); } private PursueGlory(final PursueGlory card) { diff --git a/Mage.Sets/src/mage/cards/p/PusKami.java b/Mage.Sets/src/mage/cards/p/PusKami.java index c7643966089..1014c5b7deb 100644 --- a/Mage.Sets/src/mage/cards/p/PusKami.java +++ b/Mage.Sets/src/mage/cards/p/PusKami.java @@ -1,9 +1,7 @@ - package mage.cards.p; import java.util.UUID; import mage.MageInt; -import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; @@ -16,9 +14,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.ColoredManaSymbol; import mage.constants.Zone; -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; /** @@ -27,12 +23,6 @@ import mage.target.common.TargetCreaturePermanent; */ public final class PusKami extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public PusKami(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{5}{B}{B}"); this.subtype.add(SubType.SPIRIT); @@ -42,7 +32,7 @@ public final class PusKami extends CardImpl { // {B}, Sacrifice Pus Kami: Destroy target nonblack creature. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new ColoredManaCost(ColoredManaSymbol.B)); ability.addCost(new SacrificeSourceCost()); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); this.addAbility(ability); this.addAbility(new SoulshiftAbility(6)); } diff --git a/Mage.Sets/src/mage/cards/p/PyreSpawn.java b/Mage.Sets/src/mage/cards/p/PyreSpawn.java new file mode 100644 index 00000000000..73372456180 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PyreSpawn.java @@ -0,0 +1,41 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.target.common.TargetAnyTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PyreSpawn extends CardImpl { + + public PyreSpawn(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}{R}"); + + this.subtype.add(SubType.ELEMENTAL); + this.power = new MageInt(6); + this.toughness = new MageInt(4); + + // When Pyre Spawn dies, it deals 3 damage to any target. + Ability ability = new DiesSourceTriggeredAbility(new DamageTargetEffect(3, "it")); + ability.addTarget(new TargetAnyTarget()); + this.addAbility(ability); + } + + private PyreSpawn(final PyreSpawn card) { + super(card); + } + + @Override + public PyreSpawn copy() { + return new PyreSpawn(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/Pyrokinesis.java b/Mage.Sets/src/mage/cards/p/Pyrokinesis.java index 179950b2854..b86e2277b50 100644 --- a/Mage.Sets/src/mage/cards/p/Pyrokinesis.java +++ b/Mage.Sets/src/mage/cards/p/Pyrokinesis.java @@ -1,7 +1,5 @@ - package mage.cards.p; -import java.util.UUID; import mage.ObjectColor; import mage.abilities.costs.AlternativeCostSourceAbility; import mage.abilities.costs.common.ExileFromHandCost; @@ -10,28 +8,30 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.filter.common.FilterOwnedCard; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardIdPredicate; import mage.filter.predicate.mageobject.ColorPredicate; import mage.target.common.TargetCardInHand; import mage.target.common.TargetCreaturePermanentAmount; +import java.util.UUID; + /** - * * @author Plopman */ public final class Pyrokinesis extends CardImpl { - public Pyrokinesis(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{4}{R}{R}"); + private static final FilterOwnedCard filter + = new FilterOwnedCard("a red card from your hand"); + static { + filter.add(new ColorPredicate(ObjectColor.RED)); + } + + public Pyrokinesis(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{4}{R}{R}"); // You may exile a red card from your hand rather than pay Pyrokinesis's mana cost. - FilterOwnedCard filter = new FilterOwnedCard("a red card from your hand"); - filter.add(new ColorPredicate(ObjectColor.RED)); - filter.add(Predicates.not(new CardIdPredicate(this.getId()))); // the exile cost can never be paid with the card itself this.addAbility(new AlternativeCostSourceAbility(new ExileFromHandCost(new TargetCardInHand(filter)))); - + // Pyrokinesis deals 4 damage divided as you choose among any number of target creatures. this.getSpellAbility().addEffect(new DamageMultiEffect(4)); this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount(4)); diff --git a/Mage.Sets/src/mage/cards/p/Pyrotechnics.java b/Mage.Sets/src/mage/cards/p/Pyrotechnics.java index ef6d935db08..354c0e384b7 100644 --- a/Mage.Sets/src/mage/cards/p/Pyrotechnics.java +++ b/Mage.Sets/src/mage/cards/p/Pyrotechnics.java @@ -18,7 +18,7 @@ public final class Pyrotechnics extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{4}{R}"); - // Pyrotechnics deals 4 damage divided as you choose among any number of target creatures and/or players. + // Pyrotechnics deals 4 damage divided as you choose among any number of targets. this.getSpellAbility().addEffect(new DamageMultiEffect(4)); this.getSpellAbility().addTarget(new TargetAnyTargetAmount(4)); } diff --git a/Mage.Sets/src/mage/cards/q/QuandrixCampus.java b/Mage.Sets/src/mage/cards/q/QuandrixCampus.java index 4fff5fb6473..a6c2f408b8a 100644 --- a/Mage.Sets/src/mage/cards/q/QuandrixCampus.java +++ b/Mage.Sets/src/mage/cards/q/QuandrixCampus.java @@ -30,7 +30,7 @@ public final class QuandrixCampus extends CardImpl { this.addAbility(new BlueManaAbility()); // {4}, {T}: Scry 1. - Ability ability = new SimpleActivatedAbility(new ScryEffect(1), new GenericManaCost(4)); + Ability ability = new SimpleActivatedAbility(new ScryEffect(1, false), new GenericManaCost(4)); ability.addCost(new TapSourceCost()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/q/QuarryHauler.java b/Mage.Sets/src/mage/cards/q/QuarryHauler.java index 8de39c379fc..52837ab730e 100644 --- a/Mage.Sets/src/mage/cards/q/QuarryHauler.java +++ b/Mage.Sets/src/mage/cards/q/QuarryHauler.java @@ -1,7 +1,6 @@ package mage.cards.q; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -9,18 +8,20 @@ import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.counters.Counter; import mage.counters.CounterType; -import mage.counters.Counters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetPermanent; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + /** - * * @author Styxo */ public final class QuarryHauler extends CardImpl { @@ -52,7 +53,8 @@ class QuarryHaulerEffect extends OneShotEffect { public QuarryHaulerEffect() { super(Outcome.BoostCreature); - this.staticText = "for each kind of counter on target permanent, put another counter of that kind on it or remove one from it"; + this.staticText = "for each kind of counter on target permanent, " + + "put another counter of that kind on it or remove one from it"; } @@ -69,33 +71,27 @@ class QuarryHaulerEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (controller != null) { - if (permanent != null) { - Counters counters = permanent.getCounters(game).copy(); - CounterType counterType; - for (Counter counter : counters.values()) { - if (controller.chooseUse(Outcome.BoostCreature, "Choose whether to add or remove a " + counter.getName() + " counter", null, "Add", "Remove", source, game)) { - counterType = CounterType.findByName(counter.getName()); - Counter counterToAdd; - if (counterType != null) { - counterToAdd = counterType.createInstance(); - } else { - counterToAdd = new Counter(counter.getName()); - } - permanent.addCounters(counterToAdd, source.getControllerId(), source, game); - } else { - counterType = CounterType.findByName(counter.getName()); - if (counterType != null) { - permanent.removeCounters(counterType.createInstance(), source, game); - } else { - permanent.removeCounters(counter.getName(), 1, source, game); - } - } - } - } - return true; + if (controller == null || permanent == null) { + return false; } - return false; + List counterNames = permanent + .getCounters(game) + .values() + .stream() + .map(Counter::getName) + .collect(Collectors.toList()); + for (String counterName : counterNames) { + Counter newCounter = CounterType.findByName(counterName).createInstance(); + if (controller.chooseUse( + Outcome.BoostCreature, "Add or remove a " + counterName + " counter?", + null, "Add", "Remove", source, game + )) { + permanent.addCounters(newCounter, source.getControllerId(), source, game); + } else { + permanent.removeCounters(newCounter, source, game); + } + } + return true; } } diff --git a/Mage.Sets/src/mage/cards/q/QuestForRenewal.java b/Mage.Sets/src/mage/cards/q/QuestForRenewal.java index 1c7e2922d01..2fbf44787b7 100644 --- a/Mage.Sets/src/mage/cards/q/QuestForRenewal.java +++ b/Mage.Sets/src/mage/cards/q/QuestForRenewal.java @@ -13,6 +13,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Zone; import mage.counters.CounterType; +import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; /** @@ -26,7 +27,7 @@ public final class QuestForRenewal extends CardImpl { // Whenever a creature you control becomes tapped, you may put a quest counter on Quest for Renewal. this.addAbility(new BecomesTappedTriggeredAbility(new AddCountersSourceEffect(CounterType.QUEST.createInstance()), - true, new FilterControlledCreaturePermanent("a creature you control"))); + true, StaticFilters.FILTER_CONTROLLED_A_CREATURE)); // As long as there are four or more quest counters on Quest for Renewal, untap all creatures you control during each other player's untap step. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect( diff --git a/Mage.Sets/src/mage/cards/q/QuestForUlasTemple.java b/Mage.Sets/src/mage/cards/q/QuestForUlasTemple.java index 6cf0718166b..86e916037b8 100644 --- a/Mage.Sets/src/mage/cards/q/QuestForUlasTemple.java +++ b/Mage.Sets/src/mage/cards/q/QuestForUlasTemple.java @@ -1,47 +1,59 @@ - package mage.cards.q; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.effects.Effect; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.PutCardFromHandOntoBattlefieldEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.cards.Cards; import mage.cards.CardsImpl; import mage.constants.*; import mage.counters.CounterType; +import mage.filter.FilterCard; import mage.filter.common.FilterCreatureCard; import mage.filter.predicate.Predicates; import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class QuestForUlasTemple extends CardImpl { + private static final FilterCard filter + = new FilterCreatureCard("Kraken, Leviathan, Octopus, or Serpent creature card"); + + static { + filter.add(Predicates.or( + SubType.KRAKEN.getPredicate(), + SubType.LEVIATHAN.getPredicate(), + SubType.OCTOPUS.getPredicate(), + SubType.SERPENT.getPredicate() + )); + } + + private static final Condition condition = new SourceHasCounterCondition(CounterType.QUEST, 3); + public QuestForUlasTemple(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{U}"); // At the beginning of your upkeep, you may look at the top card of your library. If it's a creature card, you may reveal it and put a quest counter on Quest for Ula's Temple. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new QuestForUlasTempleEffect(), TargetController.YOU, true)); + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new QuestForUlasTempleEffect(), TargetController.YOU, true + )); // At the beginning of each end step, if there are three or more quest counters on Quest for Ula's Temple, you may put a Kraken, Leviathan, Octopus, or Serpent creature card from your hand onto the battlefield. - FilterCreatureCard filter = new FilterCreatureCard("Kraken, Leviathan, Octopus, or Serpent creature card"); - filter.add(Predicates.or( - SubType.KRAKEN.getPredicate(), - SubType.LEVIATHAN.getPredicate(), - SubType.OCTOPUS.getPredicate(), - SubType.SERPENT.getPredicate())); - this.addAbility(new QuestForUlasTempleTriggeredAbility(new PutCardFromHandOntoBattlefieldEffect(filter))); + this.addAbility(new BeginningOfEndStepTriggeredAbility( + Zone.BATTLEFIELD, new PutCardFromHandOntoBattlefieldEffect(filter), + TargetController.ANY, condition, false + )); } private QuestForUlasTemple(final QuestForUlasTemple card) { @@ -56,12 +68,13 @@ public final class QuestForUlasTemple extends CardImpl { class QuestForUlasTempleEffect extends OneShotEffect { - public QuestForUlasTempleEffect() { + QuestForUlasTempleEffect() { super(Outcome.Benefit); - this.staticText = "you may look at the top card of your library. If it's a creature card, you may reveal it and put a quest counter on {this}"; + this.staticText = "you may look at the top card of your library. " + + "If it's a creature card, you may reveal it and put a quest counter on {this}"; } - public QuestForUlasTempleEffect(final QuestForUlasTempleEffect effect) { + private QuestForUlasTempleEffect(final QuestForUlasTempleEffect effect) { super(effect); } @@ -73,54 +86,27 @@ class QuestForUlasTempleEffect extends OneShotEffect { @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()) { - Card card = controller.getLibrary().getFromTop(game); - Cards cards = new CardsImpl(card); - controller.lookAtCards(sourcePermanent.getName(), cards, game); - if (card.isCreature(game)) { - if (controller.chooseUse(Outcome.DrawCard, "Reveal the top card of your library?", source, game)) { - controller.revealCards(sourcePermanent.getName(), cards, game); - Permanent questForUlasTemple = game.getPermanent(source.getSourceId()); - if (questForUlasTemple != null) { - questForUlasTemple.addCounters(CounterType.QUEST.createInstance(), source.getControllerId(), source, game); - return true; - } - } - } + if (controller == null) { + return false; } - return false; - } -} - -class QuestForUlasTempleTriggeredAbility extends TriggeredAbilityImpl { - - public QuestForUlasTempleTriggeredAbility(Effect effect) { - super(Zone.BATTLEFIELD, effect, true); - } - - public QuestForUlasTempleTriggeredAbility(final QuestForUlasTempleTriggeredAbility ability) { - super(ability); - } - - @Override - public QuestForUlasTempleTriggeredAbility copy() { - return new QuestForUlasTempleTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.END_TURN_STEP_PRE; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent quest = game.getPermanent(super.getSourceId()); - return quest != null && quest.getCounters(game).getCount(CounterType.QUEST) >= 3; - } - - @Override - public String getTriggerPhrase() { - return "At the beginning of each end step, if there are three or more quest counters on {this}, " ; + Card card = controller.getLibrary().getFromTop(game); + if (card == null) { + return false; + } + controller.lookAtCards("Top of library", card, game); + if (!card.isCreature(game) || !controller.chooseUse( + Outcome.DrawCard, "Reveal the top card of your library?", source, game + )) { + return true; + } + controller.revealCards(source, new CardsImpl(card), game); + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (permanent != null) { + permanent.addCounters( + CounterType.QUEST.createInstance(), + source.getControllerId(), source, game + ); + } + return true; } } diff --git a/Mage.Sets/src/mage/cards/q/QuiGonJinn.java b/Mage.Sets/src/mage/cards/q/QuiGonJinn.java index 4c8ac240c50..2350caadfc6 100644 --- a/Mage.Sets/src/mage/cards/q/QuiGonJinn.java +++ b/Mage.Sets/src/mage/cards/q/QuiGonJinn.java @@ -4,7 +4,7 @@ package mage.cards.q; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.AttacksAloneTriggeredAbility; +import mage.abilities.common.AttacksAloneSourceTriggeredAbility; import mage.abilities.common.LeavesBattlefieldTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.Effect; @@ -42,7 +42,7 @@ public final class QuiGonJinn extends CardImpl { // When Qui-Gon Jinn attacks alone, it gets +2/+2 and lifelink until end of turn. Effect effect = new BoostSourceEffect(2, 2, Duration.EndOfTurn); effect.setText("it gets +2/+2"); - Ability abitity = new AttacksAloneTriggeredAbility(effect); + Ability abitity = new AttacksAloneSourceTriggeredAbility(effect); effect = new GainAbilitySourceEffect(LifelinkAbility.getInstance()); effect.setText("and lifelink until end of turn"); abitity.addEffect(effect); diff --git a/Mage.Sets/src/mage/cards/q/QuickSliver.java b/Mage.Sets/src/mage/cards/q/QuickSliver.java index 120575ed317..97ac74de838 100644 --- a/Mage.Sets/src/mage/cards/q/QuickSliver.java +++ b/Mage.Sets/src/mage/cards/q/QuickSliver.java @@ -20,7 +20,7 @@ import mage.filter.common.FilterCreatureCard; */ public final class QuickSliver extends CardImpl { - private static final FilterCreatureCard filter = new FilterCreatureCard("Sliver cards"); + private static final FilterCreatureCard filter = new FilterCreatureCard("Sliver spells"); static { filter.add(SubType.SLIVER.getPredicate()); } diff --git a/Mage.Sets/src/mage/cards/q/QuicksilverFountain.java b/Mage.Sets/src/mage/cards/q/QuicksilverFountain.java index cfd1f6faa2e..ded892c4d58 100644 --- a/Mage.Sets/src/mage/cards/q/QuicksilverFountain.java +++ b/Mage.Sets/src/mage/cards/q/QuicksilverFountain.java @@ -96,7 +96,7 @@ class QuicksilverFountainEffect extends OneShotEffect { Permanent landChosen = game.getPermanent(source.getFirstTarget()); landChosen.addCounters(CounterType.FLOOD.createInstance(), player.getId(), source, game); ContinuousEffect becomesBasicLandTargetEffect - = new BecomesBasicLandTargetEffect(Duration.Custom, false, SubType.ISLAND); + = new BecomesBasicLandTargetEffect(Duration.Custom, SubType.ISLAND); ConditionalContinuousEffect effect = new ConditionalContinuousEffect(becomesBasicLandTargetEffect, new LandHasFloodCounterCondition(), staticText); diff --git a/Mage.Sets/src/mage/cards/q/QuicksmithRebel.java b/Mage.Sets/src/mage/cards/q/QuicksmithRebel.java index a51b2dab924..0fa59234bc5 100644 --- a/Mage.Sets/src/mage/cards/q/QuicksmithRebel.java +++ b/Mage.Sets/src/mage/cards/q/QuicksmithRebel.java @@ -1,13 +1,10 @@ package mage.cards.q; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.condition.common.SourceOnBattlefieldControlUnchangedCondition; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.cards.CardImpl; @@ -15,14 +12,13 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.common.FilterControlledArtifactPermanent; +import mage.filter.StaticFilters; import mage.target.TargetPermanent; import mage.target.common.TargetAnyTarget; -import mage.watchers.common.LostControlWatcher; + +import java.util.UUID; /** - * * @author Styxo */ public final class QuicksmithRebel extends CardImpl { @@ -36,15 +32,10 @@ public final class QuicksmithRebel extends CardImpl { this.toughness = new MageInt(2); // When Quicksmith Rebel enters the battlefield, target artifact you control gains "{T}: This artifact deals 2 damage to any target" for as long as you control Quicksmith Rebel. - Ability artifactAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(2), new TapSourceCost()); + Ability artifactAbility = new SimpleActivatedAbility(new DamageTargetEffect(2), new TapSourceCost()); artifactAbility.addTarget(new TargetAnyTarget()); - ConditionalContinuousEffect effect = new ConditionalContinuousEffect( - new GainAbilityTargetEffect(artifactAbility, Duration.Custom), - new SourceOnBattlefieldControlUnchangedCondition(), - "target artifact you control gains \"{T}: This artifact deals 2 damage to any target\" for as long as you control {this}"); - Ability ability = new EntersBattlefieldTriggeredAbility(effect, false); - ability.addTarget(new TargetPermanent(new FilterControlledArtifactPermanent())); - ability.addWatcher(new LostControlWatcher()); + Ability ability = new EntersBattlefieldTriggeredAbility(new GainAbilityTargetEffect(artifactAbility, Duration.WhileControlled).setText("target artifact you control gains \"{T}: This artifact deals 2 damage to any target\" for as long as you control {this}")); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/q/QuicksmithSpy.java b/Mage.Sets/src/mage/cards/q/QuicksmithSpy.java index 9a675936772..daa5a2ac99a 100644 --- a/Mage.Sets/src/mage/cards/q/QuicksmithSpy.java +++ b/Mage.Sets/src/mage/cards/q/QuicksmithSpy.java @@ -1,27 +1,24 @@ package mage.cards.q; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.condition.common.SourceOnBattlefieldControlUnchangedCondition; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.decorator.ConditionalContinuousEffect; -import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.common.FilterControlledArtifactPermanent; +import mage.filter.StaticFilters; import mage.target.TargetPermanent; -import mage.watchers.common.LostControlWatcher; +import mage.target.common.TargetAnyTarget; + +import java.util.UUID; /** - * * @author Styxo */ public final class QuicksmithSpy extends CardImpl { @@ -35,14 +32,10 @@ public final class QuicksmithSpy extends CardImpl { this.toughness = new MageInt(3); // When Quicksmith Spy enters the battlefield, target artifact you control gains "{T}: Draw a card" for as long as you control Quicksmith Spy. - Ability artifactAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), new TapSourceCost()); - ConditionalContinuousEffect effect = new ConditionalContinuousEffect( - new GainAbilityTargetEffect(artifactAbility, Duration.Custom), - new SourceOnBattlefieldControlUnchangedCondition(), - "target artifact you control gains \"{T}: Draw a card\" for as long as you control {this}"); - Ability ability = new EntersBattlefieldTriggeredAbility(effect, false); - ability.addTarget(new TargetPermanent(new FilterControlledArtifactPermanent())); - ability.addWatcher(new LostControlWatcher()); + Ability artifactAbility = new SimpleActivatedAbility(new DamageTargetEffect(2), new TapSourceCost()); + artifactAbility.addTarget(new TargetAnyTarget()); + Ability ability = new EntersBattlefieldTriggeredAbility(new GainAbilityTargetEffect(artifactAbility, Duration.WhileControlled).setText("target artifact you control gains \"{T}: Draw a card\" for as long as you control {this}")); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/q/QuietContemplation.java b/Mage.Sets/src/mage/cards/q/QuietContemplation.java index bc16c1b1527..25de5c921b1 100644 --- a/Mage.Sets/src/mage/cards/q/QuietContemplation.java +++ b/Mage.Sets/src/mage/cards/q/QuietContemplation.java @@ -12,9 +12,8 @@ import mage.abilities.effects.common.TapTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.TargetController; import mage.filter.FilterSpell; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.filter.predicate.Predicates; import mage.target.common.TargetCreaturePermanent; @@ -24,12 +23,10 @@ import mage.target.common.TargetCreaturePermanent; */ public final class QuietContemplation extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); private static final FilterSpell filterNonCreature = new FilterSpell("a noncreature spell"); static { filterNonCreature.add(Predicates.not(CardType.CREATURE.getPredicate())); - filter.add(TargetController.OPPONENT.getControllerPredicate()); } public QuietContemplation(UUID ownerId, CardSetInfo setInfo) { @@ -42,7 +39,7 @@ public final class QuietContemplation extends CardImpl { effect.setText("and it doesn't untap during its controller's next untap step"); doIfCostPaid.addEffect(effect); Ability ability = new SpellCastControllerTriggeredAbility(doIfCostPaid, filterNonCreature, false); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/q/QuillmaneBaku.java b/Mage.Sets/src/mage/cards/q/QuillmaneBaku.java index ddf247111fe..29e17f02005 100644 --- a/Mage.Sets/src/mage/cards/q/QuillmaneBaku.java +++ b/Mage.Sets/src/mage/cards/q/QuillmaneBaku.java @@ -9,22 +9,18 @@ import mage.abilities.costs.Cost; import mage.abilities.costs.common.RemoveVariableCountersSourceCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.ComparisonType; -import mage.constants.Outcome; -import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.common.TargetCreaturePermanent; import mage.target.targetadjustment.TargetAdjuster; @@ -33,6 +29,8 @@ import mage.target.targetadjustment.TargetAdjuster; */ public final class QuillmaneBaku extends CardImpl { + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with mana value X or less"); + public QuillmaneBaku(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}"); this.subtype.add(SubType.SPIRIT); @@ -40,14 +38,14 @@ public final class QuillmaneBaku extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(3); - // Whenever you cast a Spirit or Arcane spell, you may put a ki counter on Skullmane Baku. - this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.KI.createInstance()), StaticFilters.SPIRIT_OR_ARCANE_CARD, true)); + // Whenever you cast a Spirit or Arcane spell, you may put a ki counter on Quillmane Baku. + this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.KI.createInstance()), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true)); - // {1}, Tap, Remove X ki counters from Quillmane Baku: Return target creature with converted mana cost X or less to its owner's hand. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new QuillmaneBakuReturnEffect(), new GenericManaCost(1)); + // {1}, {T}, Remove X ki counters from Quillmane Baku: Return target creature with mana value X or less to its owner's hand. + Ability ability = new SimpleActivatedAbility(new ReturnToHandTargetEffect(), new GenericManaCost(1)); ability.addCost(new TapSourceCost()); - ability.addCost(new RemoveVariableCountersSourceCost(CounterType.KI.createInstance(1))); - ability.addTarget(new TargetCreaturePermanent()); + ability.addCost(new RemoveVariableCountersSourceCost(CounterType.KI.createInstance())); + ability.addTarget(new TargetCreaturePermanent(filter)); ability.setTargetAdjuster(QuillmaneBakuAdjuster.instance); this.addAbility(ability); } @@ -67,46 +65,15 @@ enum QuillmaneBakuAdjuster implements TargetAdjuster { @Override public void adjustTargets(Ability ability, Game game) { - int maxConvManaCost = 0; + int xValue = 0; for (Cost cost : ability.getCosts()) { if (cost instanceof RemoveVariableCountersSourceCost) { - maxConvManaCost = ((RemoveVariableCountersSourceCost) cost).getAmount(); + xValue = ((RemoveVariableCountersSourceCost) cost).getAmount(); } } ability.getTargets().clear(); - FilterCreaturePermanent newFilter = new FilterCreaturePermanent("creature with mana value " + maxConvManaCost + " or less"); - newFilter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, maxConvManaCost + 1)); - TargetCreaturePermanent target = new TargetCreaturePermanent(newFilter); - ability.getTargets().add(target); - } -} - -class QuillmaneBakuReturnEffect extends OneShotEffect { - - public QuillmaneBakuReturnEffect() { - super(Outcome.ReturnToHand); - this.staticText = "Return target creature with mana value X or less to its owner's hand"; - } - - public QuillmaneBakuReturnEffect(final QuillmaneBakuReturnEffect effect) { - super(effect); - } - - @Override - public QuillmaneBakuReturnEffect copy() { - return new QuillmaneBakuReturnEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller == null) { - return false; - } - Permanent permanent = game.getPermanent(this.getTargetPointer().getFirst(game, source)); - if (permanent != null) { - controller.moveCards(permanent, Zone.HAND, source, game); - } - return true; + FilterCreaturePermanent newFilter = new FilterCreaturePermanent("creature with mana value " + xValue + " or less"); + newFilter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, xValue + 1)); + ability.addTarget(new TargetCreaturePermanent(newFilter)); } } diff --git a/Mage.Sets/src/mage/cards/r/RabbitBattery.java b/Mage.Sets/src/mage/cards/r/RabbitBattery.java new file mode 100644 index 00000000000..11aefbdcb7f --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RabbitBattery.java @@ -0,0 +1,53 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.continuous.BoostEquippedEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.ReconfigureAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RabbitBattery extends CardImpl { + + public RabbitBattery(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{R}"); + + this.subtype.add(SubType.EQUIPMENT); + this.subtype.add(SubType.RABBIT); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // Equipped creature gets +1/+1 and has haste. + Ability ability = new SimpleStaticAbility(new BoostEquippedEffect(1, 1)); + ability.addEffect(new GainAbilityAttachedEffect( + HasteAbility.getInstance(), AttachmentType.EQUIPMENT + ).setText("and has haste")); + this.addAbility(ability); + + // Reconfigure {R} + this.addAbility(new ReconfigureAbility("{R}")); + } + + private RabbitBattery(final RabbitBattery card) { + super(card); + } + + @Override + public RabbitBattery copy() { + return new RabbitBattery(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RabbleRouser.java b/Mage.Sets/src/mage/cards/r/RabbleRouser.java index 99c0a6ebaf7..e2827759b83 100644 --- a/Mage.Sets/src/mage/cards/r/RabbleRouser.java +++ b/Mage.Sets/src/mage/cards/r/RabbleRouser.java @@ -1,4 +1,3 @@ - package mage.cards.r; import java.util.UUID; @@ -6,7 +5,7 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.costs.mana.ColoredManaCost; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.SourcePermanentPowerCount; import mage.abilities.dynamicvalue.common.StaticValue; @@ -15,10 +14,10 @@ import mage.abilities.keyword.BloodthirstAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; +import mage.constants.ColoredManaSymbol; import mage.constants.Duration; -import mage.constants.Zone; -import mage.filter.common.FilterAttackingCreature; +import mage.constants.SubType; +import mage.filter.StaticFilters; /** * @@ -26,6 +25,8 @@ import mage.filter.common.FilterAttackingCreature; */ public final class RabbleRouser extends CardImpl { + private static final DynamicValue xValue = new SourcePermanentPowerCount(false); + public RabbleRouser(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); this.subtype.add(SubType.GOBLIN); @@ -38,11 +39,10 @@ public final class RabbleRouser extends CardImpl { this.addAbility(new BloodthirstAbility(1)); //{R}, {T}: Attacking creatures get +X/+0 until end of turn, where X is Rabble-Rouser's power. - DynamicValue amount = new SourcePermanentPowerCount(); - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, - new BoostAllEffect(amount, StaticValue.get(0), Duration.EndOfTurn, new FilterAttackingCreature(), false, - "Attacking creatures get +X/+0 until end of turn, where X is {this}'s power", true), - new ManaCostsImpl("{R}")); + + Ability ability = new SimpleActivatedAbility( + new BoostAllEffect(xValue, StaticValue.get(0), Duration.EndOfTurn, StaticFilters.FILTER_ATTACKING_CREATURES, false, null, true), + new ColoredManaCost(ColoredManaSymbol.R)); ability.addCost(new TapSourceCost()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/r/RadiantDestiny.java b/Mage.Sets/src/mage/cards/r/RadiantDestiny.java index e9abe216472..7b90a359c10 100644 --- a/Mage.Sets/src/mage/cards/r/RadiantDestiny.java +++ b/Mage.Sets/src/mage/cards/r/RadiantDestiny.java @@ -42,7 +42,7 @@ public final class RadiantDestiny extends CardImpl { this.addAbility(new AsEntersBattlefieldAbility(new ChooseCreatureTypeEffect(Outcome.BoostCreature))); // Creatures you control of the chosen type get +1/+1. As long as you have the city's blessing, they also have vigilance. - Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostAllOfChosenSubtypeEffect(1, 1, Duration.WhileOnBattlefield, filter, true)); + Ability ability = new SimpleStaticAbility(new BoostAllOfChosenSubtypeEffect(1, 1, Duration.WhileOnBattlefield, filter, false)); ContinuousEffect effect = new ConditionalContinuousEffect( new GainAbilityAllOfChosenSubtypeEffect(VigilanceAbility.getInstance(), Duration.WhileOnBattlefield, FILTER_PERMANENT_CREATURES_CONTROLLED), CitysBlessingCondition.instance, diff --git a/Mage.Sets/src/mage/cards/r/RadiantGrace.java b/Mage.Sets/src/mage/cards/r/RadiantGrace.java new file mode 100644 index 00000000000..0ab4e8ac2d9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RadiantGrace.java @@ -0,0 +1,106 @@ +package mage.cards.r; + +import mage.abilities.Ability; +import mage.abilities.common.DiesAttachedTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.abilities.keyword.TransformAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; +import mage.target.common.TargetOpponent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RadiantGrace extends CardImpl { + + public RadiantGrace(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}"); + + this.subtype.add(SubType.AURA); + this.secondSideCardClazz = mage.cards.r.RadiantRestraints.class; + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + this.addAbility(new EnchantAbility(auraTarget.getTargetName())); + + // Enchanted creature gets +1/+0 and has vigilance. + Ability ability = new SimpleStaticAbility(new BoostEnchantedEffect(1, 0)); + ability.addEffect(new GainAbilityAttachedEffect( + VigilanceAbility.getInstance(), AttachmentType.AURA + ).setText("and has vigilance")); + this.addAbility(ability); + + // When enchanted creature dies, return Radiant Grace to the battlefield transformed under your control attached to target opponent. + this.addAbility(new TransformAbility()); + ability = new DiesAttachedTriggeredAbility( + new RadiantGraceEffect(), "enchanted creature", false + ); + ability.addTarget(new TargetOpponent()); + this.addAbility(ability); + } + + private RadiantGrace(final RadiantGrace card) { + super(card); + } + + @Override + public RadiantGrace copy() { + return new RadiantGrace(this); + } +} + +class RadiantGraceEffect extends OneShotEffect { + + RadiantGraceEffect() { + super(Outcome.Benefit); + staticText = "return {this} to the battlefield transformed under your control attached to target opponent"; + } + + private RadiantGraceEffect(final RadiantGraceEffect effect) { + super(effect); + } + + @Override + public RadiantGraceEffect copy() { + return new RadiantGraceEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Player player = game.getPlayer(source.getFirstTarget()); + if (controller == null || player == null + || game.getState().getZone(source.getSourceId()) != Zone.GRAVEYARD) { + return false; + } + + Card card = game.getCard(source.getSourceId()); + if (card == null) { + return false; + } + + game.getState().setValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + source.getSourceId(), Boolean.TRUE); + UUID secondFaceId = game.getCard(source.getSourceId()).getSecondCardFace().getId(); + game.getState().setValue("attachTo:" + secondFaceId, player.getId()); + if (controller.moveCards(card, Zone.BATTLEFIELD, source, game)) { + player.addAttachment(card.getId(), source, game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/r/RadiantRestraints.java b/Mage.Sets/src/mage/cards/r/RadiantRestraints.java new file mode 100644 index 00000000000..14edb9dbc12 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RadiantRestraints.java @@ -0,0 +1,56 @@ +package mage.cards.r; + +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.PermanentsEnterBattlefieldTappedEffect; +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.constants.TargetController; +import mage.filter.common.FilterCreaturePermanent; +import mage.target.TargetPlayer; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RadiantRestraints extends CardImpl { + + private static final FilterCreaturePermanent filter + = new FilterCreaturePermanent("creatures enchanted player controls"); + + static { + filter.add(TargetController.ENCHANTED.getControllerPredicate()); + } + + public RadiantRestraints(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, ""); + + this.subtype.add(SubType.AURA); + this.subtype.add(SubType.CURSE); + this.color.setWhite(true); + this.nightCard = true; + + // Enchant player + TargetPlayer auraTarget = new TargetPlayer(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + this.addAbility(new EnchantAbility(auraTarget.getTargetName())); + + // Creatures enchanted player controls enter the battlefield tapped. + this.addAbility(new SimpleStaticAbility(new PermanentsEnterBattlefieldTappedEffect(filter))); + } + + private RadiantRestraints(final RadiantRestraints card) { + super(card); + } + + @Override + public RadiantRestraints copy() { + return new RadiantRestraints(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RadiantScrollwielder.java b/Mage.Sets/src/mage/cards/r/RadiantScrollwielder.java index fc7baddd304..7c9eae74af4 100644 --- a/Mage.Sets/src/mage/cards/r/RadiantScrollwielder.java +++ b/Mage.Sets/src/mage/cards/r/RadiantScrollwielder.java @@ -97,7 +97,7 @@ class RadiantScrollwielderEffect extends OneShotEffect { TargetCardInYourGraveyard target = new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY); target.setNotTarget(true); target.setRandom(true); - player.chooseTarget(outcome, target, source, game); + target.chooseTarget(outcome, player.getId(), source, game); Card card = game.getCard(target.getFirstTarget()); if (card == null) { return false; diff --git a/Mage.Sets/src/mage/cards/r/RaffinesTower.java b/Mage.Sets/src/mage/cards/r/RaffinesTower.java new file mode 100644 index 00000000000..6951af8efe6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RaffinesTower.java @@ -0,0 +1,48 @@ +package mage.cards.r; + +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.keyword.CyclingAbility; +import mage.abilities.mana.BlackManaAbility; +import mage.abilities.mana.BlueManaAbility; +import mage.abilities.mana.WhiteManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RaffinesTower extends CardImpl { + + public RaffinesTower(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + this.subtype.add(SubType.PLAINS); + this.subtype.add(SubType.ISLAND); + this.subtype.add(SubType.SWAMP); + + // ({T}: Add {W}, {U}, or {B}.) + this.addAbility(new WhiteManaAbility()); + this.addAbility(new BlueManaAbility()); + this.addAbility(new BlackManaAbility()); + + // Raffine's Tower enters the battlefield tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // Cycling {3} + this.addAbility(new CyclingAbility(new GenericManaCost(3))); + } + + private RaffinesTower(final RaffinesTower card) { + super(card); + } + + @Override + public RaffinesTower copy() { + return new RaffinesTower(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RafiqOfTheMany.java b/Mage.Sets/src/mage/cards/r/RafiqOfTheMany.java index 4d8efab7b1d..3a123322796 100644 --- a/Mage.Sets/src/mage/cards/r/RafiqOfTheMany.java +++ b/Mage.Sets/src/mage/cards/r/RafiqOfTheMany.java @@ -1,35 +1,26 @@ - - package mage.cards.r; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; +import mage.abilities.common.AttacksAloneControlledTriggeredAbility; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.keyword.DoubleStrikeAbility; import mage.abilities.keyword.ExaltedAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; +import mage.constants.SubType; import mage.constants.SuperType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; /** - * * @author BetaSteward_at_googlemail.com */ public final class RafiqOfTheMany extends CardImpl { public RafiqOfTheMany(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{G}{W}{U}"); - + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}{W}{U}"); addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.HUMAN); @@ -41,7 +32,9 @@ public final class RafiqOfTheMany extends CardImpl { this.addAbility(new ExaltedAbility()); // Whenever a creature you control attacks alone, it gains double strike until end of turn. - this.addAbility(new RafiqOfTheManyAbility()); + this.addAbility(new AttacksAloneControlledTriggeredAbility(new GainAbilityTargetEffect( + DoubleStrikeAbility.getInstance(), Duration.EndOfTurn + ).setText("it gains double strike until end of turn"))); } private RafiqOfTheMany(final RafiqOfTheMany card) { @@ -54,43 +47,3 @@ public final class RafiqOfTheMany extends CardImpl { } } - -class RafiqOfTheManyAbility extends TriggeredAbilityImpl { - - public RafiqOfTheManyAbility() { - super(Zone.BATTLEFIELD, new GainAbilityTargetEffect(DoubleStrikeAbility.getInstance(), Duration.EndOfTurn)); - } - - public RafiqOfTheManyAbility(final RafiqOfTheManyAbility ability) { - super(ability); - } - - @Override - public RafiqOfTheManyAbility copy() { - return new RafiqOfTheManyAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DECLARED_ATTACKERS; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (game.isActivePlayer(this.controllerId) ) { - if (game.getCombat().attacksAlone()) { - for (Effect effect: this.getEffects()) { - effect.setTargetPointer(new FixedTarget(game.getCombat().getAttackers().get(0))); - } - return true; - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever a creature you control attacks alone, it gains double strike until end of turn."; - } - -} diff --git a/Mage.Sets/src/mage/cards/r/RageExtractor.java b/Mage.Sets/src/mage/cards/r/RageExtractor.java index 9c4d203be7d..ef77b41705b 100644 --- a/Mage.Sets/src/mage/cards/r/RageExtractor.java +++ b/Mage.Sets/src/mage/cards/r/RageExtractor.java @@ -1,31 +1,44 @@ - package mage.cards.r; -import java.util.UUID; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.Ability; +import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.costs.mana.ManaCost; -import mage.abilities.costs.mana.PhyrexianManaCost; -import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; import mage.abilities.effects.common.DamageTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Zone; +import mage.filter.FilterSpell; +import mage.filter.predicate.Predicate; import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.stack.Spell; +import mage.game.stack.StackObject; import mage.target.common.TargetAnyTarget; +import java.util.UUID; + /** * @author Loki */ public final class RageExtractor extends CardImpl { + private static final FilterSpell filter = new FilterSpell("a spell with {P} in its mana cost"); + + static { + filter.add(RageExtractorPredicate.instance); + } + public RageExtractor(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{4}{R/P}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}{R/P}"); - - this.addAbility(new RageExtractorTriggeredAbility()); + Ability ability = new SpellCastControllerTriggeredAbility( + new DamageTargetEffect(RageExtractorValue.instance) + .setText("{this} deals damage equal to that spell's mana value to any target"), + filter, false + ); + ability.addTarget(new TargetAnyTarget()); + this.addAbility(ability); } private RageExtractor(final RageExtractor card) { @@ -38,44 +51,31 @@ public final class RageExtractor extends CardImpl { } } -class RageExtractorTriggeredAbility extends TriggeredAbilityImpl { - RageExtractorTriggeredAbility() { - super(Zone.BATTLEFIELD, new DamageTargetEffect(0)); - this.addTarget(new TargetAnyTarget()); - } - - RageExtractorTriggeredAbility(final RageExtractorTriggeredAbility ability) { - super(ability); - } +enum RageExtractorPredicate implements Predicate { + instance; @Override - public RageExtractorTriggeredAbility copy() { - return new RageExtractorTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.SPELL_CAST; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getPlayerId().equals(this.controllerId)) { - Spell spell = (Spell) game.getStack().getStackObject(event.getTargetId()); - if (spell != null) { - for (ManaCost cost : spell.getCard().getManaCost()) { - if (cost instanceof PhyrexianManaCost) { - ((DamageTargetEffect)getEffects().get(0)).setAmount(StaticValue.get(spell.getManaValue())); - return true; - } - } - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever you cast a spell with {P} in its mana cost, {this} deals damage equal to that spell's mana value to any target."; + public boolean apply(StackObject input, Game game) { + return ((Spell) input).getCard().getManaCost().stream().anyMatch(ManaCost::isPhyrexian); + } +} + +enum RageExtractorValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + Spell spell = (Spell) effect.getValue("spellCast"); + return spell != null ? spell.getManaValue() : 0; + } + + @Override + public RageExtractorValue copy() { + return this; + } + + @Override + public String getMessage() { + return ""; } } diff --git a/Mage.Sets/src/mage/cards/r/RageForger.java b/Mage.Sets/src/mage/cards/r/RageForger.java index 80e044028a5..7954478f840 100644 --- a/Mage.Sets/src/mage/cards/r/RageForger.java +++ b/Mage.Sets/src/mage/cards/r/RageForger.java @@ -15,7 +15,7 @@ import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.TargetController; import mage.counters.CounterType; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; @@ -30,13 +30,11 @@ import mage.target.common.TargetPlayerOrPlaneswalker; public final class RageForger extends CardImpl { private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("other Shaman creature you control"); - private static final FilterControlledCreaturePermanent filterAttack = new FilterControlledCreaturePermanent("creature you control with a +1/+1 counter on it"); static { filter.add(SubType.SHAMAN.getPredicate()); filter.add(TargetController.YOU.getControllerPredicate()); filter.add(AnotherPredicate.instance); - filterAttack.add(CounterType.P1P1.getPredicate()); } public RageForger(UUID ownerId, CardSetInfo setInfo) { @@ -49,11 +47,11 @@ public final class RageForger extends CardImpl { // When Rage Forger enters the battlefield, put a +1/+1 counter on each other Shaman creature you control. this.addAbility(new EntersBattlefieldTriggeredAbility(new AddCountersAllEffect(CounterType.P1P1.createInstance(), filter), false)); + // Whenever a creature you control with a +1/+1 counter on it attacks, you may have that creature deal 1 damage to target player. - Ability ability = new AttacksCreatureYouControlTriggeredAbility(new RageForgerDamageEffect(), true, filterAttack, true); + Ability ability = new AttacksCreatureYouControlTriggeredAbility(new RageForgerDamageEffect(), true, StaticFilters.FILTER_CONTROLLED_CREATURE_P1P1, true); ability.addTarget(new TargetPlayerOrPlaneswalker()); this.addAbility(ability); - } private RageForger(final RageForger card) { diff --git a/Mage.Sets/src/mage/cards/r/RaggedRecluse.java b/Mage.Sets/src/mage/cards/r/RaggedRecluse.java new file mode 100644 index 00000000000..c8635c25072 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RaggedRecluse.java @@ -0,0 +1,49 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.condition.common.ControllerDiscardedThisTurnCondition; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.hint.common.ControllerDiscardedHint; +import mage.abilities.keyword.TransformAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.watchers.common.DiscardedCardWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RaggedRecluse extends CardImpl { + + public RaggedRecluse(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.PEASANT); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + this.secondSideCardClazz = mage.cards.o.OdiousWitch.class; + + // At the beginning of your end step, if you discarded a card this turn, transform Ragged Recluse. + this.addAbility(new TransformAbility()); + this.addAbility(new BeginningOfEndStepTriggeredAbility( + Zone.BATTLEFIELD, new TransformSourceEffect(), TargetController.YOU, + ControllerDiscardedThisTurnCondition.instance, false + ).addHint(ControllerDiscardedHint.instance), new DiscardedCardWatcher()); + } + + private RaggedRecluse(final RaggedRecluse card) { + super(card); + } + + @Override + public RaggedRecluse copy() { + return new RaggedRecluse(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RagingRiver.java b/Mage.Sets/src/mage/cards/r/RagingRiver.java index 9fb3086d78b..a712afc0885 100644 --- a/Mage.Sets/src/mage/cards/r/RagingRiver.java +++ b/Mage.Sets/src/mage/cards/r/RagingRiver.java @@ -144,7 +144,7 @@ class RagingRiverEffect extends OneShotEffect { } } RestrictionEffect effect = new CantBeBlockedByAllTargetEffect(filter, Duration.EndOfCombat); - effect.setTargetPointer(new FixedTarget(attacker.getId())); + effect.setTargetPointer(new FixedTarget(attacker.getId(), game)); game.addEffect(effect, source); } } diff --git a/Mage.Sets/src/mage/cards/r/RagsRiches.java b/Mage.Sets/src/mage/cards/r/RagsRiches.java index 8845d277936..25443012c5e 100644 --- a/Mage.Sets/src/mage/cards/r/RagsRiches.java +++ b/Mage.Sets/src/mage/cards/r/RagsRiches.java @@ -86,8 +86,8 @@ class RichesEffect extends OneShotEffect { // Has to be done as a separate loop in case there's a situation where one creature's // controller depends on another creatures controller. for (UUID target : creaturesToSteal) { - GainControlTargetEffect eff = new GainControlTargetEffect(Duration.Custom, true); - eff.setTargetPointer(new FixedTarget(target)); + GainControlTargetEffect eff = new GainControlTargetEffect(Duration.EndOfGame, true); + eff.setTargetPointer(new FixedTarget(target, game)); game.addEffect(eff, source); } diff --git a/Mage.Sets/src/mage/cards/r/RaiyuuStormsEdge.java b/Mage.Sets/src/mage/cards/r/RaiyuuStormsEdge.java new file mode 100644 index 00000000000..cb8d100c8c2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RaiyuuStormsEdge.java @@ -0,0 +1,69 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksAloneControlledTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.AdditionalCombatPhaseEffect; +import mage.abilities.effects.common.UntapTargetEffect; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.TurnPhase; +import mage.filter.StaticFilters; +import mage.game.Game; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RaiyuuStormsEdge extends CardImpl { + + public RaiyuuStormsEdge(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SAMURAI); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // First strike + this.addAbility(FirstStrikeAbility.getInstance()); + + // Whenever a Samurai or Warrior you control attacks alone, untap it. If it's the first combat phase of the turn, there is an additional combat phase after this phase. + Ability ability = new AttacksAloneControlledTriggeredAbility( + new UntapTargetEffect().setText("untap it"), + StaticFilters.FILTER_CONTROLLED_SAMURAI_OR_WARRIOR, + true, false + ); + ability.addEffect(new ConditionalOneShotEffect( + new AdditionalCombatPhaseEffect(), RaiyuuStormsEdgeCondition.instance, + "If it's the first combat phase of the turn, there is an additional combat phase after this phase" + )); + this.addAbility(ability); + } + + private RaiyuuStormsEdge(final RaiyuuStormsEdge card) { + super(card); + } + + @Override + public RaiyuuStormsEdge copy() { + return new RaiyuuStormsEdge(this); + } +} + +enum RaiyuuStormsEdgeCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + return game.getTurn().getPhase(TurnPhase.COMBAT).getCount() == 0; + } +} diff --git a/Mage.Sets/src/mage/cards/r/RakdosRiteknife.java b/Mage.Sets/src/mage/cards/r/RakdosRiteknife.java index be552ca2a91..e0cb8a67a82 100644 --- a/Mage.Sets/src/mage/cards/r/RakdosRiteknife.java +++ b/Mage.Sets/src/mage/cards/r/RakdosRiteknife.java @@ -1,7 +1,5 @@ package mage.cards.r; -import java.util.UUID; - import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; @@ -9,50 +7,51 @@ import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.CountersSourceCount; -import mage.abilities.dynamicvalue.common.StaticValue; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.common.SacrificeEffect; -import mage.abilities.effects.common.continuous.BoostEquippedEffect; -import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.abilities.keyword.EquipAbility; -import mage.constants.*; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.*; import mage.counters.CounterType; import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetPlayer; import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; import static mage.filter.StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT; /** - * - * @author noahg + * @author TheElk801 */ public final class RakdosRiteknife extends CardImpl { + private static final DynamicValue xValue = new CountersSourceCount(CounterType.BLOOD); + public RakdosRiteknife(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); - + this.subtype.add(SubType.EQUIPMENT); // Equipped creature gets +1/+0 for each blood counter on Rakdos Riteknife and has "{T}, Sacrifice a creature: Put a blood counter on Rakdos Riteknife." - SimpleStaticAbility staticAbility = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(new CountersSourceCount(CounterType.BLOOD), StaticValue.get(0)).setText("Equipped creature gets +1/+0 for each blood counter on {this}")); - SimpleActivatedAbility grantedAbility = new SimpleActivatedAbility(new RakdosRiteKnifeEffect(this.getId()), new TapSourceCost()); - grantedAbility.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(FILTER_CONTROLLED_CREATURE_SHORT_TEXT))); - staticAbility.addEffect(new GainAbilityAttachedEffect(grantedAbility, AttachmentType.EQUIPMENT).setText("and has \"{T}, Sacrifice a creature: Put a blood counter on {this}.\"")); - this.addAbility(staticAbility); + this.addAbility(new SimpleStaticAbility(new RakdosRiteknifeEffect())); // {B}{R}, Sacrifice Rakdos Riteknife: Target player sacrifices a permanent for each blood counter on Rakdos Riteknife. - SimpleActivatedAbility activatedAbility = new SimpleActivatedAbility( - new SacrificeEffect(StaticFilters.FILTER_PERMANENT, new CountersSourceCount(CounterType.BLOOD), "Target player") - .setText("target player sacrifices a permanent for each blood counter on {this}"), new ManaCostsImpl("{R}{B}")); - activatedAbility.addCost(new SacrificeSourceCost()); - activatedAbility.addTarget(new TargetPlayer()); - this.addAbility(activatedAbility); + Ability ability = new SimpleActivatedAbility( + new SacrificeEffect(StaticFilters.FILTER_PERMANENT, xValue, "Target player") + .setText("target player sacrifices a permanent for each blood counter on {this}"), + new ManaCostsImpl<>("{R}{B}") + ); + ability.addCost(new SacrificeSourceCost()); + ability.addTarget(new TargetPlayer()); + this.addAbility(ability); // Equip {2} this.addAbility(new EquipAbility(2)); @@ -68,34 +67,69 @@ public final class RakdosRiteknife extends CardImpl { } } -class RakdosRiteKnifeEffect extends OneShotEffect { +class RakdosRiteknifeEffect extends ContinuousEffectImpl { - private UUID effectGivingEquipmentId; - - public RakdosRiteKnifeEffect(UUID effectGivingEquipmentId) { - super(Outcome.Benefit); - this.effectGivingEquipmentId = effectGivingEquipmentId; - staticText = "Put a blood counter on Rakdos Riteknife"; + RakdosRiteknifeEffect() { + super(Duration.WhileOnBattlefield, Outcome.AddAbility); + staticText = "equipped creature gets +1/+0 for each blood counter on {this} " + + "and has \"{T}, Sacrifice a creature: Put a blood counter on {this}.\""; } - public RakdosRiteKnifeEffect(final RakdosRiteKnifeEffect effect) { + private RakdosRiteknifeEffect(final RakdosRiteknifeEffect effect) { super(effect); - this.effectGivingEquipmentId = effect.effectGivingEquipmentId; } @Override public boolean apply(Game game, Ability source) { - Permanent equipment = game.getPermanent(this.effectGivingEquipmentId); - if (equipment != null) { - equipment.addCounters(CounterType.BLOOD.createInstance(), source.getControllerId(), source, game); - } - return true; + return false; } @Override - public RakdosRiteKnifeEffect copy() { - return new RakdosRiteKnifeEffect(this); + public RakdosRiteknifeEffect copy() { + return new RakdosRiteknifeEffect(this); } + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (permanent == null) { + return false; + } + Permanent creature = game.getPermanent(permanent.getAttachedTo()); + if (creature == null) { + return false; + } + switch (layer) { + case AbilityAddingRemovingEffects_6: + creature.addAbility(makeAbility(permanent, game), source.getSourceId(), game); + return true; + case PTChangingEffects_7: + if (sublayer != SubLayer.ModifyPT_7c) { + return false; + } + int count = permanent.getCounters(game).getCount(CounterType.BLOOD); + if (count > 0) { + creature.addPower(count); + return true; + } + } + return false; + } -} \ No newline at end of file + @Override + public boolean hasLayer(Layer layer) { + return layer == Layer.AbilityAddingRemovingEffects_6 + || layer == Layer.PTChangingEffects_7; + } + + private static Ability makeAbility(Permanent permanent, Game game) { + Ability ability = new SimpleActivatedAbility( + new AddCountersTargetEffect(CounterType.BLOOD.createInstance()) + .setText("put a blood counter on " + permanent.getName()) + .setTargetPointer(new FixedTarget(permanent, game)), + new TapSourceCost() + ); + ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(FILTER_CONTROLLED_CREATURE_SHORT_TEXT))); + return ability; + } +} diff --git a/Mage.Sets/src/mage/cards/r/RakingCanopy.java b/Mage.Sets/src/mage/cards/r/RakingCanopy.java index 848687ad96f..99684f0b71d 100644 --- a/Mage.Sets/src/mage/cards/r/RakingCanopy.java +++ b/Mage.Sets/src/mage/cards/r/RakingCanopy.java @@ -1,4 +1,3 @@ - package mage.cards.r; import java.util.UUID; @@ -12,7 +11,6 @@ import mage.constants.CardType; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; @@ -23,8 +21,7 @@ import mage.target.targetpointer.FixedTarget; public final class RakingCanopy extends CardImpl { public RakingCanopy(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{G}{G}"); - + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{G}{G}"); // Whenever a creature with flying attacks you, Raking Canopy deals 4 damage to it. this.addAbility(new RakingCanopyTriggeredAbility()); @@ -63,12 +60,13 @@ class RakingCanopyTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { Permanent attacker = game.getPermanent(event.getSourceId()); - if (attacker == null || !attacker.getAbilities().contains(FlyingAbility.getInstance())) { + if (attacker == null + || !attacker.getAbilities().contains(FlyingAbility.getInstance())) { return false; } if (event.getTargetId().equals(this.getControllerId())) { for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(attacker.getId())); + effect.setTargetPointer(new FixedTarget(attacker.getId(), game)); } return true; } diff --git a/Mage.Sets/src/mage/cards/r/RalCallerOfStorms.java b/Mage.Sets/src/mage/cards/r/RalCallerOfStorms.java index a0759f424a2..efa91873583 100644 --- a/Mage.Sets/src/mage/cards/r/RalCallerOfStorms.java +++ b/Mage.Sets/src/mage/cards/r/RalCallerOfStorms.java @@ -3,7 +3,6 @@ package mage.cards.r; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.common.DamageAllEffect; import mage.abilities.effects.common.DamageMultiEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; @@ -28,7 +27,7 @@ public final class RalCallerOfStorms extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.RAL); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Draw a card. this.addAbility(new LoyaltyAbility( diff --git a/Mage.Sets/src/mage/cards/r/RalIzzetViceroy.java b/Mage.Sets/src/mage/cards/r/RalIzzetViceroy.java index afd6b54e811..9839f1948ad 100644 --- a/Mage.Sets/src/mage/cards/r/RalIzzetViceroy.java +++ b/Mage.Sets/src/mage/cards/r/RalIzzetViceroy.java @@ -2,7 +2,6 @@ package mage.cards.r; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.dynamicvalue.common.InstantSorceryExileGraveyardCount; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.common.DamageTargetEffect; @@ -36,7 +35,7 @@ public final class RalIzzetViceroy extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.RAL); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +1: Look at the top two cards of your library. Put one of them into your hand and the other into your graveyard. this.addAbility(new LoyaltyAbility( diff --git a/Mage.Sets/src/mage/cards/r/RalStormConduit.java b/Mage.Sets/src/mage/cards/r/RalStormConduit.java index 65049ed91fe..caa6a54e3db 100644 --- a/Mage.Sets/src/mage/cards/r/RalStormConduit.java +++ b/Mage.Sets/src/mage/cards/r/RalStormConduit.java @@ -3,7 +3,6 @@ package mage.cards.r; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.LoyaltyAbility; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.CopyTargetSpellEffect; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; @@ -30,13 +29,13 @@ public final class RalStormConduit extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.RAL); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // Whenever you cast or copy an instant or sorcery spell, Ral, Storm Conduit deals 1 damage to target opponent or planeswalker. this.addAbility(new RalStormConduitTriggeredAbility()); // +2: Scry 1. - this.addAbility(new LoyaltyAbility(new ScryEffect(1), 2)); + this.addAbility(new LoyaltyAbility(new ScryEffect(1, false), 2)); // -2: When you cast your next instant or sorcery spell this turn, copy that spell. You may choose new targets for the copy. this.addAbility(new LoyaltyAbility(new CreateDelayedTriggeredAbilityEffect( diff --git a/Mage.Sets/src/mage/cards/r/RalZarek.java b/Mage.Sets/src/mage/cards/r/RalZarek.java index edfebbc8438..bbc6520f49c 100644 --- a/Mage.Sets/src/mage/cards/r/RalZarek.java +++ b/Mage.Sets/src/mage/cards/r/RalZarek.java @@ -4,7 +4,6 @@ package mage.cards.r; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageTargetEffect; @@ -42,7 +41,7 @@ public final class RalZarek extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.RAL); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Tap target permanent, then untap another target permanent. LoyaltyAbility ability1 = new LoyaltyAbility(new TapTargetEffect(), 1); diff --git a/Mage.Sets/src/mage/cards/r/Rally.java b/Mage.Sets/src/mage/cards/r/Rally.java index 414bbb2b4d6..69187bbae9f 100644 --- a/Mage.Sets/src/mage/cards/r/Rally.java +++ b/Mage.Sets/src/mage/cards/r/Rally.java @@ -1,4 +1,3 @@ - package mage.cards.r; import java.util.UUID; @@ -7,7 +6,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.common.FilterBlockingCreature; +import mage.filter.StaticFilters; /** * @@ -19,8 +18,7 @@ public final class Rally extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{W}{W}"); // Blocking creatures get +1/+1 until end of turn. - this.getSpellAbility().addEffect(new BoostAllEffect(1, 1, Duration.EndOfTurn, - new FilterBlockingCreature("blocking creatures"), false)); + this.getSpellAbility().addEffect(new BoostAllEffect(1, 1, Duration.EndOfTurn, StaticFilters.FILTER_BLOCKING_CREATURES, false)); } private Rally(final Rally card) { diff --git a/Mage.Sets/src/mage/cards/r/RallyTheForces.java b/Mage.Sets/src/mage/cards/r/RallyTheForces.java index 2ca20f386ca..dc22f0b6076 100644 --- a/Mage.Sets/src/mage/cards/r/RallyTheForces.java +++ b/Mage.Sets/src/mage/cards/r/RallyTheForces.java @@ -1,5 +1,3 @@ - - package mage.cards.r; import java.util.UUID; @@ -11,7 +9,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.common.FilterAttackingCreature; +import mage.filter.StaticFilters; /** * @@ -23,10 +21,10 @@ public final class RallyTheForces extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{R}"); // Attacking creatures get +1/+0 and gain first strike until end of turn. - Effect effect = new BoostAllEffect(1, 0, Duration.EndOfTurn, new FilterAttackingCreature("Attacking creatures"), false); + Effect effect = new BoostAllEffect(1, 0, Duration.EndOfTurn, StaticFilters.FILTER_ATTACKING_CREATURES, false); effect.setText("Attacking creatures get +1/+0"); this.getSpellAbility().addEffect(effect); - effect = new GainAbilityAllEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn, new FilterAttackingCreature("Attacking creatures"), false); + effect = new GainAbilityAllEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_ATTACKING_CREATURES, false); effect.setText("and gain first strike until end of turn"); this.getSpellAbility().addEffect(effect); } diff --git a/Mage.Sets/src/mage/cards/r/RampagingFerocidon.java b/Mage.Sets/src/mage/cards/r/RampagingFerocidon.java index 1704036e3e9..643d0b355bb 100644 --- a/Mage.Sets/src/mage/cards/r/RampagingFerocidon.java +++ b/Mage.Sets/src/mage/cards/r/RampagingFerocidon.java @@ -45,7 +45,7 @@ public final class RampagingFerocidon extends CardImpl { this.toughness = new MageInt(3); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // Players can't gain life. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new RampagingFerocidonEffect())); diff --git a/Mage.Sets/src/mage/cards/r/RampagingMonument.java b/Mage.Sets/src/mage/cards/r/RampagingMonument.java index 936551a686c..d28d37d0523 100644 --- a/Mage.Sets/src/mage/cards/r/RampagingMonument.java +++ b/Mage.Sets/src/mage/cards/r/RampagingMonument.java @@ -32,7 +32,7 @@ public final class RampagingMonument extends CardImpl { // Rampaging Monument enters the battlefield with three +1/+1 counters on it. this.addAbility(new EntersBattlefieldAbility( new AddCountersSourceEffect(CounterType.P1P1.createInstance(3)), - "{this} enters the battlefield with three +1/+1 counters on it" + "with three +1/+1 counters on it" )); // Whenever you cast a multicolored spell, put a +1/+1 counter on Rampaging Monument. diff --git a/Mage.Sets/src/mage/cards/r/RampagingWerewolf.java b/Mage.Sets/src/mage/cards/r/RampagingWerewolf.java index 93cbf1d62ad..b31735e695b 100644 --- a/Mage.Sets/src/mage/cards/r/RampagingWerewolf.java +++ b/Mage.Sets/src/mage/cards/r/RampagingWerewolf.java @@ -21,7 +21,6 @@ public final class RampagingWerewolf extends CardImpl { // this card is the second face of double-faced card this.nightCard = true; - this.transformable = true; this.power = new MageInt(6); this.toughness = new MageInt(4); diff --git a/Mage.Sets/src/mage/cards/r/RampantRejuvenator.java b/Mage.Sets/src/mage/cards/r/RampantRejuvenator.java new file mode 100644 index 00000000000..5d1aba22a98 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RampantRejuvenator.java @@ -0,0 +1,99 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.MageItem; +import mage.abilities.Ability; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCardInLibrary; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RampantRejuvenator extends CardImpl { + + public RampantRejuvenator(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}"); + + this.subtype.add(SubType.PLANT); + this.subtype.add(SubType.HYDRA); + this.power = new MageInt(0); + this.toughness = new MageInt(0); + + // Rampant Rejuvenator enters the battlefield with two +1/+1 counters on it. + this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect( + CounterType.P1P1.createInstance(2) + ), "with two +1/+1 counters on it")); + + // When Rampant Rejuvenator dies, search your library for up to X basic land cards, where X is Rampant Rejuvenator's power, put them onto the battlefield, then shuffle. + this.addAbility(new DiesSourceTriggeredAbility(new RampantRejuvenatorEffect())); + } + + private RampantRejuvenator(final RampantRejuvenator card) { + super(card); + } + + @Override + public RampantRejuvenator copy() { + return new RampantRejuvenator(this); + } +} + +class RampantRejuvenatorEffect extends OneShotEffect { + + RampantRejuvenatorEffect() { + super(Outcome.Benefit); + staticText = "search your library for up to X basic land cards, " + + "where X is {this}'s power, put them onto the battlefield, then shuffle"; + } + + private RampantRejuvenatorEffect(final RampantRejuvenatorEffect effect) { + super(effect); + } + + @Override + public RampantRejuvenatorEffect copy() { + return new RampantRejuvenatorEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Permanent permanent = source.getSourcePermanentOrLKI(game); + if (player == null || permanent == null) { + return false; + } + TargetCardInLibrary target = new TargetCardInLibrary( + 0, permanent.getPower().getValue(), + StaticFilters.FILTER_CARD_BASIC_LANDS + ); + player.searchLibrary(target, source, game); + Cards cards = new CardsImpl(); + player.getLibrary() + .getCards(game) + .stream() + .map(MageItem::getId) + .filter(target.getTargets()::contains) + .forEach(cards::add); + player.moveCards(cards, Zone.BATTLEFIELD, source, game); + player.shuffleLibrary(source, game); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/r/RaugrinTriome.java b/Mage.Sets/src/mage/cards/r/RaugrinTriome.java index 8953e63b1a8..975242485bb 100644 --- a/Mage.Sets/src/mage/cards/r/RaugrinTriome.java +++ b/Mage.Sets/src/mage/cards/r/RaugrinTriome.java @@ -1,7 +1,7 @@ package mage.cards.r; import mage.abilities.common.EntersBattlefieldTappedAbility; -import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.keyword.CyclingAbility; import mage.abilities.mana.BlueManaAbility; import mage.abilities.mana.RedManaAbility; @@ -34,7 +34,7 @@ public final class RaugrinTriome extends CardImpl { this.addAbility(new EntersBattlefieldTappedAbility()); // Cycling {3} - this.addAbility(new CyclingAbility(new ManaCostsImpl("{3}"))); + this.addAbility(new CyclingAbility(new GenericManaCost(3))); } private RaugrinTriome(final RaugrinTriome card) { diff --git a/Mage.Sets/src/mage/cards/r/RavagerOfTheFells.java b/Mage.Sets/src/mage/cards/r/RavagerOfTheFells.java index 8b4c020a10f..75d0f2abc71 100644 --- a/Mage.Sets/src/mage/cards/r/RavagerOfTheFells.java +++ b/Mage.Sets/src/mage/cards/r/RavagerOfTheFells.java @@ -3,7 +3,7 @@ package mage.cards.r; import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.TransformIntoSourceTriggeredAbility; import mage.abilities.common.WerewolfBackTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.TrampleAbility; @@ -12,14 +12,11 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; import mage.filter.StaticFilters; import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.game.stack.StackObject; import mage.players.Player; -import mage.target.Target; import mage.target.TargetPermanent; import mage.target.common.TargetOpponentOrPlaneswalker; @@ -40,7 +37,6 @@ public final class RavagerOfTheFells extends CardImpl { // this card is the second face of double-faced card this.nightCard = true; - this.transformable = true; this.power = new MageInt(4); this.toughness = new MageInt(4); @@ -48,7 +44,12 @@ public final class RavagerOfTheFells extends CardImpl { this.addAbility(TrampleAbility.getInstance()); // Whenever this creature transforms into Ravager of the Fells, it deals 2 damage to target opponent and 2 damage to up to one target creature that player controls. - this.addAbility(new RavagerOfTheFellsAbility()); + Ability ability = new TransformIntoSourceTriggeredAbility( + new RavagerOfTheFellsEffect(), false, true + ); + ability.addTarget(new TargetOpponentOrPlaneswalker()); + ability.addTarget(new RavagerOfTheFellsTarget()); + this.addAbility(ability); // At the beginning of each upkeep, if a player cast two or more spells last turn, transform Ravager of the Fells. this.addAbility(new WerewolfBackTriggeredAbility()); @@ -64,53 +65,12 @@ public final class RavagerOfTheFells extends CardImpl { } } -class RavagerOfTheFellsAbility extends TriggeredAbilityImpl { - - RavagerOfTheFellsAbility() { - super(Zone.BATTLEFIELD, new RavagerOfTheFellsEffect(), false); - Target target1 = new TargetOpponentOrPlaneswalker(); - this.addTarget(target1); - this.addTarget(new RavagerOfTheFellsTarget()); - } - - private RavagerOfTheFellsAbility(final RavagerOfTheFellsAbility ability) { - super(ability); - } - - @Override - public RavagerOfTheFellsAbility copy() { - return new RavagerOfTheFellsAbility(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 "Whenever this creature transforms into {this}, " - + "it deals 2 damage to target opponent or planeswalker " - + "and 2 damage to up to one target creature that player or that planeswalker's controller controls."; - } - -} - class RavagerOfTheFellsEffect extends OneShotEffect { RavagerOfTheFellsEffect() { super(Outcome.Damage); + staticText = "it deals 2 damage to target opponent or planeswalker and 2 damage " + + "to up to one target creature that player or that planeswalker's controller controls."; } private RavagerOfTheFellsEffect(final RavagerOfTheFellsEffect effect) { diff --git a/Mage.Sets/src/mage/cards/r/RavenousChupacabra.java b/Mage.Sets/src/mage/cards/r/RavenousChupacabra.java index 5fc6ea562e1..32fad1fc866 100644 --- a/Mage.Sets/src/mage/cards/r/RavenousChupacabra.java +++ b/Mage.Sets/src/mage/cards/r/RavenousChupacabra.java @@ -10,8 +10,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; /** @@ -20,12 +19,6 @@ import mage.target.common.TargetCreaturePermanent; */ public final class RavenousChupacabra extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public RavenousChupacabra(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{B}{B}"); this.subtype.add(SubType.BEAST); @@ -35,7 +28,7 @@ public final class RavenousChupacabra extends CardImpl { // When Ravenous Chupacabra enters the battlefield, destroy target creature an opponent controls. Ability ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect()); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/r/RavenousDemon.java b/Mage.Sets/src/mage/cards/r/RavenousDemon.java index 59e53fbb941..d4f60829f80 100644 --- a/Mage.Sets/src/mage/cards/r/RavenousDemon.java +++ b/Mage.Sets/src/mage/cards/r/RavenousDemon.java @@ -30,7 +30,6 @@ public final class RavenousDemon extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{B}"); this.subtype.add(SubType.DEMON); - this.transformable = true; this.secondSideCardClazz = mage.cards.a.ArchdemonOfGreed.class; this.power = new MageInt(4); @@ -38,7 +37,7 @@ public final class RavenousDemon extends CardImpl { // Sacrifice a Human: Transform Ravenous Demon. Activate this ability only any time you could cast a sorcery. this.addAbility(new TransformAbility()); - this.addAbility(new ActivateAsSorceryActivatedAbility(Zone.BATTLEFIELD, new TransformSourceEffect(true), new SacrificeTargetCost(new TargetControlledPermanent(filter)))); + this.addAbility(new ActivateAsSorceryActivatedAbility(Zone.BATTLEFIELD, new TransformSourceEffect(), new SacrificeTargetCost(new TargetControlledPermanent(filter)))); } private RavenousDemon(final RavenousDemon card) { diff --git a/Mage.Sets/src/mage/cards/r/RayOfCommand.java b/Mage.Sets/src/mage/cards/r/RayOfCommand.java index b33ad55ca92..1823b23cacd 100644 --- a/Mage.Sets/src/mage/cards/r/RayOfCommand.java +++ b/Mage.Sets/src/mage/cards/r/RayOfCommand.java @@ -13,11 +13,9 @@ 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.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.target.common.TargetCreaturePermanent; /** @@ -25,24 +23,16 @@ import mage.target.common.TargetCreaturePermanent; * @author LevelX2 */ public final class RayOfCommand extends CardImpl { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } public RayOfCommand(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{3}{U}"); - // Untap target creature an opponent controls and gain control of it until end of turn. That creature gains haste until end of turn. When you lose control of the creature, tap it. this.getSpellAbility().addEffect(new UntapTargetEffect()); this.getSpellAbility().addEffect(new GainControlTargetEffect(Duration.EndOfTurn)); this.getSpellAbility().addEffect(new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn)); this.getSpellAbility().addEffect(new CreateDelayedTriggeredAbilityEffect(new RayOfCommandDelayedTriggeredAbility(), true)); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); - + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); } private RayOfCommand(final RayOfCommand card) { diff --git a/Mage.Sets/src/mage/cards/r/RazorHippogriff.java b/Mage.Sets/src/mage/cards/r/RazorHippogriff.java index c4f2c1dc119..b067eb6b062 100644 --- a/Mage.Sets/src/mage/cards/r/RazorHippogriff.java +++ b/Mage.Sets/src/mage/cards/r/RazorHippogriff.java @@ -1,5 +1,3 @@ - - package mage.cards.r; import java.util.UUID; @@ -8,7 +6,6 @@ import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; -import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.Card; import mage.cards.CardImpl; @@ -16,11 +13,9 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Outcome; -import mage.constants.Zone; import mage.filter.common.FilterArtifactCard; import mage.game.Game; import mage.players.Player; -import mage.target.TargetCard; import mage.target.common.TargetCardInYourGraveyard; /** @@ -29,24 +24,25 @@ import mage.target.common.TargetCardInYourGraveyard; */ public final class RazorHippogriff extends CardImpl { - public RazorHippogriff (UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{W}{W}"); + private static final FilterArtifactCard filter = new FilterArtifactCard("artifact card from your graveyard"); + + public RazorHippogriff(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}{W}"); this.subtype.add(SubType.HIPPOGRIFF); this.power = new MageInt(3); - this.toughness = new MageInt(3); + this.toughness = new MageInt(3); this.addAbility(FlyingAbility.getInstance()); Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect()); - TargetCard target = new TargetCardInYourGraveyard(new FilterArtifactCard("artifact card from your graveyard")); - ability.addTarget(target); + ability.addTarget(new TargetCardInYourGraveyard(filter)); ability.addEffect(new RazorHippogriffGainLifeEffect()); this.addAbility(ability); } - public RazorHippogriff (final RazorHippogriff card) { + private RazorHippogriff(final RazorHippogriff card) { super(card); } @@ -54,38 +50,32 @@ public final class RazorHippogriff extends CardImpl { public RazorHippogriff copy() { return new RazorHippogriff(this); } +} - public final class RazorHippogriffGainLifeEffect extends OneShotEffect { - - public RazorHippogriffGainLifeEffect() { - super(Outcome.GainLife); - staticText = "you gain life equal to that card's mana value."; - } - - public RazorHippogriffGainLifeEffect(final RazorHippogriffGainLifeEffect effect) { - super(effect); - } - - @Override - public RazorHippogriffGainLifeEffect copy() { - return new RazorHippogriffGainLifeEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - Card card = player.getGraveyard().get(source.getFirstTarget(), game); - if (card == null) { - card = (Card)game.getLastKnownInformation(source.getFirstTarget(), Zone.GRAVEYARD); - } - if (card != null) { - player.gainLife(card.getManaValue(), game, source); - } - } - return true; - } +class RazorHippogriffGainLifeEffect extends OneShotEffect { + public RazorHippogriffGainLifeEffect() { + super(Outcome.GainLife); + staticText = "you gain life equal to that card's mana value."; } + private RazorHippogriffGainLifeEffect(final RazorHippogriffGainLifeEffect effect) { + super(effect); + } + + @Override + public RazorHippogriffGainLifeEffect copy() { + return new RazorHippogriffGainLifeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Card card = game.getCard(source.getFirstTarget()); + if (player != null && card != null) { + player.gainLife(card.getManaValue(), game, source); + return true; + } + return false; + } } diff --git a/Mage.Sets/src/mage/cards/r/RealityHeist.java b/Mage.Sets/src/mage/cards/r/RealityHeist.java new file mode 100644 index 00000000000..5df4ca20370 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RealityHeist.java @@ -0,0 +1,51 @@ +package mage.cards.r; + +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.common.ArtifactYouControlCount; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.common.LookLibraryAndPickControllerEffect; +import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect; +import mage.abilities.hint.common.ArtifactYouControlHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RealityHeist extends CardImpl { + + public RealityHeist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{5}{U}{U}"); + + // This spell costs {1} less to cast for each artifact you control. + this.addAbility(new SimpleStaticAbility(Zone.ALL, + new SpellCostReductionForEachSourceEffect(1, ArtifactYouControlCount.instance) + ).addHint(ArtifactYouControlHint.instance)); + + // Look at the top seven cards of your library. You may reveal up to two artifact cards from among them and put them into your hand. Put the rest on the bottom of your library in a random order. + this.getSpellAbility().addEffect( + new LookLibraryAndPickControllerEffect( + StaticValue.get(5), false, StaticValue.get(1), + StaticFilters.FILTER_CARD_ARTIFACT, Zone.LIBRARY, false, true, + false, Zone.HAND, true, false, false + ).setBackInRandomOrder(true) + .setText("look at the top seven cards of your library. You may reveal up to " + + "two artifact cards from among them and put them into your hand. " + + "Put the rest on the bottom of your library in a random order") + ); + } + + private RealityHeist(final RealityHeist card) { + super(card); + } + + @Override + public RealityHeist copy() { + return new RealityHeist(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/ReaverDrone.java b/Mage.Sets/src/mage/cards/r/ReaverDrone.java index 91699d555c0..457909fbb30 100644 --- a/Mage.Sets/src/mage/cards/r/ReaverDrone.java +++ b/Mage.Sets/src/mage/cards/r/ReaverDrone.java @@ -45,7 +45,7 @@ public final class ReaverDrone extends CardImpl { // At the beginning of your upkeep, you lose 1 life unless you control another colorless creature. this.addAbility(new BeginningOfUpkeepTriggeredAbility(new ConditionalOneShotEffect( new LoseLifeSourceControllerEffect(1), - new InvertCondition(new PermanentsOnTheBattlefieldCondition(filter, ComparisonType.MORE_THAN, 0, true)), + new InvertCondition(new PermanentsOnTheBattlefieldCondition(filter)), "you lose 1 life unless you control another colorless creature"), TargetController.YOU, false)); } diff --git a/Mage.Sets/src/mage/cards/r/Rebuke.java b/Mage.Sets/src/mage/cards/r/Rebuke.java index 1acef97f6ce..555aabe5d56 100644 --- a/Mage.Sets/src/mage/cards/r/Rebuke.java +++ b/Mage.Sets/src/mage/cards/r/Rebuke.java @@ -1,4 +1,3 @@ - package mage.cards.r; import java.util.UUID; @@ -6,8 +5,7 @@ import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterAttackingCreature; -import mage.target.common.TargetCreaturePermanent; +import mage.target.common.TargetAttackingCreature; /** * @@ -20,7 +18,7 @@ public final class Rebuke extends CardImpl { // Destroy target attacking creature. - this.getSpellAbility().addTarget(new TargetCreaturePermanent(new FilterAttackingCreature())); + this.getSpellAbility().addTarget(new TargetAttackingCreature()); this.getSpellAbility().addEffect(new DestroyTargetEffect()); } diff --git a/Mage.Sets/src/mage/cards/r/RecklessImpulse.java b/Mage.Sets/src/mage/cards/r/RecklessImpulse.java new file mode 100644 index 00000000000..86130163393 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RecklessImpulse.java @@ -0,0 +1,33 @@ +package mage.cards.r; + +import mage.abilities.effects.common.ExileTopXMayPlayUntilEndOfTurnEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RecklessImpulse extends CardImpl { + + public RecklessImpulse(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{R}"); + + // Exile the top two cards of your library. Until the end of your next turn, you may play those cards. + this.getSpellAbility().addEffect(new ExileTopXMayPlayUntilEndOfTurnEffect( + 2, false, Duration.UntilEndOfYourNextTurn + )); + } + + private RecklessImpulse(final RecklessImpulse card) { + super(card); + } + + @Override + public RecklessImpulse copy() { + return new RecklessImpulse(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RecklessOgre.java b/Mage.Sets/src/mage/cards/r/RecklessOgre.java index def2b8f40b9..f351fd85129 100644 --- a/Mage.Sets/src/mage/cards/r/RecklessOgre.java +++ b/Mage.Sets/src/mage/cards/r/RecklessOgre.java @@ -3,7 +3,7 @@ package mage.cards.r; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.AttacksAloneTriggeredAbility; +import mage.abilities.common.AttacksAloneSourceTriggeredAbility; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -24,7 +24,7 @@ public final class RecklessOgre extends CardImpl { this.toughness = new MageInt(2); // Whenever Reckless Ogre attacks alone, it gets +3/+0 until end of turn. - this.addAbility(new AttacksAloneTriggeredAbility(new BoostSourceEffect(3, 0, Duration.EndOfTurn).setText("it gets +3/+0 until end of turn"))); + this.addAbility(new AttacksAloneSourceTriggeredAbility(new BoostSourceEffect(3, 0, Duration.EndOfTurn).setText("it gets +3/+0 until end of turn"))); } private RecklessOgre(final RecklessOgre card) { diff --git a/Mage.Sets/src/mage/cards/r/RecklessSpite.java b/Mage.Sets/src/mage/cards/r/RecklessSpite.java index f6e6eb192e5..280e0a92109 100644 --- a/Mage.Sets/src/mage/cards/r/RecklessSpite.java +++ b/Mage.Sets/src/mage/cards/r/RecklessSpite.java @@ -1,15 +1,11 @@ - package mage.cards.r; -import mage.ObjectColor; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.LoseLifeSourceControllerEffect; 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; @@ -20,17 +16,11 @@ import java.util.UUID; */ public final class RecklessSpite extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creatures"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public RecklessSpite(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{B}{B}"); this.getSpellAbility().addEffect(new DestroyTargetEffect()); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(2, 2, filter, false)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(2, 2, StaticFilters.FILTER_PERMANENT_CREATURES_NON_BLACK, false)); this.getSpellAbility().addEffect(new LoseLifeSourceControllerEffect(5)); } diff --git a/Mage.Sets/src/mage/cards/r/RecklessStormseeker.java b/Mage.Sets/src/mage/cards/r/RecklessStormseeker.java index 8b78f15c067..20f1d2838b0 100644 --- a/Mage.Sets/src/mage/cards/r/RecklessStormseeker.java +++ b/Mage.Sets/src/mage/cards/r/RecklessStormseeker.java @@ -7,7 +7,6 @@ import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.keyword.DayboundAbility; import mage.abilities.keyword.HasteAbility; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -30,7 +29,6 @@ public final class RecklessStormseeker extends CardImpl { this.subtype.add(SubType.WEREWOLF); this.power = new MageInt(2); this.toughness = new MageInt(3); - this.transformable = true; this.secondSideCardClazz = mage.cards.s.StormChargedSlasher.class; // At the beginning of combat on your turn, target creature you control gets +1/+0 and gains haste until end of turn. @@ -46,7 +44,6 @@ public final class RecklessStormseeker extends CardImpl { this.addAbility(ability); // Daybound - this.addAbility(new TransformAbility()); this.addAbility(new DayboundAbility()); } diff --git a/Mage.Sets/src/mage/cards/r/RecklessWaif.java b/Mage.Sets/src/mage/cards/r/RecklessWaif.java index 848d23ac623..dae8b27df78 100644 --- a/Mage.Sets/src/mage/cards/r/RecklessWaif.java +++ b/Mage.Sets/src/mage/cards/r/RecklessWaif.java @@ -21,7 +21,6 @@ public final class RecklessWaif extends CardImpl { this.subtype.add(SubType.ROGUE); this.subtype.add(SubType.WEREWOLF); - this.transformable = true; this.secondSideCardClazz = mage.cards.m.MercilessPredator.class; this.power = new MageInt(1); diff --git a/Mage.Sets/src/mage/cards/r/ReckonerBankbuster.java b/Mage.Sets/src/mage/cards/r/ReckonerBankbuster.java new file mode 100644 index 00000000000..77bf08713c6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/ReckonerBankbuster.java @@ -0,0 +1,74 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.SourceHasCounterCondition; +import mage.abilities.costs.common.RemoveCountersSourceCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.CrewAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.game.permanent.token.PilotToken; +import mage.game.permanent.token.TreasureToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ReckonerBankbuster extends CardImpl { + + private static final Condition condition = new SourceHasCounterCondition(CounterType.CHARGE, 0, 0); + + public ReckonerBankbuster(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); + + this.subtype.add(SubType.VEHICLE); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Reckoner Bankbuster enters the battlefield with three charge counters on it. + this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect( + CounterType.CHARGE.createInstance(3) + ), "with three charge counters on it")); + + // {2}, {T}, Remove a charge counter from Reckoner Bankbuster: Draw a card. Then if there are no charge counters on Reckoner Bankbuster, create a Treasure token and a 1/1 colorless Pilot creature token with "This creature crews Vehicles as though its power were 2 greater." + Ability ability = new SimpleActivatedAbility( + new DrawCardSourceControllerEffect(1), new GenericManaCost(2) + ); + ability.addEffect(new ConditionalOneShotEffect( + new CreateTokenEffect(new TreasureToken()), condition, + "Then if there are no charge counters on {this}, create a Treasure token" + )); + ability.addEffect(new ConditionalOneShotEffect( + new CreateTokenEffect(new PilotToken()), condition, "and a 1/1 colorless Pilot creature token " + + "with \"This creature crews Vehicles as though its power were 2 greater.\"" + )); + ability.addCost(new TapSourceCost()); + ability.addCost(new RemoveCountersSourceCost(CounterType.CHARGE.createInstance())); + this.addAbility(ability); + + // Crew 3 + this.addAbility(new CrewAbility(3)); + } + + private ReckonerBankbuster(final ReckonerBankbuster card) { + super(card); + } + + @Override + public ReckonerBankbuster copy() { + return new ReckonerBankbuster(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/ReckonerShakedown.java b/Mage.Sets/src/mage/cards/r/ReckonerShakedown.java new file mode 100644 index 00000000000..e373efb09fd --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/ReckonerShakedown.java @@ -0,0 +1,103 @@ +package mage.cards.r; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.TargetPermanent; +import mage.target.common.TargetCardInHand; +import mage.target.common.TargetOpponent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ReckonerShakedown extends CardImpl { + + public ReckonerShakedown(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}"); + + // Target opponent reveals their hand. You may choose a nonland card from it. If you do, that player discards that card. If you don't, put two +1/+1 counters on a creature or Vehicle you control. + this.getSpellAbility().addEffect(new ReckonerShakedownEffect()); + this.getSpellAbility().addTarget(new TargetOpponent()); + } + + private ReckonerShakedown(final ReckonerShakedown card) { + super(card); + } + + @Override + public ReckonerShakedown copy() { + return new ReckonerShakedown(this); + } +} + +class ReckonerShakedownEffect extends OneShotEffect { + + private static final FilterPermanent filter = new FilterControlledPermanent("creature or Vehicle you control"); + + static { + filter.add(Predicates.or( + CardType.CREATURE.getPredicate(), + SubType.VEHICLE.getPredicate() + )); + } + + ReckonerShakedownEffect() { + super(Outcome.Benefit); + staticText = "target opponent reveals their hand. You may choose a nonland card from it. " + + "If you do, that player discards that card. If you don't, " + + "put two +1/+1 counters on a creature or Vehicle you control"; + } + + private ReckonerShakedownEffect(final ReckonerShakedownEffect effect) { + super(effect); + } + + @Override + public ReckonerShakedownEffect copy() { + return new ReckonerShakedownEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Player player = game.getPlayer(source.getFirstTarget()); + if (controller == null || player == null) { + return false; + } + player.revealCards(source, player.getHand(), game); + TargetCard target = new TargetCardInHand(0, 1, StaticFilters.FILTER_CARD_A_NON_LAND); + controller.choose(Outcome.Discard, player.getHand(), target, game); + Card card = game.getCard(target.getFirstTarget()); + if (card != null) { + player.discard(card, false, source, game); + return true; + } + if (!game.getBattlefield().contains(filter, source, game, 1)) { + return true; + } + TargetPermanent targetPermanent = new TargetPermanent(filter); + targetPermanent.setNotTarget(true); + player.choose(Outcome.BoostCreature, targetPermanent, source.getSourceId(), game); + Permanent permanent = game.getPermanent(targetPermanent.getFirstTarget()); + if (permanent != null) { + permanent.addCounters(CounterType.P1P1.createInstance(2), source, game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/r/ReckonersBargain.java b/Mage.Sets/src/mage/cards/r/ReckonersBargain.java new file mode 100644 index 00000000000..64a64c39a06 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/ReckonersBargain.java @@ -0,0 +1,84 @@ +package mage.cards.r; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.target.common.TargetControlledPermanent; +import mage.util.CardUtil; + +import java.util.Collection; +import java.util.Objects; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ReckonersBargain extends CardImpl { + + private static final FilterControlledPermanent filter + = new FilterControlledPermanent("an artifact or creature"); + + static { + filter.add(Predicates.or( + CardType.ARTIFACT.getPredicate(), + CardType.CREATURE.getPredicate() + )); + } + + public ReckonersBargain(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{B}"); + + // As an additional cost to cast this spell, sacrifice an artifact or creature. + this.getSpellAbility().addCost(new SacrificeTargetCost(new TargetControlledPermanent(filter))); + + // You gain life equal to the sacrificed permanent's mana value. Draw two cards. + this.getSpellAbility().addEffect(new GainLifeEffect( + ReckonersBargainValue.instance, "you gain life " + + "equal to the sacrificed permanent's mana value" + )); + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(2)); + } + + private ReckonersBargain(final ReckonersBargain card) { + super(card); + } + + @Override + public ReckonersBargain copy() { + return new ReckonersBargain(this); + } +} + +enum ReckonersBargainValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + return CardUtil.castStream(sourceAbility.getCosts().stream(), SacrificeTargetCost.class) + .map(SacrificeTargetCost::getPermanents) + .flatMap(Collection::stream) + .filter(Objects::nonNull) + .mapToInt(MageObject::getManaValue) + .sum(); + } + + @Override + public ReckonersBargainValue copy() { + return this; + } + + @Override + public String getMessage() { + return ""; + } +} diff --git a/Mage.Sets/src/mage/cards/r/ReclusiveTaxidermist.java b/Mage.Sets/src/mage/cards/r/ReclusiveTaxidermist.java new file mode 100644 index 00000000000..171040a89e4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/ReclusiveTaxidermist.java @@ -0,0 +1,60 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.CardsInControllerGraveyardCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.mana.AnyColorManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ReclusiveTaxidermist extends CardImpl { + + private static final Condition condition + = new CardsInControllerGraveyardCondition(4, StaticFilters.FILTER_CARD_CREATURE); + private static final Hint hint = new ValueHint( + "Creature cards in your graveyard", + new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_CREATURE) + ); + + public ReclusiveTaxidermist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.DRUID); + this.power = new MageInt(1); + this.toughness = new MageInt(2); + + // Reclusive Taxidermist gets +3/+2 as long as there are four or more creature cards in your graveyard. + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new BoostSourceEffect(3, 2, Duration.WhileOnBattlefield), + condition, "{this} gets +3/+2 as long as there are four or more creature cards in your graveyard" + )).addHint(hint)); + + // {T}: Add one mana of any color. + this.addAbility(new AnyColorManaAbility()); + } + + private ReclusiveTaxidermist(final ReclusiveTaxidermist card) { + super(card); + } + + @Override + public ReclusiveTaxidermist copy() { + return new ReclusiveTaxidermist(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/ReconnaissanceMission.java b/Mage.Sets/src/mage/cards/r/ReconnaissanceMission.java index a435aa9e8d3..6ffe195fe0e 100644 --- a/Mage.Sets/src/mage/cards/r/ReconnaissanceMission.java +++ b/Mage.Sets/src/mage/cards/r/ReconnaissanceMission.java @@ -23,7 +23,7 @@ public final class ReconnaissanceMission extends CardImpl { // Whenever a creature you control deals combat damage to a player, you may draw a card. this.addAbility(new DealsDamageToAPlayerAllTriggeredAbility( new DrawCardSourceControllerEffect(1), - StaticFilters.FILTER_CONTROLLED_CREATURE, + StaticFilters.FILTER_CONTROLLED_A_CREATURE, true, SetTargetPointer.NONE, true )); diff --git a/Mage.Sets/src/mage/cards/r/RedWard.java b/Mage.Sets/src/mage/cards/r/RedWard.java index d41d9e49bd8..7e789ea4185 100644 --- a/Mage.Sets/src/mage/cards/r/RedWard.java +++ b/Mage.Sets/src/mage/cards/r/RedWard.java @@ -1,10 +1,7 @@ - package mage.cards.r; -import java.util.UUID; import mage.ObjectColor; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; import mage.abilities.keyword.EnchantAbility; @@ -13,28 +10,20 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.AttachmentType; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; -import mage.constants.Zone; -import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.ColorPredicate; +import mage.constants.SubType; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LoneFox */ public final class RedWard extends CardImpl { - private static final FilterCard filter = new FilterCard("red"); - - static { - filter.add(new ColorPredicate(ObjectColor.RED)); - } - public RedWard(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}"); this.subtype.add(SubType.AURA); // Enchant creature @@ -42,12 +31,10 @@ public final class RedWard extends CardImpl { this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.Protect)); this.addAbility(new EnchantAbility(auraTarget.getTargetName())); - // Enchanted creature has protection from red. This effect doesn't remove Red Ward. - ProtectionAbility gainedAbility = new ProtectionAbility(filter); - gainedAbility.setAuraIdNotToBeRemoved(this.getId()); - Effect effect = new GainAbilityAttachedEffect(gainedAbility, AttachmentType.AURA); - effect.setText("Enchanted creature has protection from red. This effect doesn't remove {this}."); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); + + this.addAbility(new SimpleStaticAbility(new GainAbilityAttachedEffect( + ProtectionAbility.from(ObjectColor.RED), AttachmentType.AURA + ).setDoesntRemoveItself(true))); } private RedWard(final RedWard card) { diff --git a/Mage.Sets/src/mage/cards/r/ReflectionOfKikiJiki.java b/Mage.Sets/src/mage/cards/r/ReflectionOfKikiJiki.java new file mode 100644 index 00000000000..2a4dd547193 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/ReflectionOfKikiJiki.java @@ -0,0 +1,91 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenCopyTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.game.Game; +import mage.target.TargetPermanent; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ReflectionOfKikiJiki extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("another nonlegendary creature you control"); + + static { + filter.add(AnotherPredicate.instance); + filter.add(Predicates.not(SuperType.LEGENDARY.getPredicate())); + } + + public ReflectionOfKikiJiki(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, ""); + + this.subtype.add(SubType.GOBLIN); + this.subtype.add(SubType.SHAMAN); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + this.color.setRed(true); + this.nightCard = true; + + // {1}, {T}: Create a token that's a copy of another target nonlegendary creature you control, except it has haste. Sacrifice it at the beginning of the next end step. + Ability ability = new SimpleActivatedAbility(new ReflectionOfKikiJikiEffect(), new GenericManaCost(1)); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + } + + private ReflectionOfKikiJiki(final ReflectionOfKikiJiki card) { + super(card); + } + + @Override + public ReflectionOfKikiJiki copy() { + return new ReflectionOfKikiJiki(this); + } +} + +class ReflectionOfKikiJikiEffect extends OneShotEffect { + + ReflectionOfKikiJikiEffect() { + super(Outcome.Benefit); + staticText = "create a token that's a copy of another target nonlegendary creature you control, " + + "except it has haste. Sacrifice it at the beginning of the next end step"; + } + + private ReflectionOfKikiJikiEffect(final ReflectionOfKikiJikiEffect effect) { + super(effect); + } + + @Override + public ReflectionOfKikiJikiEffect copy() { + return new ReflectionOfKikiJikiEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(null, null, true); + effect.setTargetPointer(new FixedTarget(source.getFirstTarget(), game)); + effect.apply(game, source); + effect.sacrificeTokensCreatedAtNextEndStep(game, source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/r/RegalLeosaur.java b/Mage.Sets/src/mage/cards/r/RegalLeosaur.java index da3993b7e8b..24ef8eba65e 100644 --- a/Mage.Sets/src/mage/cards/r/RegalLeosaur.java +++ b/Mage.Sets/src/mage/cards/r/RegalLeosaur.java @@ -30,7 +30,7 @@ public final class RegalLeosaur extends CardImpl { // Whenever this creature mutates, other creatures you control get +2/+1 until end of turn. this.addAbility(new MutatesSourceTriggeredAbility( - new BoostControlledEffect(2, 1, Duration.EndOfTurn, false) + new BoostControlledEffect(2, 1, Duration.EndOfTurn, true) )); } diff --git a/Mage.Sets/src/mage/cards/r/RegentsAuthority.java b/Mage.Sets/src/mage/cards/r/RegentsAuthority.java new file mode 100644 index 00000000000..147d87140f6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RegentsAuthority.java @@ -0,0 +1,73 @@ +package mage.cards.r; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RegentsAuthority extends CardImpl { + + public RegentsAuthority(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{W}"); + + // Target creature gets +2/+2 until end of turn. If it's an enchantment creature or legendary creature, instead put a +1/+1 counter on it and it gets +1/+1 until end of turn. + this.getSpellAbility().addEffect(new RegentsAuthorityEffect()); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + private RegentsAuthority(final RegentsAuthority card) { + super(card); + } + + @Override + public RegentsAuthority copy() { + return new RegentsAuthority(this); + } +} + +class RegentsAuthorityEffect extends OneShotEffect { + + RegentsAuthorityEffect() { + super(Outcome.Benefit); + staticText = "target creature gets +2/+2 until end of turn. If it's an enchantment creature " + + "or legendary creature, instead put a +1/+1 counter on it and it gets +1/+1 until end of turn"; + } + + private RegentsAuthorityEffect(final RegentsAuthorityEffect effect) { + super(effect); + } + + @Override + public RegentsAuthorityEffect copy() { + return new RegentsAuthorityEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent == null) { + return false; + } + if (!permanent.isEnchantment(game) + && (!permanent.isCreature(game) + || !permanent.isLegendary())) { + game.addEffect(new BoostTargetEffect(2, 2), source); + return true; + } + permanent.addCounters(CounterType.P1P1.createInstance(), source, game); + game.addEffect(new BoostTargetEffect(1, 1), source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/r/RegnasSanction.java b/Mage.Sets/src/mage/cards/r/RegnasSanction.java index 5a2d859f27d..7717297b92a 100644 --- a/Mage.Sets/src/mage/cards/r/RegnasSanction.java +++ b/Mage.Sets/src/mage/cards/r/RegnasSanction.java @@ -10,7 +10,9 @@ import mage.choices.ChooseFriendsAndFoes; import mage.constants.CardType; import mage.constants.Outcome; import mage.counters.CounterType; +import mage.filter.FilterPermanent; import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.ControllerIdPredicate; @@ -47,6 +49,12 @@ public final class RegnasSanction extends CardImpl { class RegnasSanctionEffect extends OneShotEffect { + private static final FilterPermanent filter = new FilterControlledCreaturePermanent("untapped creature you control"); + + static { + filter.add(TappedPredicate.UNTAPPED); + } + RegnasSanctionEffect() { super(Outcome.Benefit); this.staticText = "For each player, choose friend or foe. " @@ -67,27 +75,29 @@ class RegnasSanctionEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); ChooseFriendsAndFoes choice = new ChooseFriendsAndFoes(); - if (controller == null) { - return false; - } if (!choice.chooseFriendOrFoe(controller, source, game)) { return false; } - FilterCreaturePermanent filterToTap = new FilterCreaturePermanent(); + FilterPermanent filterToTap = new FilterCreaturePermanent(); for (Player player : choice.getFoes()) { - FilterCreaturePermanent filter = new FilterCreaturePermanent("untapped creature you control"); - filter.add(TappedPredicate.UNTAPPED); - filter.add(new ControllerIdPredicate(player.getId())); - TargetPermanent target = new TargetPermanent(1, 1, filter, true); - if (player.choose(Outcome.Benefit, target, source.getSourceId(), game)) { + TargetPermanent target = new TargetPermanent(filter); + target.setNotTarget(true); + if (game.getBattlefield().contains(filter, source, game, 1) + && player.choose(Outcome.Benefit, target, source.getSourceId(), game)) { filterToTap.add(Predicates.not(new PermanentIdPredicate(target.getFirstTarget()))); } } + choice.getFriends() + .stream() + .map(MageItem::getId) + .map(ControllerIdPredicate::new) + .map(Predicates::not) + .forEach(filterToTap::add); for (Permanent permanent : game.getBattlefield().getActivePermanents( StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), source.getSourceId(), game )) { if (choice.getFriends().stream().map(MageItem::getId).anyMatch(permanent::isControlledBy)) { - permanent.addCounters(CounterType.P1P1.createInstance(), permanent.getControllerId(), source, game); + permanent.addCounters(CounterType.P1P1.createInstance(), source, game); } } return new TapAllEffect(filterToTap).apply(game, source); diff --git a/Mage.Sets/src/mage/cards/r/ReidaneGodOfTheWorthy.java b/Mage.Sets/src/mage/cards/r/ReidaneGodOfTheWorthy.java index b87cb13d6ca..bf21dbc3f19 100644 --- a/Mage.Sets/src/mage/cards/r/ReidaneGodOfTheWorthy.java +++ b/Mage.Sets/src/mage/cards/r/ReidaneGodOfTheWorthy.java @@ -7,8 +7,8 @@ import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.PreventionEffectImpl; -import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.common.CounterUnlessPaysEffect; +import mage.abilities.effects.common.PermanentsEnterBattlefieldTappedEffect; import mage.abilities.effects.common.cost.CostModificationEffectImpl; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.VigilanceAbility; @@ -16,8 +16,9 @@ import mage.cards.Card; import mage.cards.CardSetInfo; import mage.cards.ModalDoubleFacesCard; import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterLandPermanent; import mage.game.Game; -import mage.game.events.EntersTheBattlefieldEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.game.stack.StackObject; @@ -31,6 +32,13 @@ import java.util.UUID; */ public final class ReidaneGodOfTheWorthy extends ModalDoubleFacesCard { + private static final FilterPermanent filter = new FilterLandPermanent("snow lands your opponents control"); + + static { + filter.add(SuperType.SNOW.getPredicate()); + filter.add(TargetController.OPPONENT.getControllerPredicate()); + } + public ReidaneGodOfTheWorthy(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, new SubType[]{SubType.GOD}, "{2}{W}", @@ -50,7 +58,7 @@ public final class ReidaneGodOfTheWorthy extends ModalDoubleFacesCard { this.getLeftHalfCard().addAbility(VigilanceAbility.getInstance()); // Snow lands your opponents control enter the battlefield tapped. - this.getLeftHalfCard().addAbility(new SimpleStaticAbility(new ReidaneGodOfTheWorthyTapEffect())); + this.getLeftHalfCard().addAbility(new SimpleStaticAbility(new PermanentsEnterBattlefieldTappedEffect(filter))); // Noncreature spells your opponents cast with converted mana cost 4 or more cost {2} more to cast. this.getLeftHalfCard().addAbility(new SimpleStaticAbility(new ReidaneGodOfTheWorthyCostEffect())); @@ -77,46 +85,6 @@ public final class ReidaneGodOfTheWorthy extends ModalDoubleFacesCard { } } -class ReidaneGodOfTheWorthyTapEffect extends ReplacementEffectImpl { - - ReidaneGodOfTheWorthyTapEffect() { - super(Duration.WhileOnBattlefield, Outcome.Tap); - staticText = "snow lands your opponents control enter the battlefield tapped"; - } - - private ReidaneGodOfTheWorthyTapEffect(final ReidaneGodOfTheWorthyTapEffect effect) { - super(effect); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Permanent target = ((EntersTheBattlefieldEvent) event).getTarget(); - if (target != null) { - target.setTapped(true); - } - return false; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (!game.getOpponents(source.getControllerId()).contains(event.getPlayerId())) { - return false; - } - Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget(); - return permanent != null && permanent.isLand(game) && permanent.isSnow(); - } - - @Override - public ReidaneGodOfTheWorthyTapEffect copy() { - return new ReidaneGodOfTheWorthyTapEffect(this); - } -} - class ReidaneGodOfTheWorthyCostEffect extends CostModificationEffectImpl { ReidaneGodOfTheWorthyCostEffect() { @@ -214,6 +182,9 @@ class ValkmiraProtectorsShieldTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { + if (event.getTargetId().equals(getSourceId())) { + return false; + } StackObject stackObject = game.getStack().getStackObject(event.getSourceId()); if (stackObject == null || !game.getOpponents(getControllerId()).contains(stackObject.getControllerId())) { return false; @@ -232,7 +203,7 @@ class ValkmiraProtectorsShieldTriggeredAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "Whenever you or a permanent you control becomes the target of a spell or ability " + + return "Whenever you or another permanent you control becomes the target of a spell or ability " + "an opponent controls, counter that spell or ability unless its controller pays {1}."; } } diff --git a/Mage.Sets/src/mage/cards/r/ReinforcedRonin.java b/Mage.Sets/src/mage/cards/r/ReinforcedRonin.java new file mode 100644 index 00000000000..3cdbd24725b --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/ReinforcedRonin.java @@ -0,0 +1,50 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.ReturnToHandSourceEffect; +import mage.abilities.keyword.ChannelAbility; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ReinforcedRonin extends CardImpl { + + public ReinforcedRonin(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{R}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SAMURAI); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // At the beginning of your end step, return Reinforced Ronin to its owner's hand. + this.addAbility(new BeginningOfEndStepTriggeredAbility( + new ReturnToHandSourceEffect(), TargetController.YOU, false + )); + + // Channel — {1}{R}, Discard Reinforced Ronin: Draw a card. + this.addAbility(new ChannelAbility("{1}{R}", new DrawCardSourceControllerEffect(1))); + } + + private ReinforcedRonin(final ReinforcedRonin card) { + super(card); + } + + @Override + public ReinforcedRonin copy() { + return new ReinforcedRonin(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/Reinforcements.java b/Mage.Sets/src/mage/cards/r/Reinforcements.java index ed1e15e309b..1617387dbb7 100644 --- a/Mage.Sets/src/mage/cards/r/Reinforcements.java +++ b/Mage.Sets/src/mage/cards/r/Reinforcements.java @@ -1,4 +1,3 @@ - package mage.cards.r; import java.util.UUID; @@ -6,7 +5,7 @@ import mage.abilities.effects.common.PutOnLibraryTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterCreatureCard; +import mage.filter.StaticFilters; import mage.target.common.TargetCardInYourGraveyard; /** @@ -20,7 +19,7 @@ public final class Reinforcements extends CardImpl { // Put up to three target creature cards from your graveyard on top of your library. this.getSpellAbility().addEffect(new PutOnLibraryTargetEffect(true)); - this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, 3, new FilterCreatureCard("creature cards from your graveyard"))); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, 3, StaticFilters.FILTER_CARD_CREATURES_YOUR_GRAVEYARD)); } private Reinforcements(final Reinforcements card) { diff --git a/Mage.Sets/src/mage/cards/r/ReinsOfPower.java b/Mage.Sets/src/mage/cards/r/ReinsOfPower.java index c9e99f5e58b..7f1ab2982ca 100644 --- a/Mage.Sets/src/mage/cards/r/ReinsOfPower.java +++ b/Mage.Sets/src/mage/cards/r/ReinsOfPower.java @@ -1,4 +1,3 @@ - package mage.cards.r; import java.util.HashSet; @@ -32,7 +31,7 @@ import mage.target.targetpointer.FixedTarget; public final class ReinsOfPower extends CardImpl { public ReinsOfPower(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{U}{U}"); // Untap all creatures you control and all creatures target opponent controls. You and that opponent each gain control of all creatures the other controls until end of turn. Those creatures gain haste until end of turn. this.getSpellAbility().addEffect(new ReinsOfPowerEffect()); @@ -50,21 +49,21 @@ public final class ReinsOfPower extends CardImpl { } class ReinsOfPowerEffect extends OneShotEffect { - + ReinsOfPowerEffect() { super(Outcome.Benefit); this.staticText = "Untap all creatures you control and all creatures target opponent controls. You and that opponent each gain control of all creatures the other controls until end of turn. Those creatures gain haste until end of turn"; } - + ReinsOfPowerEffect(final ReinsOfPowerEffect effect) { super(effect); } - + @Override public ReinsOfPowerEffect copy() { return new ReinsOfPowerEffect(this); } - + @Override public boolean apply(Game game, Ability source) { UUID opponentId = this.getTargetPointer().getFirst(game, source); @@ -73,7 +72,7 @@ class ReinsOfPowerEffect extends OneShotEffect { FilterCreaturePermanent filter = new FilterCreaturePermanent(); filter.add(Predicates.or(new ControllerIdPredicate(source.getControllerId()), new ControllerIdPredicate(opponentId))); new UntapAllEffect(filter).apply(game, source); - + // You and that opponent each gain control of all creatures the other controls until end of turn. Set yourCreatures = new HashSet<>(); Set opponentCreatures = new HashSet<>(); @@ -87,18 +86,18 @@ class ReinsOfPowerEffect extends OneShotEffect { } for (UUID creatureId : yourCreatures) { ContinuousEffect effect = new GainControlTargetEffect(Duration.EndOfTurn, opponentId); - effect.setTargetPointer(new FixedTarget(creatureId)); + effect.setTargetPointer(new FixedTarget(creatureId, game)); game.addEffect(effect, source); } for (UUID creatureId : opponentCreatures) { ContinuousEffect effect = new GainControlTargetEffect(Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(creatureId)); + effect.setTargetPointer(new FixedTarget(creatureId, game)); game.addEffect(effect, source); } - + // Those creatures gain haste until end of turn. game.addEffect(new GainAbilityAllEffect(HasteAbility.getInstance(), Duration.EndOfTurn, filter), source); - + return true; } return false; diff --git a/Mage.Sets/src/mage/cards/r/Reinterpret.java b/Mage.Sets/src/mage/cards/r/Reinterpret.java index 3f8276acb85..c48522aab3f 100644 --- a/Mage.Sets/src/mage/cards/r/Reinterpret.java +++ b/Mage.Sets/src/mage/cards/r/Reinterpret.java @@ -60,7 +60,7 @@ class ReinterpretEffect extends OneShotEffect { return false; } int manaValue = spell.getManaValue(); - spell.counter(source, game); + game.getStack().counter(spell.getId(), source, game); new CastWithoutPayingManaCostEffect(manaValue).apply(game, source); return true; } diff --git a/Mage.Sets/src/mage/cards/r/ReitoSentinel.java b/Mage.Sets/src/mage/cards/r/ReitoSentinel.java new file mode 100644 index 00000000000..ac0b2485d57 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/ReitoSentinel.java @@ -0,0 +1,56 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.MillCardsTargetEffect; +import mage.abilities.effects.common.PutOnLibraryTargetEffect; +import mage.abilities.keyword.DefenderAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.target.TargetPlayer; +import mage.target.common.TargetCardInGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ReitoSentinel extends CardImpl { + + public ReitoSentinel(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}"); + + this.subtype.add(SubType.CONSTRUCT); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Defender + this.addAbility(DefenderAbility.getInstance()); + + // When Reito Sentinel enters the battlefield, target player mills three cards. + Ability ability = new EntersBattlefieldTriggeredAbility(new MillCardsTargetEffect(3)); + ability.addTarget(new TargetPlayer()); + this.addAbility(ability); + + // {3}: Put target card from a graveyard on the bottom of its owner's library. + ability = new SimpleActivatedAbility(new PutOnLibraryTargetEffect( + false, "put target card from a graveyard on the bottom of its owner's library" + ), new GenericManaCost(3)); + ability.addTarget(new TargetCardInGraveyard()); + this.addAbility(ability); + } + + private ReitoSentinel(final ReitoSentinel card) { + super(card); + } + + @Override + public ReitoSentinel copy() { + return new ReitoSentinel(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RekiTheHistoryOfKamigawa.java b/Mage.Sets/src/mage/cards/r/RekiTheHistoryOfKamigawa.java index b80c3ff479d..a1f7bdfb96a 100644 --- a/Mage.Sets/src/mage/cards/r/RekiTheHistoryOfKamigawa.java +++ b/Mage.Sets/src/mage/cards/r/RekiTheHistoryOfKamigawa.java @@ -18,7 +18,7 @@ import mage.filter.FilterSpell; */ public final class RekiTheHistoryOfKamigawa extends CardImpl { - private static final FilterSpell filter = new FilterSpell("legendary spell"); + private static final FilterSpell filter = new FilterSpell("a legendary spell"); static { filter.add(SuperType.LEGENDARY.getPredicate()); diff --git a/Mage.Sets/src/mage/cards/r/ReleaseToMemory.java b/Mage.Sets/src/mage/cards/r/ReleaseToMemory.java new file mode 100644 index 00000000000..c323affd7a2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/ReleaseToMemory.java @@ -0,0 +1,71 @@ +package mage.cards.r; + +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.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.token.SpiritToken; +import mage.players.Player; +import mage.target.common.TargetOpponent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ReleaseToMemory extends CardImpl { + + public ReleaseToMemory(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{W}"); + + // Exile target opponent's graveyard. For each creature card exiled this way, create a 1/1 colorless Spirit creature token. + this.getSpellAbility().addEffect(new ReleaseToMemoryEffect()); + this.getSpellAbility().addTarget(new TargetOpponent()); + } + + private ReleaseToMemory(final ReleaseToMemory card) { + super(card); + } + + @Override + public ReleaseToMemory copy() { + return new ReleaseToMemory(this); + } +} + +class ReleaseToMemoryEffect extends OneShotEffect { + + ReleaseToMemoryEffect() { + super(Outcome.Benefit); + staticText = "exile target opponent's graveyard. For each creature " + + "card exiled this way, create a 1/1 colorless Spirit creature token"; + } + + private ReleaseToMemoryEffect(final ReleaseToMemoryEffect effect) { + super(effect); + } + + @Override + public ReleaseToMemoryEffect copy() { + return new ReleaseToMemoryEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getFirstTarget()); + if (player == null) { + return false; + } + int creatures = player.getGraveyard().count(StaticFilters.FILTER_CARD_CREATURE, game); + player.moveCards(player.getGraveyard(), Zone.EXILED, source, game); + if (creatures > 0) { + new SpiritToken().putOntoBattlefield(creatures, game, source); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/r/RemnantOfTheRisingStar.java b/Mage.Sets/src/mage/cards/r/RemnantOfTheRisingStar.java new file mode 100644 index 00000000000..306cc7f262e --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RemnantOfTheRisingStar.java @@ -0,0 +1,134 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.costs.mana.ManaCost; +import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.permanent.ModifiedPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RemnantOfTheRisingStar extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledCreaturePermanent(); + + static { + filter.add(ModifiedPredicate.instance); + } + + private static final Condition condition + = new PermanentsOnTheBattlefieldCondition(filter, ComparisonType.MORE_THAN, 4); + private static final Hint hint + = new ValueHint("Modified creatures you control", new PermanentsOnBattlefieldCount(filter)); + + public RemnantOfTheRisingStar(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, ""); + + this.subtype.add(SubType.DRAGON); + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + this.color.setGreen(true); + this.nightCard = true; + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever another creature enters the battlefield under your control, you may pay {X}. When you do, put X +1/+1 counters on that creature. + this.addAbility(new EntersBattlefieldControlledTriggeredAbility( + new RemnantOfTheRisingStarEffect(), StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE + )); + + // As long as you control five or more modified creatures, Remnant of the Rising Star gets +5/+5 and has trample. + Ability ability = new SimpleStaticAbility(new ConditionalContinuousEffect( + new BoostSourceEffect(5, 5, Duration.WhileOnBattlefield), + condition, "as long as you control five or more modified creatures, {this} gets +5/+5" + )); + ability.addEffect(new ConditionalContinuousEffect( + new GainAbilitySourceEffect(TrampleAbility.getInstance()), condition, "and has trample" + )); + this.addAbility(ability.addHint(hint)); + } + + private RemnantOfTheRisingStar(final RemnantOfTheRisingStar card) { + super(card); + } + + @Override + public RemnantOfTheRisingStar copy() { + return new RemnantOfTheRisingStar(this); + } +} + +class RemnantOfTheRisingStarEffect extends OneShotEffect { + + RemnantOfTheRisingStarEffect() { + super(Outcome.Benefit); + staticText = "you may pay {X}. When you do, put X +1/+1 counters on that creature"; + } + + private RemnantOfTheRisingStarEffect(final RemnantOfTheRisingStarEffect effect) { + super(effect); + } + + @Override + public RemnantOfTheRisingStarEffect copy() { + return new RemnantOfTheRisingStarEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + ManaCosts cost = new ManaCostsImpl<>("{X}"); + if (player == null || !player.chooseUse( + Outcome.BoostCreature, "Pay " + cost.getText() + "?", source, game + )) { + return false; + } + int xValue = player.announceXMana(0, Integer.MAX_VALUE, "Announce the value for {X}", game, source); + cost.add(new GenericManaCost(xValue)); + if (!cost.pay(source, game, source, source.getControllerId(), false, null)) { + return false; + } + Permanent permanent = (Permanent) getValue("permanentEnteringBattlefield"); + if (permanent == null) { + return false; + } + game.fireReflexiveTriggeredAbility(new ReflexiveTriggeredAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance(xValue)) + .setTargetPointer(new FixedTarget(permanent, game)), false + ), source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/r/RendingFlame.java b/Mage.Sets/src/mage/cards/r/RendingFlame.java new file mode 100644 index 00000000000..22958e05de4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RendingFlame.java @@ -0,0 +1,73 @@ +package mage.cards.r; + +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.constants.SubType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCreatureOrPlaneswalker; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RendingFlame extends CardImpl { + + public RendingFlame(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{R}"); + + // Rending Flame deals 5 damage to target creature or planeswalker. If that permanent is a Spirit, Rending Flame also deals 2 damage to that permanent's controller. + this.getSpellAbility().addEffect(new RendingFlameEffect()); + this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalker()); + } + + private RendingFlame(final RendingFlame card) { + super(card); + } + + @Override + public RendingFlame copy() { + return new RendingFlame(this); + } +} + +class RendingFlameEffect extends OneShotEffect { + + RendingFlameEffect() { + super(Outcome.Benefit); + staticText = "{this} deals 5 damage to target creature or planeswalker. " + + "If that permanent is a Spirit, {this} also deals 2 damage to that permanent's controller"; + } + + private RendingFlameEffect(final RendingFlameEffect effect) { + super(effect); + } + + @Override + public RendingFlameEffect copy() { + return new RendingFlameEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent == null) { + return false; + } + permanent.damage(5, source, game); + if (!permanent.hasSubtype(SubType.SPIRIT, game)) { + return true; + } + Player player = game.getPlayer(permanent.getControllerId()); + if (player != null) { + player.damage(2, source, game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/r/RenegadeKrasis.java b/Mage.Sets/src/mage/cards/r/RenegadeKrasis.java index bf60d72cca1..e8c3b5e3542 100644 --- a/Mage.Sets/src/mage/cards/r/RenegadeKrasis.java +++ b/Mage.Sets/src/mage/cards/r/RenegadeKrasis.java @@ -12,8 +12,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; import mage.counters.CounterType; -import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent; @@ -47,15 +46,8 @@ public final class RenegadeKrasis extends CardImpl { class RenegadeKrasisTriggeredAbility extends TriggeredAbilityImpl { - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent(); - - static { - filter.add(AnotherPredicate.instance); - filter.add(CounterType.P1P1.getPredicate()); - } - public RenegadeKrasisTriggeredAbility() { - super(Zone.BATTLEFIELD, new AddCountersAllEffect(CounterType.P1P1.createInstance(1), filter), false); + super(Zone.BATTLEFIELD, new AddCountersAllEffect(CounterType.P1P1.createInstance(1), StaticFilters.FILTER_OTHER_CONTROLLED_CREATURE_P1P1), false); } public RenegadeKrasisTriggeredAbility(final RenegadeKrasisTriggeredAbility ability) { diff --git a/Mage.Sets/src/mage/cards/r/RenegadeRallier.java b/Mage.Sets/src/mage/cards/r/RenegadeRallier.java index f3a0bfc3595..8b99f7d8665 100644 --- a/Mage.Sets/src/mage/cards/r/RenegadeRallier.java +++ b/Mage.Sets/src/mage/cards/r/RenegadeRallier.java @@ -1,7 +1,5 @@ - package mage.cards.r; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -12,15 +10,16 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.AbilityWord; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.ComparisonType; +import mage.constants.SubType; import mage.filter.common.FilterPermanentCard; import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInYourGraveyard; import mage.watchers.common.RevoltWatcher; +import java.util.UUID; + /** - * * @author fireshoes */ public final class RenegadeRallier extends CardImpl { @@ -41,14 +40,13 @@ public final class RenegadeRallier extends CardImpl { // Revolt — When Renegade Rallier enters the battlefield, if a permanent you controlled left the battlefield this turn, // return target permanent card with converted mana cost 2 or less from your graveyard to your battlefield. - Ability ability = new ConditionalInterveningIfTriggeredAbility(new EntersBattlefieldTriggeredAbility( - new ReturnFromGraveyardToBattlefieldTargetEffect(), false), RevoltCondition.instance, - "Revolt — When {this} enters the battlefield, if a permanent you controlled left" - + " the battlefield this turn, return target permanent card with mana value 2 or less from your graveyard to the battlefield."); - ability.setAbilityWord(AbilityWord.REVOLT); + Ability ability = new ConditionalInterveningIfTriggeredAbility( + new EntersBattlefieldTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect(), false), + RevoltCondition.instance, "When {this} enters the battlefield, if a permanent you controlled " + + "left the battlefield this turn, return target permanent card with mana value 2 or less from your graveyard to the battlefield." + ).setAbilityWord(AbilityWord.REVOLT); ability.addTarget(new TargetCardInYourGraveyard(filter)); - ability.addWatcher(new RevoltWatcher()); - this.addAbility(ability); + this.addAbility(ability, new RevoltWatcher()); } private RenegadeRallier(final RenegadeRallier card) { diff --git a/Mage.Sets/src/mage/cards/r/RenegadeTactics.java b/Mage.Sets/src/mage/cards/r/RenegadeTactics.java index e8fbb8dd6d3..c9d30484b0a 100644 --- a/Mage.Sets/src/mage/cards/r/RenegadeTactics.java +++ b/Mage.Sets/src/mage/cards/r/RenegadeTactics.java @@ -23,7 +23,7 @@ public final class RenegadeTactics extends CardImpl { this.getSpellAbility().addEffect(new CantBlockTargetEffect(Duration.EndOfTurn)); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); // Draw a card. - this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("
")); } private RenegadeTactics(final RenegadeTactics card) { diff --git a/Mage.Sets/src/mage/cards/r/RenegadeWarlord.java b/Mage.Sets/src/mage/cards/r/RenegadeWarlord.java index 8dc902b9963..685e295d7e1 100644 --- a/Mage.Sets/src/mage/cards/r/RenegadeWarlord.java +++ b/Mage.Sets/src/mage/cards/r/RenegadeWarlord.java @@ -1,4 +1,3 @@ - package mage.cards.r; import java.util.UUID; @@ -12,6 +11,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; import mage.filter.common.FilterAttackingCreature; +import mage.filter.common.FilterCreaturePermanent; /** * @@ -19,6 +19,8 @@ import mage.filter.common.FilterAttackingCreature; */ public final class RenegadeWarlord extends CardImpl { + private static final FilterCreaturePermanent filter = new FilterAttackingCreature("each other attacking creature"); + public RenegadeWarlord(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{R}"); this.subtype.add(SubType.HUMAN); @@ -27,7 +29,7 @@ public final class RenegadeWarlord extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(3); this.addAbility(FirstStrikeAbility.getInstance()); - this.addAbility(new AttacksTriggeredAbility(new BoostAllEffect(1, 0, Duration.EndOfTurn, new FilterAttackingCreature(), true), false)); + this.addAbility(new AttacksTriggeredAbility(new BoostAllEffect(1, 0, Duration.EndOfTurn, filter, true), false)); } private RenegadeWarlord(final RenegadeWarlord card) { diff --git a/Mage.Sets/src/mage/cards/r/RenewingTouch.java b/Mage.Sets/src/mage/cards/r/RenewingTouch.java index f023afe71c3..058b1fa9d36 100644 --- a/Mage.Sets/src/mage/cards/r/RenewingTouch.java +++ b/Mage.Sets/src/mage/cards/r/RenewingTouch.java @@ -1,4 +1,3 @@ - package mage.cards.r; import java.util.UUID; @@ -10,7 +9,7 @@ import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; -import mage.filter.common.FilterCreatureCard; +import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; @@ -26,7 +25,7 @@ public final class RenewingTouch extends CardImpl { // Shuffle any number of target creature cards from your graveyard into your library. this.getSpellAbility().addEffect(new RenewingTouchEffect()); - this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, Integer.MAX_VALUE, new FilterCreatureCard("creature cards from your graveyard"))); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, Integer.MAX_VALUE, StaticFilters.FILTER_CARD_CREATURES_YOUR_GRAVEYARD)); } private RenewingTouch(final RenewingTouch card) { @@ -43,7 +42,7 @@ class RenewingTouchEffect extends OneShotEffect { RenewingTouchEffect() { super(Outcome.Neutral); - this.staticText = "Shuffle any number of target cards from your graveyard into your library"; + this.staticText = "Shuffle any number of target creature cards from your graveyard into your library"; } RenewingTouchEffect(final RenewingTouchEffect effect) { diff --git a/Mage.Sets/src/mage/cards/r/RepayInKind.java b/Mage.Sets/src/mage/cards/r/RepayInKind.java index 540364b4284..75b43a3bbed 100644 --- a/Mage.Sets/src/mage/cards/r/RepayInKind.java +++ b/Mage.Sets/src/mage/cards/r/RepayInKind.java @@ -1,7 +1,5 @@ - package mage.cards.r; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; @@ -11,15 +9,16 @@ import mage.constants.Outcome; import mage.game.Game; import mage.players.Player; +import java.util.Objects; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class RepayInKind extends CardImpl { public RepayInKind(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{5}{B}{B}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{5}{B}{B}"); // Each player's life total becomes the lowest life total among all players. this.getSpellAbility().addEffect(new RepayInKindEffect()); @@ -48,17 +47,19 @@ class RepayInKindEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - int lowestLife = game.getPlayer(source.getControllerId()).getLife(); - for (Player playerid : game.getPlayers().values()) { - if (playerid != null) { - if (lowestLife > playerid.getLife()) { - lowestLife = playerid.getLife(); - } - } - } - for (Player playerId : game.getPlayers().values()) { - if (playerId != null) { - playerId.setLife(lowestLife, game, source); + int lowestLife = game + .getState() + .getPlayersInRange(source.getControllerId(), game) + .stream() + .map(game::getPlayer) + .filter(Objects::nonNull) + .mapToInt(Player::getLife) + .min() + .orElse(0); + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + if (player != null) { + player.setLife(lowestLife, game, source); } } return true; @@ -68,5 +69,4 @@ class RepayInKindEffect extends OneShotEffect { public RepayInKindEffect copy() { return new RepayInKindEffect(this); } - } diff --git a/Mage.Sets/src/mage/cards/r/RepelTheVile.java b/Mage.Sets/src/mage/cards/r/RepelTheVile.java new file mode 100644 index 00000000000..6f263aa536a --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RepelTheVile.java @@ -0,0 +1,51 @@ +package mage.cards.r; + +import mage.abilities.Mode; +import mage.abilities.effects.common.ExileTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.PowerPredicate; +import mage.target.TargetPermanent; +import mage.target.common.TargetEnchantmentPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RepelTheVile extends CardImpl { + + private static final FilterPermanent filter + = new FilterCreaturePermanent("creature with power 4 or greater"); + + static { + filter.add(new PowerPredicate(ComparisonType.MORE_THAN, 3)); + } + + public RepelTheVile(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{W}"); + + // Choose one — + // • Exile target creature with power 4 or greater. + this.getSpellAbility().addEffect(new ExileTargetEffect()); + this.getSpellAbility().addTarget(new TargetPermanent(filter)); + + // • Exile target enchantment. + Mode mode = new Mode(new ExileTargetEffect()); + mode.addTarget(new TargetEnchantmentPermanent()); + this.getSpellAbility().addMode(mode); + } + + private RepelTheVile(final RepelTheVile card) { + super(card); + } + + @Override + public RepelTheVile copy() { + return new RepelTheVile(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/ReplicationSpecialist.java b/Mage.Sets/src/mage/cards/r/ReplicationSpecialist.java new file mode 100644 index 00000000000..cf6a8f285a3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/ReplicationSpecialist.java @@ -0,0 +1,62 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.CreateTokenCopyTargetEffect; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SetTargetPointer; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledArtifactPermanent; +import mage.filter.predicate.permanent.TokenPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ReplicationSpecialist extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledArtifactPermanent("a nontoken artifact"); + + static { + filter.add(TokenPredicate.FALSE); + } + + public ReplicationSpecialist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}"); + + this.subtype.add(SubType.MOONFOLK); + this.subtype.add(SubType.ARTIFICER); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever a nontoken artifact enters the battlefield under your control, you may pay {1}{U}. If you do, create a token that's a copy of that artifact. + this.addAbility(new EntersBattlefieldControlledTriggeredAbility( + Zone.BATTLEFIELD, + new DoIfCostPaid( + new CreateTokenCopyTargetEffect() + .setText("create a token that's a copy of that artifact"), + new ManaCostsImpl<>("{1}{U}") + ), filter, false, SetTargetPointer.PERMANENT, null + )); + } + + private ReplicationSpecialist(final ReplicationSpecialist card) { + super(card); + } + + @Override + public ReplicationSpecialist copy() { + return new ReplicationSpecialist(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RepositorySkaab.java b/Mage.Sets/src/mage/cards/r/RepositorySkaab.java new file mode 100644 index 00000000000..9ee404accad --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RepositorySkaab.java @@ -0,0 +1,46 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.ExploitCreatureTriggeredAbility; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.abilities.keyword.ExploitAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RepositorySkaab extends CardImpl { + + public RepositorySkaab(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); + + this.subtype.add(SubType.ZOMBIE); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Exploit + this.addAbility(new ExploitAbility()); + + // When Repository Skaab exploits a creature, return target instant or sorcery card from your graveyard to your hand. + Ability ability = new ExploitCreatureTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect()); + ability.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY_FROM_YOUR_GRAVEYARD)); + this.addAbility(ability); + } + + private RepositorySkaab(final RepositorySkaab card) { + super(card); + } + + @Override + public RepositorySkaab copy() { + return new RepositorySkaab(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/ResearchThief.java b/Mage.Sets/src/mage/cards/r/ResearchThief.java new file mode 100644 index 00000000000..b9d96c11c7f --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/ResearchThief.java @@ -0,0 +1,60 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.common.DealsDamageToAPlayerAllTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.keyword.FlashAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SetTargetPointer; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterArtifactCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ResearchThief extends CardImpl { + + private static final FilterPermanent filter + = new FilterArtifactCreaturePermanent("an artifact creature you control"); + + static { + filter.add(TargetController.YOU.getControllerPredicate()); + } + + public ResearchThief(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{4}{U}"); + + this.subtype.add(SubType.MOONFOLK); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever an artifact creature you control deals combat damage to a player, draw a card. + this.addAbility(new DealsDamageToAPlayerAllTriggeredAbility( + new DrawCardSourceControllerEffect(1), + filter, false, SetTargetPointer.NONE, true + )); + } + + private ResearchThief(final ResearchThief card) { + super(card); + } + + @Override + public ResearchThief copy() { + return new ResearchThief(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/ResistanceSquad.java b/Mage.Sets/src/mage/cards/r/ResistanceSquad.java new file mode 100644 index 00000000000..f46a7382e95 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/ResistanceSquad.java @@ -0,0 +1,59 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.mageobject.AnotherPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ResistanceSquad extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.HUMAN); + + static { + filter.add(AnotherPredicate.instance); + } + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter); + private static final Hint hint = new ConditionHint(condition, "You control another Human"); + + public ResistanceSquad(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // When Resistance Squad enters the battlefield, if you control another Human, draw a card. + this.addAbility(new ConditionalInterveningIfTriggeredAbility(new EntersBattlefieldTriggeredAbility( + new DrawCardSourceControllerEffect(1)), + condition, "When {this} enters the battlefield, " + + "if you control another Human, draw a card." + ).addHint(hint)); + } + + private ResistanceSquad(final ResistanceSquad card) { + super(card); + } + + @Override + public ResistanceSquad copy() { + return new ResistanceSquad(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/ResoluteBlademaster.java b/Mage.Sets/src/mage/cards/r/ResoluteBlademaster.java index 5d83c61fcc0..fbb09e4b7a8 100644 --- a/Mage.Sets/src/mage/cards/r/ResoluteBlademaster.java +++ b/Mage.Sets/src/mage/cards/r/ResoluteBlademaster.java @@ -12,7 +12,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; /** * @@ -32,7 +32,7 @@ public final class ResoluteBlademaster extends CardImpl { // you control gain double strike until end of turn. Ability ability = new AllyEntersBattlefieldTriggeredAbility( new GainAbilityAllEffect(DoubleStrikeAbility.getInstance(), Duration.EndOfTurn, - new FilterControlledCreaturePermanent("creatures you control")), false); + StaticFilters.FILTER_CONTROLLED_CREATURES), false); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/r/ResoundingSilence.java b/Mage.Sets/src/mage/cards/r/ResoundingSilence.java index 99f6127f68d..ff073a50df7 100644 --- a/Mage.Sets/src/mage/cards/r/ResoundingSilence.java +++ b/Mage.Sets/src/mage/cards/r/ResoundingSilence.java @@ -1,4 +1,3 @@ - package mage.cards.r; import java.util.UUID; @@ -10,7 +9,7 @@ import mage.abilities.keyword.CyclingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterAttackingCreature; +import mage.filter.StaticFilters; import mage.target.TargetPermanent; import mage.target.common.TargetAttackingCreature; @@ -23,7 +22,6 @@ public final class ResoundingSilence extends CardImpl { public ResoundingSilence(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{3}{W}"); - // Exile target attacking creature. this.getSpellAbility().addEffect(new ExileTargetEffect()); this.getSpellAbility().addTarget(new TargetAttackingCreature()); @@ -31,7 +29,7 @@ public final class ResoundingSilence extends CardImpl { this.addAbility(new CyclingAbility(new ManaCostsImpl("{5}{G}{W}{U}"))); // When you cycle Resounding Silence, exile up to two target attacking creatures. Ability ability = new CycleTriggeredAbility(new ExileTargetEffect()); - TargetPermanent target = new TargetAttackingCreature(0, 2, new FilterAttackingCreature("up to two target attacking creatures"), false); + TargetPermanent target = new TargetAttackingCreature(0, 2, StaticFilters.FILTER_ATTACKING_CREATURES, false); ability.addTarget(target); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/r/ResoundingWave.java b/Mage.Sets/src/mage/cards/r/ResoundingWave.java index e022446a18d..33c7eef4a0c 100644 --- a/Mage.Sets/src/mage/cards/r/ResoundingWave.java +++ b/Mage.Sets/src/mage/cards/r/ResoundingWave.java @@ -1,4 +1,3 @@ - package mage.cards.r; import java.util.UUID; @@ -10,7 +9,7 @@ import mage.abilities.keyword.CyclingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; import mage.target.TargetPermanent; /** @@ -30,7 +29,7 @@ public final class ResoundingWave extends CardImpl { this.addAbility(new CyclingAbility(new ManaCostsImpl("{5}{W}{U}{B}"))); // When you cycle Resounding Wave, return two target permanents to their owners' hands. Ability ability = new CycleTriggeredAbility(new ReturnToHandTargetEffect()); - TargetPermanent target = new TargetPermanent(2, new FilterPermanent("two target permanents")); + TargetPermanent target = new TargetPermanent(2, StaticFilters.FILTER_PERMANENTS); ability.addTarget(target); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/r/ResourcefulReturn.java b/Mage.Sets/src/mage/cards/r/ResourcefulReturn.java index 8bf98ff0a0f..2b9e985c4c5 100644 --- a/Mage.Sets/src/mage/cards/r/ResourcefulReturn.java +++ b/Mage.Sets/src/mage/cards/r/ResourcefulReturn.java @@ -5,6 +5,7 @@ import java.util.UUID; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -23,7 +24,7 @@ public final class ResourcefulReturn extends CardImpl { // Return target creature card from your graveyard to your hand. If you control an artifact, draw a card. this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); - this.getSpellAbility().addEffect(new ReturnToHandTargetEffect()); + this.getSpellAbility().addEffect(new ReturnFromGraveyardToHandTargetEffect()); this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new DrawCardSourceControllerEffect(1), new PermanentsOnTheBattlefieldCondition(StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT), "If you control an artifact, draw a card")); diff --git a/Mage.Sets/src/mage/cards/r/RestlessBloodseeker.java b/Mage.Sets/src/mage/cards/r/RestlessBloodseeker.java new file mode 100644 index 00000000000..2525c0588d9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RestlessBloodseeker.java @@ -0,0 +1,69 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.YouGainedLifeCondition; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.abilities.keyword.TransformAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.permanent.token.BloodToken; +import mage.target.common.TargetControlledPermanent; +import mage.watchers.common.PlayerGainedLifeWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RestlessBloodseeker extends CardImpl { + + private static final Condition condition = new YouGainedLifeCondition(ComparisonType.MORE_THAN, 0); + private static final Hint hint = new ConditionHint(condition, "You gained life this turn"); + private static final FilterControlledPermanent filter + = new FilterControlledPermanent(SubType.BLOOD, "Blood tokens"); + + static { + filter.add(TokenPredicate.TRUE); + } + + public RestlessBloodseeker(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}"); + + this.subtype.add(SubType.VAMPIRE); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + this.secondSideCardClazz = mage.cards.b.BloodsoakedReveler.class; + + // At the beginning of your end step, if you gained life this turn, create a Blood token. + this.addAbility(new BeginningOfEndStepTriggeredAbility( + Zone.BATTLEFIELD, new CreateTokenEffect(new BloodToken()), + TargetController.YOU, condition, false + ).addHint(hint), new PlayerGainedLifeWatcher()); + + // Sacrifice two Blood tokens: Transform Restless Bloodseeker. Activate only as a sorcery. + this.addAbility(new TransformAbility()); + this.addAbility(new ActivateAsSorceryActivatedAbility( + new TransformSourceEffect(), + new SacrificeTargetCost(new TargetControlledPermanent(2, filter)) + )); + } + + private RestlessBloodseeker(final RestlessBloodseeker card) { + super(card); + } + + @Override + public RestlessBloodseeker copy() { + return new RestlessBloodseeker(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/Resuscitate.java b/Mage.Sets/src/mage/cards/r/Resuscitate.java index 148e15618a8..14386d5cd9a 100644 --- a/Mage.Sets/src/mage/cards/r/Resuscitate.java +++ b/Mage.Sets/src/mage/cards/r/Resuscitate.java @@ -11,7 +11,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; /** * @@ -19,15 +19,13 @@ import mage.filter.common.FilterControlledCreaturePermanent; */ public final class Resuscitate extends CardImpl { - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("creatures you control"); - public Resuscitate(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}"); // Until end of turn, creatures you control gain "{1}: Regenerate this creature." Ability ability = new SimpleActivatedAbility(new RegenerateSourceEffect().setText("Regenerate this creature"), new GenericManaCost(1)); this.getSpellAbility().addEffect( - new GainAbilityAllEffect(ability, Duration.EndOfTurn, filter, + new GainAbilityAllEffect(ability, Duration.EndOfTurn, StaticFilters.FILTER_CONTROLLED_CREATURES, "Until end of turn, creatures you control gain \"{1}: Regenerate this creature.\"" ) ); diff --git a/Mage.Sets/src/mage/cards/r/RetributionOfTheAncients.java b/Mage.Sets/src/mage/cards/r/RetributionOfTheAncients.java index 3aa922b835b..998e2336445 100644 --- a/Mage.Sets/src/mage/cards/r/RetributionOfTheAncients.java +++ b/Mage.Sets/src/mage/cards/r/RetributionOfTheAncients.java @@ -14,7 +14,7 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Zone; import mage.counters.CounterType; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; import java.util.UUID; @@ -24,19 +24,13 @@ import java.util.UUID; */ public final class RetributionOfTheAncients extends CardImpl { - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("creatures you control"); - - static { - filter.add(CounterType.P1P1.getPredicate()); - } - public RetributionOfTheAncients(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{B}"); // {B}, Remove X +1/+1 counters from among creatures you control: Target creature gets -X/-X until end of turn. DynamicValue xValue = new SignInversionDynamicValue(GetXValue.instance); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostTargetEffect(xValue, xValue, Duration.EndOfTurn, true), new ManaCostsImpl("{B}")); - ability.addCost(new RemoveVariableCountersTargetCost(filter, CounterType.P1P1, "X", 0)); + ability.addCost(new RemoveVariableCountersTargetCost(StaticFilters.FILTER_CONTROLLED_CREATURES, CounterType.P1P1, "X", 0)); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/r/Retrieve.java b/Mage.Sets/src/mage/cards/r/Retrieve.java new file mode 100644 index 00000000000..dd35c9bd53a --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/Retrieve.java @@ -0,0 +1,51 @@ +package mage.cards.r; + +import mage.abilities.effects.common.ExileSpellEffect; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.common.FilterPermanentCard; +import mage.filter.predicate.Predicates; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Retrieve extends CardImpl { + + private static final FilterCard filter = new FilterPermanentCard("noncreature permanent card"); + + static { + filter.add(Predicates.not(CardType.CREATURE.getPredicate())); + } + + public Retrieve(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{G}"); + + // Return up to one target creature card and up to one target noncreature permanent card from your graveyard to your hand. Exile Retrieve. + this.getSpellAbility().addEffect(new ReturnFromGraveyardToHandTargetEffect() + .setText("return up to one target creature card and up to one target " + + "noncreature permanent card from your graveyard to your hand")); + this.getSpellAbility().addEffect(new ExileSpellEffect()); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard( + 0, 1, StaticFilters.FILTER_CARD_CREATURE + )); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard( + 0, 1, filter + )); + } + + private Retrieve(final Retrieve card) { + super(card); + } + + @Override + public Retrieve copy() { + return new Retrieve(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/ReturnToAction.java b/Mage.Sets/src/mage/cards/r/ReturnToAction.java new file mode 100644 index 00000000000..0e797aeea84 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/ReturnToAction.java @@ -0,0 +1,45 @@ +package mage.cards.r; + +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ReturnToAction extends CardImpl { + + public ReturnToAction(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{B}"); + + // Until end of turn, target creature gets +1/+0 and gains lifelink and "When this creature dies, return it to the battlefield tapped under its owner's control." + this.getSpellAbility().addEffect(new BoostTargetEffect(1, 0, Duration.EndOfTurn) + .setText("Until end of turn, target creature gets +1/+0") + ); + this.getSpellAbility().addEffect(new GainAbilityTargetEffect( + LifelinkAbility.getInstance(), Duration.EndOfTurn, "and gains lifelink" + )); + this.getSpellAbility().addEffect(new GainAbilityTargetEffect(new DiesSourceTriggeredAbility( + new ReturnSourceFromGraveyardToBattlefieldEffect(true, true), false + ), Duration.EndOfTurn, "and \"When this creature dies, return it to the battlefield tapped under its owner's control.\"")); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + private ReturnToAction(final ReturnToAction card) { + super(card); + } + + @Override + public ReturnToAction copy() { + return new ReturnToAction(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RevealingEye.java b/Mage.Sets/src/mage/cards/r/RevealingEye.java new file mode 100644 index 00000000000..ba9d8cd96dd --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RevealingEye.java @@ -0,0 +1,95 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.TransformIntoSourceTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.Card; +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.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInHand; +import mage.target.common.TargetOpponent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RevealingEye extends CardImpl { + + public RevealingEye(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); + + this.subtype.add(SubType.EYE); + this.subtype.add(SubType.HORROR); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + this.color.setBlack(true); + this.nightCard = true; + + // Menace + this.addAbility(new MenaceAbility(false)); + + // When this creature transforms into Revealing Eye, target opponent reveals their hand. You may choose a nonland card from it. If you do, that player discards that card, then draws a card. + Ability ability = new TransformIntoSourceTriggeredAbility(new RevealingEyeEffect()); + ability.addTarget(new TargetOpponent()); + this.addAbility(ability); + } + + private RevealingEye(final RevealingEye card) { + super(card); + } + + @Override + public RevealingEye copy() { + return new RevealingEye(this); + } +} + +class RevealingEyeEffect extends OneShotEffect { + + RevealingEyeEffect() { + super(Outcome.Discard); + staticText = "target opponent reveals their hand. You may choose a nonland card from it. " + + "If you do, that player discards that card, then draws a card"; + } + + private RevealingEyeEffect(final RevealingEyeEffect effect) { + super(effect); + } + + @Override + public RevealingEyeEffect copy() { + return new RevealingEyeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Player opponent = game.getPlayer(source.getFirstTarget()); + if (controller == null || opponent == null) { + return false; + } + opponent.revealCards(source, opponent.getHand(), game); + if (opponent.getHand().count(StaticFilters.FILTER_CARD_NON_LAND, game) < 1) { + return true; + } + TargetCard target = new TargetCardInHand(0, 1, StaticFilters.FILTER_CARD_NON_LAND); + controller.choose(outcome, opponent.getHand(), target, game); + Card card = game.getCard(target.getFirstTarget()); + if (card == null) { + return true; + } + opponent.discard(card, false, source, game); + opponent.drawCards(1, source, game); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/r/ReveilleSquad.java b/Mage.Sets/src/mage/cards/r/ReveilleSquad.java index 273a094771a..792b3a394ae 100644 --- a/Mage.Sets/src/mage/cards/r/ReveilleSquad.java +++ b/Mage.Sets/src/mage/cards/r/ReveilleSquad.java @@ -1,23 +1,21 @@ - package mage.cards.r; -import java.util.UUID; import mage.MageInt; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.condition.InvertCondition; import mage.abilities.condition.common.SourceTappedCondition; import mage.abilities.effects.common.UntapAllControllerEffect; -import mage.constants.SubType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.events.GameEvent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class ReveilleSquad extends CardImpl { @@ -76,7 +74,7 @@ class ReveilleSquadTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkInterveningIfClause(Game game) { - return new InvertCondition(SourceTappedCondition.instance).apply(game, this); + return SourceTappedCondition.UNTAPPED.apply(game, this); } @Override diff --git a/Mage.Sets/src/mage/cards/r/RevelInRiches.java b/Mage.Sets/src/mage/cards/r/RevelInRiches.java index 00c247527a7..6c269718e99 100644 --- a/Mage.Sets/src/mage/cards/r/RevelInRiches.java +++ b/Mage.Sets/src/mage/cards/r/RevelInRiches.java @@ -14,7 +14,7 @@ import mage.constants.ComparisonType; import mage.constants.SubType; import mage.constants.TargetController; import mage.filter.FilterPermanent; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.permanent.token.TreasureToken; import java.util.UUID; @@ -24,11 +24,9 @@ import java.util.UUID; */ public final class RevelInRiches extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature an opponent controls"); private static final FilterPermanent filter2 = new FilterPermanent("Treasures"); static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); filter2.add(SubType.TREASURE.getPredicate()); } @@ -36,7 +34,7 @@ public final class RevelInRiches extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{B}"); // Whenever a creature an opponent controls dies, create a colorless Treasure artifact token with "{T}, Sacrifice this artifact: Add one mana of any color." - this.addAbility(new DiesCreatureTriggeredAbility(new CreateTokenEffect(new TreasureToken()), false, filter)); + this.addAbility(new DiesCreatureTriggeredAbility(new CreateTokenEffect(new TreasureToken()), false, StaticFilters.FILTER_OPPONENTS_PERMANENT_A_CREATURE)); // At the beginning of your upkeep, if you control ten or more Treasures, you win the game. TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new WinGameSourceControllerEffect(), TargetController.YOU, false); this.addAbility(new ConditionalInterveningIfTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/r/RevengeStarWars.java b/Mage.Sets/src/mage/cards/r/RevengeStarWars.java index a005bdf42a8..37566433eaf 100644 --- a/Mage.Sets/src/mage/cards/r/RevengeStarWars.java +++ b/Mage.Sets/src/mage/cards/r/RevengeStarWars.java @@ -1,4 +1,3 @@ - package mage.cards.r; import java.util.UUID; @@ -59,7 +58,6 @@ enum LostLifeCondition implements Condition { instance; - @Override public boolean apply(Game game, Ability source) { PlayerLostLifeWatcher watcher = game.getState().getWatcher(PlayerLostLifeWatcher.class); @@ -92,7 +90,7 @@ class RevengeEffect extends OneShotEffect { Permanent target = game.getPermanent(targetPointer.getFirst(game, source)); if (target != null && target.isCreature(game)) { ContinuousEffect effect = new BoostTargetEffect(4, 0, Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(target.getId())); + effect.setTargetPointer(new FixedTarget(target.getId(), game)); game.addEffect(effect, source); return true; } diff --git a/Mage.Sets/src/mage/cards/r/ReverentMantra.java b/Mage.Sets/src/mage/cards/r/ReverentMantra.java index d04cef231f5..4b2911d55dc 100644 --- a/Mage.Sets/src/mage/cards/r/ReverentMantra.java +++ b/Mage.Sets/src/mage/cards/r/ReverentMantra.java @@ -1,7 +1,5 @@ - package mage.cards.r; -import java.util.UUID; import mage.ObjectColor; import mage.abilities.costs.AlternativeCostSourceAbility; import mage.abilities.costs.common.ExileFromHandCost; @@ -10,31 +8,35 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.filter.common.FilterOwnedCard; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardIdPredicate; import mage.filter.predicate.mageobject.ColorPredicate; import mage.target.common.TargetCardInHand; +import java.util.UUID; + /** - * * @author ciaccona007 */ public final class ReverentMantra extends CardImpl { + private static final FilterOwnedCard filter + = new FilterOwnedCard("a white card from your hand"); + + static { + filter.add(new ColorPredicate(ObjectColor.WHITE)); + } + public ReverentMantra(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{W}"); // You may exile a white card from your hand rather than pay Reverent Mantra's mana cost. - FilterOwnedCard filter = new FilterOwnedCard("a white card from your hand"); - filter.add(new ColorPredicate(ObjectColor.WHITE)); - filter.add(Predicates.not(new CardIdPredicate(this.getId()))); - this.addAbility(new AlternativeCostSourceAbility(new ExileFromHandCost(new TargetCardInHand(filter)))); // Choose a color. All creatures gain protection from the chosen color until end of turn. - this.getSpellAbility().addEffect(new GainProtectionFromColorAllEffect(Duration.EndOfTurn, new FilterCreaturePermanent())); + this.getSpellAbility().addEffect(new GainProtectionFromColorAllEffect( + Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURE + )); } private ReverentMantra(final ReverentMantra card) { diff --git a/Mage.Sets/src/mage/cards/r/Revivify.java b/Mage.Sets/src/mage/cards/r/Revivify.java index 690999b9807..7fbf9505243 100644 --- a/Mage.Sets/src/mage/cards/r/Revivify.java +++ b/Mage.Sets/src/mage/cards/r/Revivify.java @@ -41,7 +41,7 @@ public final class Revivify extends CardImpl { // Roll a d20 and add the number of creature cards in your graveyard that were put there from the battlefield this turn. RollDieWithResultTableEffect effect = new RollDieWithResultTableEffect( 20, "roll a d20 and add the number of creature cards " + - "in your graveyard that were put there from the battlefield this turn", xValue + "in your graveyard that were put there from the battlefield this turn", xValue, 0 ); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addWatcher(new CardsPutIntoGraveyardWatcher()); diff --git a/Mage.Sets/src/mage/cards/r/RhodaGeistAvenger.java b/Mage.Sets/src/mage/cards/r/RhodaGeistAvenger.java new file mode 100644 index 00000000000..5c07eb8827a --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RhodaGeistAvenger.java @@ -0,0 +1,51 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.common.TappedNotAttackingTriggeredAbility; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.PartnerWithAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.CounterType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RhodaGeistAvenger extends CardImpl { + + public RhodaGeistAvenger(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Partner with Timin, Youthful Geist + this.addAbility(new PartnerWithAbility("Timin, Youthful Geist")); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // Whenever a creature an opponent controls becomes tapped, if it isn't being declared as an attacker, put a +1/+1 counter on Rhoda, Geist Avenger. + this.addAbility(new TappedNotAttackingTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false + )); + } + + private RhodaGeistAvenger(final RhodaGeistAvenger card) { + super(card); + } + + @Override + public RhodaGeistAvenger copy() { + return new RhodaGeistAvenger(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RhysticStudy.java b/Mage.Sets/src/mage/cards/r/RhysticStudy.java index 53cea2e790d..2a8db1310f4 100644 --- a/Mage.Sets/src/mage/cards/r/RhysticStudy.java +++ b/Mage.Sets/src/mage/cards/r/RhysticStudy.java @@ -27,7 +27,7 @@ public final class RhysticStudy extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}"); // Whenever an opponent casts a spell, you may draw a card unless that player pays {1}. - this.addAbility(new SpellCastOpponentTriggeredAbility(Zone.BATTLEFIELD, new RhysticStudyDrawEffect(), StaticFilters.FILTER_SPELL, false, SetTargetPointer.PLAYER)); + this.addAbility(new SpellCastOpponentTriggeredAbility(Zone.BATTLEFIELD, new RhysticStudyDrawEffect(), StaticFilters.FILTER_SPELL_A, false, SetTargetPointer.PLAYER)); } private RhysticStudy(final RhysticStudy card) { diff --git a/Mage.Sets/src/mage/cards/r/Riddlesmith.java b/Mage.Sets/src/mage/cards/r/Riddlesmith.java index 87f35b3b36e..e5eb409d956 100644 --- a/Mage.Sets/src/mage/cards/r/Riddlesmith.java +++ b/Mage.Sets/src/mage/cards/r/Riddlesmith.java @@ -1,8 +1,5 @@ - - package mage.cards.r; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.effects.common.DrawDiscardControllerEffect; @@ -10,15 +7,20 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; +import mage.filter.FilterSpell; import mage.filter.common.FilterArtifactSpell; +import java.util.UUID; + /** - * * @author Loki, North */ public final class Riddlesmith extends CardImpl { - public Riddlesmith (UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}"); + + private static final FilterSpell filter = new FilterArtifactSpell("an artifact spell"); + + public Riddlesmith(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.ARTIFICER); @@ -26,10 +28,12 @@ public final class Riddlesmith extends CardImpl { this.toughness = new MageInt(1); // Whenever you cast an artifact spell, you may draw a card. If you do, discard a card. - this.addAbility(new SpellCastControllerTriggeredAbility(new DrawDiscardControllerEffect(), new FilterArtifactSpell("an artifact spell"), true)); + this.addAbility(new SpellCastControllerTriggeredAbility( + new DrawDiscardControllerEffect(true), filter, false + )); } - public Riddlesmith (final Riddlesmith card) { + public Riddlesmith(final Riddlesmith card) { super(card); } diff --git a/Mage.Sets/src/mage/cards/r/RideDown.java b/Mage.Sets/src/mage/cards/r/RideDown.java index d3fd09a88ba..d06a77ad228 100644 --- a/Mage.Sets/src/mage/cards/r/RideDown.java +++ b/Mage.Sets/src/mage/cards/r/RideDown.java @@ -1,4 +1,3 @@ - package mage.cards.r; import java.util.UUID; @@ -34,8 +33,7 @@ public final class RideDown extends CardImpl { } public RideDown(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{R}{W}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{R}{W}"); // Destroy target blocking creature. Creatures that were blocked by that creature this combat gain trample until end of turn. this.getSpellAbility().addEffect(new RideDownEffect()); @@ -77,9 +75,9 @@ class RideDownEffect extends OneShotEffect { if (blockingCreature != null) { for (CombatGroup combatGroup : game.getCombat().getGroups()) { if (combatGroup.getBlockers().contains(blockingCreature.getId())) { - for (UUID attackerId: combatGroup.getAttackers()) { + for (UUID attackerId : combatGroup.getAttackers()) { ContinuousEffect effect = new GainAbilityTargetEffect(TrampleAbility.getInstance(), Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(attackerId)); + effect.setTargetPointer(new FixedTarget(attackerId, game)); game.addEffect(effect, source); } break; diff --git a/Mage.Sets/src/mage/cards/r/RighteousIndignation.java b/Mage.Sets/src/mage/cards/r/RighteousIndignation.java index a9df817204e..e9476d1f95b 100644 --- a/Mage.Sets/src/mage/cards/r/RighteousIndignation.java +++ b/Mage.Sets/src/mage/cards/r/RighteousIndignation.java @@ -11,7 +11,6 @@ import mage.constants.Duration; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; @@ -67,7 +66,7 @@ class RighteousIndignationTriggeredAbility extends TriggeredAbilityImpl { if (blocked != null) { if (blocked.getColor(game).contains(ObjectColor.BLACK) || blocked.getColor(game).contains(ObjectColor.RED)) { - getEffects().get(0).setTargetPointer(new FixedTarget(blocker.getId())); + getEffects().get(0).setTargetPointer(new FixedTarget(blocker.getId(), game)); return true; } } diff --git a/Mage.Sets/src/mage/cards/r/RimehornAurochs.java b/Mage.Sets/src/mage/cards/r/RimehornAurochs.java index f878e9c1d86..bef3a9c630e 100644 --- a/Mage.Sets/src/mage/cards/r/RimehornAurochs.java +++ b/Mage.Sets/src/mage/cards/r/RimehornAurochs.java @@ -1,4 +1,3 @@ - package mage.cards.r; import java.util.UUID; @@ -7,6 +6,7 @@ import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.RequirementEffect; @@ -15,9 +15,9 @@ import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; +import mage.filter.common.FilterAttackingCreature; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.AnotherPredicate; -import mage.filter.predicate.permanent.AttackingPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; @@ -29,13 +29,15 @@ import mage.watchers.common.BlockedAttackerWatcher; */ public final class RimehornAurochs extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(SubType.AUROCHS, "other attacking Aurochs"); + private static final FilterAttackingCreature filter = new FilterAttackingCreature("other attacking Aurochs"); static { - filter.add(AttackingPredicate.instance); + filter.add(SubType.AUROCHS.getPredicate()); filter.add(AnotherPredicate.instance); } + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter); + public RimehornAurochs(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{G}"); this.addSuperType(SuperType.SNOW); @@ -47,10 +49,10 @@ public final class RimehornAurochs extends CardImpl { this.addAbility(TrampleAbility.getInstance()); // Whenever Rimehorn Aurochs attacks, it gets +1/+0 until end of turn for each other attacking Aurochs. - 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)); + // {2}{S}: Target creature blocks target creature this turn if able. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new RimehornAurochsEffect(), new ManaCostsImpl("{2}{S}")); + Ability ability = new SimpleActivatedAbility(new RimehornAurochsEffect(), new ManaCostsImpl("{2}{S}")); ability.addTarget(new TargetCreaturePermanent(new FilterCreaturePermanent("creature that must block"))); ability.addTarget(new TargetCreaturePermanent(new FilterCreaturePermanent("creature that is to be blocked"))); this.addAbility(ability, new BlockedAttackerWatcher()); @@ -123,5 +125,4 @@ class RimehornAurochsEffect extends RequirementEffect { public RimehornAurochsEffect copy() { return new RimehornAurochsEffect(this); } - } diff --git a/Mage.Sets/src/mage/cards/r/RingOfMaruf.java b/Mage.Sets/src/mage/cards/r/RingOfMaruf.java index b0e284423d7..2abb2ffaa2e 100644 --- a/Mage.Sets/src/mage/cards/r/RingOfMaruf.java +++ b/Mage.Sets/src/mage/cards/r/RingOfMaruf.java @@ -16,7 +16,6 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.Zone; -import mage.filter.FilterCard; import mage.game.Game; import mage.game.events.GameEvent; import mage.players.Player; @@ -68,7 +67,7 @@ class RingOfMarufEffect extends ReplacementEffectImpl { public boolean replaceEvent(GameEvent event, Ability source, Game game) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - new WishEffect(new FilterCard(), false).apply(game, source); + new WishEffect().apply(game, source); this.discard(); return true; } diff --git a/Mage.Sets/src/mage/cards/r/RionyaFireDancer.java b/Mage.Sets/src/mage/cards/r/RionyaFireDancer.java index 4d0ab71efcf..65f3990d29d 100644 --- a/Mage.Sets/src/mage/cards/r/RionyaFireDancer.java +++ b/Mage.Sets/src/mage/cards/r/RionyaFireDancer.java @@ -16,6 +16,7 @@ import mage.game.Game; import mage.game.events.GameEvent; import mage.game.stack.Spell; import mage.target.TargetPermanent; +import mage.util.CardUtil; import mage.watchers.Watcher; import java.util.HashMap; @@ -121,7 +122,7 @@ class RionyaFireDancerWatcher extends Watcher { } Spell spell = game.getSpell(event.getTargetId()); if (spell != null && spell.isInstantOrSorcery(game)) { - playerMap.compute(spell.getControllerId(), (u, i) -> i == null ? 1 : Integer.sum(i, 1)); + playerMap.compute(spell.getControllerId(), CardUtil::setOrIncrementValue); } } diff --git a/Mage.Sets/src/mage/cards/r/RiphookRaider.java b/Mage.Sets/src/mage/cards/r/RiphookRaider.java new file mode 100644 index 00000000000..f423c55b5f8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RiphookRaider.java @@ -0,0 +1,42 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.keyword.DauntAbility; +import mage.abilities.keyword.NightboundAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RiphookRaider extends CardImpl { + + public RiphookRaider(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); + + this.subtype.add(SubType.WEREWOLF); + this.power = new MageInt(6); + this.toughness = new MageInt(4); + this.color.setGreen(true); + this.nightCard = true; + + // Riphook Raider can't be blocked by creatures with power 2 or less. + this.addAbility(new DauntAbility()); + + // Nightbound + this.addAbility(new NightboundAbility()); + } + + private RiphookRaider(final RiphookRaider card) { + super(card); + } + + @Override + public RiphookRaider copy() { + return new RiphookRaider(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RiseOfTheAnts.java b/Mage.Sets/src/mage/cards/r/RiseOfTheAnts.java index afb446d9c5e..35039a6d118 100644 --- a/Mage.Sets/src/mage/cards/r/RiseOfTheAnts.java +++ b/Mage.Sets/src/mage/cards/r/RiseOfTheAnts.java @@ -7,7 +7,7 @@ import mage.abilities.keyword.FlashbackAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.game.permanent.token.RiseOfTheAntsToken; +import mage.game.permanent.token.RiseOfTheAntsInsectToken; import java.util.UUID; @@ -20,7 +20,7 @@ public final class RiseOfTheAnts extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{G}{G}"); // Create two 3/3 green Insect creature tokens. You gain 2 life. - this.getSpellAbility().addEffect(new CreateTokenEffect(new RiseOfTheAntsToken(), 2)); + this.getSpellAbility().addEffect(new CreateTokenEffect(new RiseOfTheAntsInsectToken(), 2)); this.getSpellAbility().addEffect(new GainLifeEffect(2).concatBy(".")); // Flashback {6}{G}{G} diff --git a/Mage.Sets/src/mage/cards/r/RishkarPeemaRenegade.java b/Mage.Sets/src/mage/cards/r/RishkarPeemaRenegade.java index 963bb2ccd04..4666bc3d435 100644 --- a/Mage.Sets/src/mage/cards/r/RishkarPeemaRenegade.java +++ b/Mage.Sets/src/mage/cards/r/RishkarPeemaRenegade.java @@ -43,7 +43,7 @@ public final class RishkarPeemaRenegade extends CardImpl { // When Rishkar, Peema Renegade enters the battlefield, put a +1/+1 counter on each of up to two target creatures. Effect effect = new AddCountersTargetEffect(CounterType.P1P1.createInstance()); - effect.setText("Put a +1/+1 counter on each of up to two target creatures"); + effect.setText("put a +1/+1 counter on each of up to two target creatures"); Ability ability = new EntersBattlefieldTriggeredAbility(effect, false); ability.addTarget(new TargetCreaturePermanent(0, 2)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/r/RiskyMove.java b/Mage.Sets/src/mage/cards/r/RiskyMove.java index 4bdc0a3363e..c6769da6b51 100644 --- a/Mage.Sets/src/mage/cards/r/RiskyMove.java +++ b/Mage.Sets/src/mage/cards/r/RiskyMove.java @@ -1,4 +1,3 @@ - package mage.cards.r; import java.util.UUID; @@ -72,21 +71,25 @@ class RiskyMoveGetControlEffect extends OneShotEffect { MageObject sourceObject = source.getSourceObject(game); Permanent sourcePermanent = game.getPermanent(source.getSourceId()); Player newController = game.getPlayer(getTargetPointer().getFirst(game, source)); - if (newController != null && controller != null && sourceObject != null && sourceObject.equals(sourcePermanent)) { + if (newController != null + && controller != null + && sourceObject != null + && sourceObject.equals(sourcePermanent)) { // remove old control effects of the same player for (ContinuousEffect effect : game.getState().getContinuousEffects().getLayeredEffects(game)) { if (effect instanceof GainControlTargetEffect) { UUID checkId = (UUID) effect.getValue("RiskyMoveSourceId"); UUID controllerId = (UUID) effect.getValue("RiskyMoveControllerId"); - if (source.getSourceId().equals(checkId) && newController.getId().equals(controllerId)) { + if (source.getSourceId().equals(checkId) + && newController.getId().equals(controllerId)) { effect.discard(); } } } - ContinuousEffect effect = new GainControlTargetEffect(Duration.Custom, true, newController.getId()); + ContinuousEffect effect = new GainControlTargetEffect(Duration.EndOfGame, true, newController.getId()); effect.setValue("RiskyMoveSourceId", source.getSourceId()); effect.setValue("RiskyMoveControllerId", newController.getId()); - effect.setTargetPointer(new FixedTarget(sourcePermanent.getId())); + effect.setTargetPointer(new FixedTarget(sourcePermanent.getId(), game)); effect.setText("and gains control of it"); game.addEffect(effect, source); return true; @@ -122,11 +125,10 @@ class RiskyMoveTriggeredAbility extends TriggeredAbilityImpl { @Override public String getTriggerPhrase() { - return "When you gain control of {this} from another player, " ; + return "When you gain control of {this} from another player, "; } } - class RiskyMoveFlipCoinEffect extends OneShotEffect { public RiskyMoveFlipCoinEffect() { @@ -149,14 +151,18 @@ class RiskyMoveFlipCoinEffect extends OneShotEffect { if (controller != null) { Target target1 = new TargetControlledCreaturePermanent(1, 1, new FilterControlledCreaturePermanent(), true); Target target2 = new TargetOpponent(true); - + if (target1.canChoose(source.getSourceId(), controller.getId(), game)) { - while (!target1.isChosen() && target1.canChoose(source.getSourceId(), controller.getId(), game) && controller.canRespond()) { + while (!target1.isChosen() + && target1.canChoose(source.getSourceId(), controller.getId(), game) + && controller.canRespond()) { controller.chooseTarget(outcome, target1, source, game); } } if (target2.canChoose(source.getSourceId(), controller.getId(), game)) { - while (!target2.isChosen() && target2.canChoose(source.getSourceId(), controller.getId(), game) && controller.canRespond()) { + while (!target2.isChosen() + && target2.canChoose(source.getSourceId(), controller.getId(), game) + && controller.canRespond()) { controller.chooseTarget(outcome, target2, source, game); } } @@ -164,7 +170,7 @@ class RiskyMoveFlipCoinEffect extends OneShotEffect { Player chosenOpponent = game.getPlayer(target2.getFirstTarget()); if (!controller.flipCoin(source, game, true)) { if (permanent != null && chosenOpponent != null) { - ContinuousEffect effect = new RiskyMoveCreatureGainControlEffect(Duration.Custom, chosenOpponent.getId()); + ContinuousEffect effect = new RiskyMoveCreatureGainControlEffect(Duration.EndOfGame, chosenOpponent.getId()); effect.setTargetPointer(new FixedTarget(permanent, game)); game.addEffect(effect, source); game.informPlayers(chosenOpponent.getLogName() + " has gained control of " + permanent.getLogName()); diff --git a/Mage.Sets/src/mage/cards/r/RisonaAsariCommander.java b/Mage.Sets/src/mage/cards/r/RisonaAsariCommander.java new file mode 100644 index 00000000000..4dd7fc8d99a --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RisonaAsariCommander.java @@ -0,0 +1,116 @@ +package mage.cards.r; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.effects.common.counter.RemoveCounterSourceEffect; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.events.DamagedBatchEvent; +import mage.game.events.DamagedEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; + +/** + * + * @author weirddan455 + */ +public final class RisonaAsariCommander extends CardImpl { + + public RisonaAsariCommander(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SAMURAI); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // Whenever Risona, Asari Commander deals combat damage to a player, if it doesn't have an indestructible counter on it, put an indestructible counter on it. + this.addAbility(new ConditionalInterveningIfTriggeredAbility( + new DealsCombatDamageToAPlayerTriggeredAbility(new AddCountersSourceEffect(CounterType.INDESTRUCTIBLE.createInstance()), false), + RisonaAsariCommanderCondition.instance, + "Whenever {this} deals combat damage to a player, if it doesn't have an indestructible counter on it, put an indestructible counter on it." + )); + + // Whenever combat damage is dealt to you, remove an indestructible counter from Risona. + this.addAbility(new RisonaAsariCommanderTriggeredAbility()); + } + + private RisonaAsariCommander(final RisonaAsariCommander card) { + super(card); + } + + @Override + public RisonaAsariCommander copy() { + return new RisonaAsariCommander(this); + } +} + +class RisonaAsariCommanderTriggeredAbility extends TriggeredAbilityImpl { + + public RisonaAsariCommanderTriggeredAbility() { + super(Zone.BATTLEFIELD, new RemoveCounterSourceEffect(CounterType.INDESTRUCTIBLE.createInstance())); + } + + private RisonaAsariCommanderTriggeredAbility(final RisonaAsariCommanderTriggeredAbility ability) { + super(ability); + } + + @Override + public RisonaAsariCommanderTriggeredAbility copy() { + return new RisonaAsariCommanderTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DAMAGED_PLAYER_BATCH; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (controllerId == null) { + return false; + } + if (!(event instanceof DamagedBatchEvent)) { + return false; + } + DamagedBatchEvent batchEvent = (DamagedBatchEvent) event; + for (DamagedEvent damageEvent : batchEvent.getEvents()) { + if (damageEvent.isCombatDamage() && controllerId.equals(damageEvent.getTargetId())) { + return true; + } + } + return false; + } + + @Override + public String getTriggerPhrase() { + return "Whenever combat damage is dealt to you, "; + } +} + +enum RisonaAsariCommanderCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + return permanent != null && permanent.getCounters(game).getCount(CounterType.INDESTRUCTIBLE) == 0; + } +} diff --git a/Mage.Sets/src/mage/cards/r/RiteOfBelzenlok.java b/Mage.Sets/src/mage/cards/r/RiteOfBelzenlok.java index 9e421c20ab2..a4b024b26ef 100644 --- a/Mage.Sets/src/mage/cards/r/RiteOfBelzenlok.java +++ b/Mage.Sets/src/mage/cards/r/RiteOfBelzenlok.java @@ -24,7 +24,7 @@ public final class RiteOfBelzenlok extends CardImpl { this.subtype.add(SubType.SAGA); // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) - SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); + SagaAbility sagaAbility = new SagaAbility(this); // I, II — Create two 0/1 black Cleric creature tokens. sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_II, new CreateTokenEffect(new BelzenlokClericToken(), 2)); // III — Create a 6/6 black Demon creature token with flying, trample, and "At the beginning of your upkeep, sacrifice another creature. If you can't, this creature deals 6 damage to you." diff --git a/Mage.Sets/src/mage/cards/r/RiteOfTheSerpent.java b/Mage.Sets/src/mage/cards/r/RiteOfTheSerpent.java index 9d89ab814d9..beaf1149ddd 100644 --- a/Mage.Sets/src/mage/cards/r/RiteOfTheSerpent.java +++ b/Mage.Sets/src/mage/cards/r/RiteOfTheSerpent.java @@ -63,7 +63,7 @@ class RiteOfTheSerpentEffect extends OneShotEffect { Permanent targetCreature = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); if (targetCreature != null) { if (targetCreature.getCounters(game).containsKey(CounterType.P1P1)) { - new CreateTokenEffect(new SnakeToken("KTK")).apply(game, source); + new CreateTokenEffect(new SnakeToken()).apply(game, source); } return true; } diff --git a/Mage.Sets/src/mage/cards/r/RivalsDuel.java b/Mage.Sets/src/mage/cards/r/RivalsDuel.java index ab2aed77709..633080b3f55 100644 --- a/Mage.Sets/src/mage/cards/r/RivalsDuel.java +++ b/Mage.Sets/src/mage/cards/r/RivalsDuel.java @@ -41,7 +41,8 @@ class RivalsDuelFightTargetsEffect extends OneShotEffect { public RivalsDuelFightTargetsEffect() { super(Outcome.Damage); - staticText = "Choose two target creatures that share no creature types. Those creatures fight each other"; + staticText = "Choose two target creatures that share no creature types. " + + "Those creatures fight each other. (Each deals damage equal to its power to the other.)"; } public RivalsDuelFightTargetsEffect(final RivalsDuelFightTargetsEffect effect) { diff --git a/Mage.Sets/src/mage/cards/r/RiverfallMimic.java b/Mage.Sets/src/mage/cards/r/RiverfallMimic.java index 9d61c7ef789..3f4891678a1 100644 --- a/Mage.Sets/src/mage/cards/r/RiverfallMimic.java +++ b/Mage.Sets/src/mage/cards/r/RiverfallMimic.java @@ -32,7 +32,7 @@ public final class RiverfallMimic extends CardImpl { filter.add(new ColorPredicate(ObjectColor.RED)); } - private String rule = "Whenever you cast a spell that's both blue and red, {this} has base power and toughness 3/3 until end of turn and can't be blocked this turn."; + private static final String rule = "Whenever you cast a spell that's both blue and red, {this} has base power and toughness 3/3 until end of turn and can't be blocked this turn."; public RiverfallMimic(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U/R}"); diff --git a/Mage.Sets/src/mage/cards/r/RiversGrasp.java b/Mage.Sets/src/mage/cards/r/RiversGrasp.java index d6bdf5b16cb..95b3fb8426e 100644 --- a/Mage.Sets/src/mage/cards/r/RiversGrasp.java +++ b/Mage.Sets/src/mage/cards/r/RiversGrasp.java @@ -1,26 +1,20 @@ - package mage.cards.r; import mage.abilities.Ability; import mage.abilities.condition.common.ManaWasSpentCondition; import mage.abilities.decorator.ConditionalOneShotEffect; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.InfoEffect; import mage.abilities.effects.common.ReturnToHandTargetEffect; -import mage.cards.Card; +import mage.abilities.effects.common.discard.DiscardCardYouChooseTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.ColoredManaSymbol; -import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.TargetController; import mage.filter.StaticFilters; -import mage.game.Game; -import mage.players.Player; -import mage.target.Target; -import mage.target.TargetCard; import mage.target.TargetPlayer; import mage.target.common.TargetCreaturePermanent; +import mage.target.targetpointer.SecondTargetPointer; import java.util.UUID; @@ -33,17 +27,18 @@ public final class RiversGrasp extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{U/B}"); // If {U} was spent to cast River's Grasp, return up to one target creature to its owner's hand. If {B} was spent to cast River's Grasp, target player reveals their hand, you choose a nonland card from it, then that player discards that card. - Target targetCreature = new TargetCreaturePermanent(0, 1); - Target targetPlayer = new TargetPlayer(); this.getSpellAbility().addEffect(new ConditionalOneShotEffect( new ReturnToHandTargetEffect(), - new ManaWasSpentCondition(ColoredManaSymbol.U), "If {U} was spent to cast this spell, return up to one target creature to its owner's hand")); + new ManaWasSpentCondition(ColoredManaSymbol.U), + "If {U} was spent to cast this spell, return up to one target creature to its owner's hand")); this.getSpellAbility().addEffect(new ConditionalOneShotEffect( - new RiversGraspEffect(), - new ManaWasSpentCondition(ColoredManaSymbol.B), " If {B} was spent to cast this spell, target player reveals their hand, you choose a nonland card from it, then that player discards that card")); + new DiscardCardYouChooseTargetEffect(StaticFilters.FILTER_CARD_NON_LAND, TargetController.ANY), + new ManaWasSpentCondition(ColoredManaSymbol.B), + " If {B} was spent to cast this spell, target player reveals their hand, you choose a nonland card from it, then that player discards that card") + .setTargetPointer(new SecondTargetPointer())); - this.getSpellAbility().addTarget(targetCreature); - this.getSpellAbility().addTarget(targetPlayer); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(0, 1)); + this.getSpellAbility().addTarget(new TargetPlayer()); this.getSpellAbility().addEffect(new InfoEffect("(Do both if {U}{B} was spent.)")); } @@ -57,38 +52,3 @@ public final class RiversGrasp extends CardImpl { return new RiversGrasp(this); } } - -class RiversGraspEffect extends OneShotEffect { - - public RiversGraspEffect() { - super(Outcome.Discard); - this.staticText = "Target player reveals their hand, you choose a card from it, then that player discards that card."; - } - - public RiversGraspEffect(final RiversGraspEffect effect) { - super(effect); - } - - @Override - public RiversGraspEffect copy() { - return new RiversGraspEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getTargets().get(1).getFirstTarget()); - if (player != null) { - player.revealCards("River's Grasp", player.getHand(), game); - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - TargetCard target = new TargetCard(Zone.HAND, StaticFilters.FILTER_CARD_A_NON_LAND); - if (controller.choose(Outcome.Benefit, player.getHand(), target, game)) { - Card card = player.getHand().get(target.getFirstTarget(), game); - return player.discard(card, false, source, game); - - } - } - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/r/RoadsideReliquary.java b/Mage.Sets/src/mage/cards/r/RoadsideReliquary.java new file mode 100644 index 00000000000..8082d20acbb --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RoadsideReliquary.java @@ -0,0 +1,68 @@ +package mage.cards.r; + +import java.util.UUID; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.abilities.mana.ColorlessManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; + +/** + * + * @author weirddan455 + */ +public final class RoadsideReliquary extends CardImpl { + + private static final Condition artifactCondition = new PermanentsOnTheBattlefieldCondition(StaticFilters.FILTER_PERMANENT_ARTIFACT); + private static final Condition enchantmentCondition = new PermanentsOnTheBattlefieldCondition(StaticFilters.FILTER_PERMANENT_ENCHANTMENT); + private static final Hint artifactHint = new ConditionHint(artifactCondition, "You control an artifact"); + private static final Hint enchantmentHint = new ConditionHint(enchantmentCondition, "You control an enchantment"); + + public RoadsideReliquary(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // {T}: Add {C}. + this.addAbility(new ColorlessManaAbility()); + + // {2}, {T}, Sacrifice Roadside Reliquary: Draw a card if you control an artifact. Draw a card if you control an enchantment. + Ability ability = new SimpleActivatedAbility( + new ConditionalOneShotEffect( + new DrawCardSourceControllerEffect(1), + artifactCondition, + "Draw a card if you control an artifact" + ), + new GenericManaCost(2) + ); + ability.addCost(new TapSourceCost()); + ability.addCost(new SacrificeSourceCost()); + ability.addEffect(new ConditionalOneShotEffect( + new DrawCardSourceControllerEffect(1), + enchantmentCondition, + "Draw a card if you control an enchantment" + )); + ability.addHint(artifactHint); + ability.addHint(enchantmentHint); + this.addAbility(ability); + } + + private RoadsideReliquary(final RoadsideReliquary card) { + super(card); + } + + @Override + public RoadsideReliquary copy() { + return new RoadsideReliquary(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RoaringEarth.java b/Mage.Sets/src/mage/cards/r/RoaringEarth.java new file mode 100644 index 00000000000..5cb9e90e011 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RoaringEarth.java @@ -0,0 +1,72 @@ +package mage.cards.r; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.dynamicvalue.common.ManacostVariableValue; +import mage.abilities.effects.common.continuous.BecomesCreatureTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.ChannelAbility; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.game.permanent.token.custom.CreatureToken; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RoaringEarth extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent("creature or Vehicle you control"); + + static { + filter.add(Predicates.or( + CardType.CREATURE.getPredicate(), + SubType.VEHICLE.getPredicate() + )); + } + + public RoaringEarth(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{G}"); + + // Whenever a land enters the battlefield under your control, put a +1/+1 counter on target creature or Vehicle you control. + Ability ability = new EntersBattlefieldControlledTriggeredAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance()), StaticFilters.FILTER_LAND_A + ); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + + // Channel — {X}{G}{G}, Discard Roaring Earth; Put X +1/+1 counters on target land you control. It becomes a 0/0 green Spirit creature with haste. It's still a land. + ability = new ChannelAbility("{X}{G}{G}", new AddCountersTargetEffect( + CounterType.P1P1.createInstance(0), ManacostVariableValue.REGULAR + ).setText("Put X +1/+1 counters on target land you control.")); + ability.addEffect(new BecomesCreatureTargetEffect( + new CreatureToken(0, 0) + .withColor("G") + .withSubType(SubType.SPIRIT) + .withAbility(HasteAbility.getInstance()), + false, true, Duration.Custom + ).setText("It becomes a 0/0 green Spirit creature with haste. It's still a land")); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND)); + this.addAbility(ability); + } + + private RoaringEarth(final RoaringEarth card) { + super(card); + } + + @Override + public RoaringEarth copy() { + return new RoaringEarth(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RobberFly.java b/Mage.Sets/src/mage/cards/r/RobberFly.java index b1497fa78a6..2dd018a61d3 100644 --- a/Mage.Sets/src/mage/cards/r/RobberFly.java +++ b/Mage.Sets/src/mage/cards/r/RobberFly.java @@ -5,12 +5,12 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.constants.SubType; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; import mage.players.Player; @@ -32,8 +32,7 @@ public final class RobberFly extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever Robber Fly becomes blocked, defending player discards all the cards in their hand, then draws that many cards. - this.addAbility(new BecomesBlockedSourceTriggeredAbility( - Zone.BATTLEFIELD, new DrawCardsDefendingPlayerEffect(), false, true)); + this.addAbility(new BecomesBlockedSourceTriggeredAbility(new DrawCardsDefendingPlayerEffect(), false, true)); } private RobberFly(final RobberFly card) { @@ -76,5 +75,4 @@ class DrawCardsDefendingPlayerEffect extends OneShotEffect { } return false; } - } diff --git a/Mage.Sets/src/mage/cards/r/RobberOfTheRich.java b/Mage.Sets/src/mage/cards/r/RobberOfTheRich.java index 08592b2ce27..4d8298f83d5 100644 --- a/Mage.Sets/src/mage/cards/r/RobberOfTheRich.java +++ b/Mage.Sets/src/mage/cards/r/RobberOfTheRich.java @@ -14,13 +14,15 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.game.Game; -import mage.game.permanent.Permanent; import mage.players.Player; import mage.util.CardUtil; import mage.watchers.common.AttackedThisTurnWatcher; - import java.util.Objects; import java.util.UUID; +import mage.abilities.decorator.ConditionalAsThoughEffect; +import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; +import mage.abilities.effects.common.asthought.YouMaySpendManaAsAnyColorToCastTargetEffect; +import mage.target.targetpointer.FixedTarget; /** * @author TheElk801 @@ -50,7 +52,7 @@ public final class RobberOfTheRich extends CardImpl { "if defending player has more cards in hand than you, exile the top card of their library. " + "During any turn you attacked with a Rogue, you may cast that card and " + "you may spend mana as though it were mana of any color to cast that spell." - ).addHint(new ConditionHint(RobberOfTheRichAnyTurnAttackedCondition.instance)), new AttackedThisTurnWatcher()); + ).addHint(new ConditionHint(new RogueAttackedThisTurnCondition(null))), new AttackedThisTurnWatcher()); } private RobberOfTheRich(final RobberOfTheRich card) { @@ -70,24 +72,36 @@ enum RobberOfTheRichAttacksCondition implements Condition { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); Player player = game.getPlayer(game.getCombat().getDefendingPlayerId(source.getSourceId(), game)); - return controller != null && player != null && controller.getHand().size() < player.getHand().size(); + return controller != null + && player != null + && controller.getHand().size() < player.getHand().size(); } } -enum RobberOfTheRichAnyTurnAttackedCondition implements Condition { - instance; +class RogueAttackedThisTurnCondition implements Condition { + + private Ability ability; + + RogueAttackedThisTurnCondition(Ability source) { + this.ability = source; + } @Override public boolean apply(Game game, Ability source) { + // in case the Robber leaves the battlefield, the ability must be referenced for controller information + if (ability == null) { + ability = source; + } // your turn - if (!source.isControlledBy(game.getActivePlayerId())) { + if (!ability.isControlledBy(game.getActivePlayerId())) { return false; } // attacked with Rogue + // note that the MOR object doesn't work well with LKI call when checking for the subtype, thus we check the LKI permanent in the battlefield AttackedThisTurnWatcher watcher = game.getState().getWatcher(AttackedThisTurnWatcher.class); - return watcher != null && watcher.getAttackedThisTurnCreatures() + return watcher != null && watcher.getAttackedThisTurnCreaturesPermanentLKI() .stream() - .map(mor -> mor.getPermanentOrLKIBattlefield(game)) + .map(permanent -> permanent) .filter(Objects::nonNull) .anyMatch(permanent -> permanent.hasSubtype(SubType.ROGUE, game)); } @@ -101,7 +115,7 @@ enum RobberOfTheRichAnyTurnAttackedCondition implements Condition { class RobberOfTheRichEffect extends OneShotEffect { RobberOfTheRichEffect() { - super(Outcome.PutCreatureInPlay); + super(Outcome.Benefit); } private RobberOfTheRichEffect(final RobberOfTheRichEffect effect) { @@ -117,23 +131,36 @@ class RobberOfTheRichEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); Player damagedPlayer = game.getPlayer(this.getTargetPointer().getFirst(game, source)); - if (controller == null || damagedPlayer == null) { + if (controller == null + || damagedPlayer == null) { return false; } - Permanent sourceObject = source.getSourcePermanentIfItStillExists(game); Card card = damagedPlayer.getLibrary().getFromTop(game); - if (card == null || sourceObject == null) { + if (card == null) { return false; } // move card to exile - controller.moveCardsToExile(card, source, game, true, CardUtil.getExileZoneId(game, source), sourceObject.getIdName()); - // Add effects only if the card has a spellAbility (e.g. not for lands). + controller.moveCardsToExile(card, source, game, true, CardUtil.getExileZoneId(game, source), CardUtil.getSourceName(game, source)); + // add the effects to the exiled card directly + // don't worry about land if (card.getSpellAbility() != null) { - // allow to cast the card - // and you may spend mana as though it were mana of any color to cast it - CardUtil.makeCardPlayable(game, source, card, Duration.Custom, true, RobberOfTheRichAnyTurnAttackedCondition.instance); + // the exiled card is independent and requires a new ability in case the Robber leaves the battlefield + // the exiled card can be cast throughout the entire game as long as the controller attacked with a rogue that turn + Ability copiedAbility = source.copy(); + copiedAbility.newId(); + copiedAbility.setSourceId(card.getId()); + copiedAbility.setControllerId(source.getControllerId()); + PlayFromNotOwnHandZoneTargetEffect playFromExile = new PlayFromNotOwnHandZoneTargetEffect(Zone.EXILED, Duration.EndOfGame); + YouMaySpendManaAsAnyColorToCastTargetEffect spendAnyMana = new YouMaySpendManaAsAnyColorToCastTargetEffect(Duration.EndOfGame); + ConditionalAsThoughEffect castOnlyIfARogueAttackedThisTurn = new ConditionalAsThoughEffect(playFromExile, new RogueAttackedThisTurnCondition(copiedAbility)); + playFromExile.setTargetPointer(new FixedTarget(card, game)); + spendAnyMana.setTargetPointer(new FixedTarget(card, game)); + castOnlyIfARogueAttackedThisTurn.setTargetPointer(new FixedTarget(card, game)); + game.addEffect(castOnlyIfARogueAttackedThisTurn, copiedAbility); + game.addEffect(spendAnyMana, copiedAbility); + return true; } - return true; + return false; } } diff --git a/Mage.Sets/src/mage/cards/r/RockSlide.java b/Mage.Sets/src/mage/cards/r/RockSlide.java index 10e2125a0b2..102ec690c7b 100644 --- a/Mage.Sets/src/mage/cards/r/RockSlide.java +++ b/Mage.Sets/src/mage/cards/r/RockSlide.java @@ -1,4 +1,3 @@ - package mage.cards.r; import java.util.UUID; @@ -9,11 +8,9 @@ import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.common.FilterAttackingOrBlockingCreature; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.AbilityPredicate; -import mage.filter.predicate.permanent.AttackingPredicate; -import mage.filter.predicate.permanent.BlockingPredicate; import mage.target.common.TargetCreaturePermanentAmount; /** @@ -22,13 +19,9 @@ import mage.target.common.TargetCreaturePermanentAmount; */ public final class RockSlide extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("attacking or blocking creatures without flying"); + private static final FilterAttackingOrBlockingCreature filter = new FilterAttackingOrBlockingCreature("attacking or blocking creatures without flying"); static { - filter.add(Predicates.or( - AttackingPredicate.instance, - BlockingPredicate.instance - )); filter.add(Predicates.not(new AbilityPredicate(FlyingAbility.class))); } diff --git a/Mage.Sets/src/mage/cards/r/RocketLauncher.java b/Mage.Sets/src/mage/cards/r/RocketLauncher.java index 22074458c82..3563189cab9 100644 --- a/Mage.Sets/src/mage/cards/r/RocketLauncher.java +++ b/Mage.Sets/src/mage/cards/r/RocketLauncher.java @@ -1,8 +1,5 @@ - package mage.cards.r; -import java.util.Objects; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.condition.Condition; @@ -14,30 +11,30 @@ import mage.abilities.effects.common.DestroySourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.WatcherScope; import mage.constants.Zone; import mage.game.Game; -import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; import mage.target.common.TargetAnyTarget; -import mage.watchers.Watcher; + +import java.util.UUID; /** - * - * @author MarcoMarin + * @author TheElk801 */ public final class RocketLauncher extends CardImpl { public RocketLauncher(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{4}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); - Watcher watcher = new RocketLauncherWatcher(this.getId()); // {2}: Rocket Launcher deals 1 damage to any target. Destroy Rocket Launcher at the beginning of the next end step. Activate this ability only if you've controlled Rocket Launcher continuously since the beginning of your most recent turn. - Ability ability = new ConditionalActivatedAbility(Zone.BATTLEFIELD, - new DamageTargetEffect(1), new GenericManaCost(2), ControlledTurnCondition.instance); + Ability ability = new ConditionalActivatedAbility( + Zone.BATTLEFIELD, new DamageTargetEffect(1), + new GenericManaCost(2), RocketLauncherCondition.instance); ability.addTarget(new TargetAnyTarget()); - ability.addEffect(new CreateDelayedTriggeredAbilityEffect(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new DestroySourceEffect(true)))); - - this.addAbility(ability, watcher); + ability.addEffect(new CreateDelayedTriggeredAbilityEffect( + new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new DestroySourceEffect(true)) + ).setText("destroy {this} at the beginning of the next end step")); + this.addAbility(ability); } private RocketLauncher(final RocketLauncher card) { @@ -50,56 +47,17 @@ public final class RocketLauncher extends CardImpl { } } -class RocketLauncherWatcher extends Watcher { - - private boolean changedControllerOR1stTurn; - private UUID cardId = null; - - public RocketLauncherWatcher(UUID cardId) { - super(WatcherScope.GAME); - this.changedControllerOR1stTurn = true; - this.cardId = cardId; - } - - @Override - public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.CLEANUP_STEP_POST) { - changedControllerOR1stTurn = false; - } - if (event.getType() == GameEvent.EventType.LOST_CONTROL && - Objects.equals(event.getTargetId(), cardId)) { - changedControllerOR1stTurn = true; - } - } - - - @Override - public void reset() { - super.reset(); - changedControllerOR1stTurn = true; //when is this reset called? may cause problems if in mid-life - } - - public boolean isChangedControllerOR1stTurn() { - return changedControllerOR1stTurn; - } -} - -enum ControlledTurnCondition implements Condition { - +enum RocketLauncherCondition implements Condition { instance; - @Override public boolean apply(Game game, Ability source) { - RocketLauncherWatcher watcher = game.getState().getWatcher(RocketLauncherWatcher.class); - - return watcher != null && !watcher.isChangedControllerOR1stTurn(); + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + return permanent != null && permanent.wasControlledFromStartOfControllerTurn(); } @Override public String toString() { - return "Permanent hasn't changed controller NOR entered this turn"; + return "you've controlled {this} continuously since the beginning of your most recent turn"; } - - -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/r/RograkhSonOfRohgahh.java b/Mage.Sets/src/mage/cards/r/RograkhSonOfRohgahh.java index bef6a014d81..45f1b406b27 100644 --- a/Mage.Sets/src/mage/cards/r/RograkhSonOfRohgahh.java +++ b/Mage.Sets/src/mage/cards/r/RograkhSonOfRohgahh.java @@ -32,7 +32,7 @@ public final class RograkhSonOfRohgahh extends CardImpl { this.addAbility(FirstStrikeAbility.getInstance()); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // Trample this.addAbility(TrampleAbility.getInstance()); diff --git a/Mage.Sets/src/mage/cards/r/RogueClass.java b/Mage.Sets/src/mage/cards/r/RogueClass.java index 78e9c3ff09b..34a6ab2050f 100644 --- a/Mage.Sets/src/mage/cards/r/RogueClass.java +++ b/Mage.Sets/src/mage/cards/r/RogueClass.java @@ -1,6 +1,5 @@ package mage.cards.r; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.DealsDamageToAPlayerAllTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; @@ -51,7 +50,7 @@ public final class RogueClass extends CardImpl { // Creatures you control have menace. this.addAbility(new SimpleStaticAbility(new GainClassAbilitySourceEffect( new GainAbilityControlledEffect( - new MenaceAbility(), Duration.WhileOnBattlefield, + new MenaceAbility(false), Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURES ), 2 ))); @@ -103,11 +102,10 @@ class RogueClassExileEffect extends OneShotEffect { if (card == null) { return false; } - MageObject sourceObject = source.getSourcePermanentOrLKI(game); controller.moveCardsToExile( card, source, game, false, CardUtil.getExileZoneId(game, source), - sourceObject != null ? sourceObject.getName() : null + CardUtil.getSourceName(game, source) ); card.setFaceDown(true, game); game.addEffect(new RogueClassLookEffect().setTargetPointer(new FixedTarget(card, game)), source); diff --git a/Mage.Sets/src/mage/cards/r/RogueKavu.java b/Mage.Sets/src/mage/cards/r/RogueKavu.java index 661ebf8f90f..d1106f31bf8 100644 --- a/Mage.Sets/src/mage/cards/r/RogueKavu.java +++ b/Mage.Sets/src/mage/cards/r/RogueKavu.java @@ -3,7 +3,7 @@ package mage.cards.r; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.AttacksAloneTriggeredAbility; +import mage.abilities.common.AttacksAloneSourceTriggeredAbility; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -24,7 +24,7 @@ public final class RogueKavu extends CardImpl { this.toughness = new MageInt(1); // Whenever Rogue Kavu attacks alone, it gets +2/+0 until end of turn. - this.addAbility(new AttacksAloneTriggeredAbility(new BoostSourceEffect(2, 0, Duration.EndOfTurn))); + this.addAbility(new AttacksAloneSourceTriggeredAbility(new BoostSourceEffect(2, 0, Duration.EndOfTurn))); } private RogueKavu(final RogueKavu card) { diff --git a/Mage.Sets/src/mage/cards/r/RoilElemental.java b/Mage.Sets/src/mage/cards/r/RoilElemental.java index 410af04027a..b12fb6d4541 100644 --- a/Mage.Sets/src/mage/cards/r/RoilElemental.java +++ b/Mage.Sets/src/mage/cards/r/RoilElemental.java @@ -1,21 +1,20 @@ package mage.cards.r; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.LandfallAbility; -import mage.abilities.condition.common.SourceOnBattlefieldControlUnchangedCondition; -import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.common.continuous.GainControlTargetEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; import mage.target.common.TargetCreaturePermanent; -import mage.watchers.common.LostControlWatcher; + +import java.util.UUID; /** - * * @author jeffwadsworth */ public final class RoilElemental extends CardImpl { @@ -27,16 +26,12 @@ public final class RoilElemental extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(2); - String rule = "you may gain control of target creature for as long as you control Roil Elemental"; - // Flying this.addAbility(FlyingAbility.getInstance()); // Landfall - Whenever a land enters the battlefield under your control, you may gain control of target creature for as long as you control Roil Elemental. - ConditionalContinuousEffect effect = new ConditionalContinuousEffect(new GainControlTargetEffect(Duration.Custom), new SourceOnBattlefieldControlUnchangedCondition(), rule); - Ability ability = new LandfallAbility(Zone.BATTLEFIELD, effect, true); + Ability ability = new LandfallAbility(new GainControlTargetEffect(Duration.WhileControlled), true); ability.addTarget(new TargetCreaturePermanent()); - ability.addWatcher(new LostControlWatcher()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/r/RoilsRetribution.java b/Mage.Sets/src/mage/cards/r/RoilsRetribution.java index b8500b5e035..d3ac9b51a63 100644 --- a/Mage.Sets/src/mage/cards/r/RoilsRetribution.java +++ b/Mage.Sets/src/mage/cards/r/RoilsRetribution.java @@ -1,4 +1,3 @@ - package mage.cards.r; import java.util.UUID; @@ -6,7 +5,7 @@ import mage.abilities.effects.common.DamageMultiEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterAttackingOrBlockingCreature; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanentAmount; /** @@ -20,7 +19,7 @@ public final class RoilsRetribution extends CardImpl { // Roil's Retribution deals 5 damage divided as you choose among any number of target attacking or blocking creatures. this.getSpellAbility().addEffect(new DamageMultiEffect(5)); - this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount(5, new FilterAttackingOrBlockingCreature("attacking or blocking creatures"))); + this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount(5, StaticFilters.FILTER_ATTACKING_OR_BLOCKING_CREATURES)); } private RoilsRetribution(final RoilsRetribution card) { diff --git a/Mage.Sets/src/mage/cards/r/RollingThunder.java b/Mage.Sets/src/mage/cards/r/RollingThunder.java index dcbda74b27a..ae6d7b033a1 100644 --- a/Mage.Sets/src/mage/cards/r/RollingThunder.java +++ b/Mage.Sets/src/mage/cards/r/RollingThunder.java @@ -1,4 +1,3 @@ - package mage.cards.r; import java.util.UUID; @@ -19,7 +18,7 @@ public final class RollingThunder extends CardImpl { public RollingThunder(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{X}{R}{R}"); - // Rolling Thunder deals X damage divided as you choose among any number of target creatures and/or players. + // Rolling Thunder deals X damage divided as you choose among any number of targets. DynamicValue xValue = ManacostVariableValue.REGULAR; this.getSpellAbility().addEffect(new DamageMultiEffect(xValue)); this.getSpellAbility().addTarget(new TargetAnyTargetAmount(xValue)); diff --git a/Mage.Sets/src/mage/cards/r/RootMaze.java b/Mage.Sets/src/mage/cards/r/RootMaze.java index 713624f3bd6..e781d11b31f 100644 --- a/Mage.Sets/src/mage/cards/r/RootMaze.java +++ b/Mage.Sets/src/mage/cards/r/RootMaze.java @@ -1,32 +1,35 @@ package mage.cards.r; -import java.util.UUID; -import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.PermanentsEnterBattlefieldTappedEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.EntersTheBattlefieldEvent; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicates; + +import java.util.UUID; /** - * * @author emerald000 */ public final class RootMaze extends CardImpl { + private static final FilterPermanent filter = new FilterPermanent("artifacts and lands"); + + static { + filter.add(Predicates.or( + CardType.ARTIFACT.getPredicate(), + CardType.LAND.getPredicate() + )); + } + public RootMaze(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{G}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{G}"); // Artifacts and lands enter the battlefield tapped. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new RootMazeEffect())); + this.addAbility(new SimpleStaticAbility(new PermanentsEnterBattlefieldTappedEffect(filter))); } private RootMaze(final RootMaze card) { @@ -38,40 +41,3 @@ public final class RootMaze extends CardImpl { return new RootMaze(this); } } - -class RootMazeEffect extends ReplacementEffectImpl { - - RootMazeEffect() { - super(Duration.WhileOnBattlefield, Outcome.Tap); - staticText = "Artifacts and lands enter the battlefield tapped"; - } - - RootMazeEffect(final RootMazeEffect effect) { - super(effect); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Permanent target = ((EntersTheBattlefieldEvent) event).getTarget(); - if (target != null) { - target.setTapped(true); - } - return false; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget(); - return permanent != null && (permanent.isLand(game) || permanent.isArtifact(game)); - } - - @Override - public RootMazeEffect copy() { - return new RootMazeEffect(this); - } -} diff --git a/Mage.Sets/src/mage/cards/r/RotTideGargantua.java b/Mage.Sets/src/mage/cards/r/RotTideGargantua.java new file mode 100644 index 00000000000..2bb0443ee34 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RotTideGargantua.java @@ -0,0 +1,43 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.common.ExploitCreatureTriggeredAbility; +import mage.abilities.effects.common.SacrificeOpponentsEffect; +import mage.abilities.keyword.ExploitAbility; +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 RotTideGargantua extends CardImpl { + + public RotTideGargantua(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{B}"); + + this.subtype.add(SubType.ZOMBIE); + this.subtype.add(SubType.KRAKEN); + this.power = new MageInt(5); + this.toughness = new MageInt(4); + + // Exploit + this.addAbility(new ExploitAbility()); + + // When Rot-Tide Gargantua exploits a creature, each opponent sacrifices a creature. + this.addAbility(new ExploitCreatureTriggeredAbility(new SacrificeOpponentsEffect(StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT))); + } + + private RotTideGargantua(final RotTideGargantua card) { + super(card); + } + + @Override + public RotTideGargantua copy() { + return new RotTideGargantua(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RovingKeep.java b/Mage.Sets/src/mage/cards/r/RovingKeep.java index 8006368aa06..204524c59d6 100644 --- a/Mage.Sets/src/mage/cards/r/RovingKeep.java +++ b/Mage.Sets/src/mage/cards/r/RovingKeep.java @@ -40,8 +40,7 @@ public final class RovingKeep extends CardImpl { ability.addEffect(new GainAbilitySourceEffect( TrampleAbility.getInstance(), Duration.EndOfTurn ).setText("and gains trample until end of turn")); - ability.addEffect(new CanAttackAsThoughItDidntHaveDefenderSourceEffect(Duration.EndOfTurn) - .setText("It can attack this turn as though it didn't have defender")); + ability.addEffect(new CanAttackAsThoughItDidntHaveDefenderSourceEffect(Duration.EndOfTurn, "it")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/r/RowanFearlessSparkmage.java b/Mage.Sets/src/mage/cards/r/RowanFearlessSparkmage.java index bd5b73ffbfd..1e73be6fd15 100644 --- a/Mage.Sets/src/mage/cards/r/RowanFearlessSparkmage.java +++ b/Mage.Sets/src/mage/cards/r/RowanFearlessSparkmage.java @@ -2,7 +2,6 @@ package mage.cards.r; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.UntapAllEffect; import mage.abilities.effects.common.combat.CantBlockTargetEffect; @@ -18,8 +17,7 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; import mage.constants.SuperType; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; import java.util.UUID; @@ -29,14 +27,12 @@ import java.util.UUID; */ public final class RowanFearlessSparkmage extends CardImpl { - private static final FilterPermanent filter = new FilterCreaturePermanent("all creatures"); - public RowanFearlessSparkmage(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{3}{R}{R}"); this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.ROWAN); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +1: Up to one target creature gets +3/+0 and gains first strike until end of turn. Ability ability = new LoyaltyAbility(new BoostTargetEffect( @@ -57,11 +53,11 @@ public final class RowanFearlessSparkmage extends CardImpl { this.addAbility(ability); // −9: Gain control of all creatures until end of turn. Untap them. They gain haste until end of turn. - ability = new LoyaltyAbility(new GainControlAllEffect(Duration.EndOfTurn, filter) + ability = new LoyaltyAbility(new GainControlAllEffect(Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_ALL_CREATURES) .setText("gain control of all creatures until end of turn."), -9); - ability.addEffect(new UntapAllEffect(filter).setText("Untap them.")); + ability.addEffect(new UntapAllEffect(StaticFilters.FILTER_PERMANENT_ALL_CREATURES).setText("Untap them.")); ability.addEffect(new GainAbilityAllEffect( - HasteAbility.getInstance(), Duration.EndOfTurn, filter + HasteAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_ALL_CREATURES ).setText("They gain haste until end of turn")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/r/RowanKenrith.java b/Mage.Sets/src/mage/cards/r/RowanKenrith.java index 45ec313fe47..fa10de5ed81 100644 --- a/Mage.Sets/src/mage/cards/r/RowanKenrith.java +++ b/Mage.Sets/src/mage/cards/r/RowanKenrith.java @@ -3,7 +3,6 @@ package mage.cards.r; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.common.CanBeYourCommanderAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.RequirementEffect; @@ -33,7 +32,7 @@ public final class RowanKenrith extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.ROWAN); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +2: During target player's next turn, each creature that player controls attacks if able. LoyaltyAbility ability = new LoyaltyAbility(new RowanKenrithAttackEffect(), 2); diff --git a/Mage.Sets/src/mage/cards/r/RowanScholarOfSparks.java b/Mage.Sets/src/mage/cards/r/RowanScholarOfSparks.java index 8d8ff3c1cc8..31fbc9caa24 100644 --- a/Mage.Sets/src/mage/cards/r/RowanScholarOfSparks.java +++ b/Mage.Sets/src/mage/cards/r/RowanScholarOfSparks.java @@ -3,7 +3,6 @@ package mage.cards.r; import mage.MageItem; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.Condition; import mage.abilities.decorator.ConditionalOneShotEffect; @@ -55,7 +54,7 @@ public final class RowanScholarOfSparks extends ModalDoubleFacesCard { // Rowan, Scholar of Sparks // Legendary Planeswalker - Rowan this.getLeftHalfCard().addSuperType(SuperType.LEGENDARY); - this.getLeftHalfCard().addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(2)); + this.getLeftHalfCard().setStartingLoyalty(2); // Instant and sorcery spells you cast cost {1} less to cast. this.getLeftHalfCard().addAbility(new SimpleStaticAbility(new SpellsCostReductionControllerEffect(filter, 1))); @@ -79,7 +78,7 @@ public final class RowanScholarOfSparks extends ModalDoubleFacesCard { // Will, Scholar of Frost // Legendary Planeswalker - Will this.getRightHalfCard().addSuperType(SuperType.LEGENDARY); - this.getRightHalfCard().addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.getRightHalfCard().setStartingLoyalty(4); // Instant and sorcery spells you cast cost {1} less to cast. this.getRightHalfCard().addAbility(new SimpleStaticAbility( diff --git a/Mage.Sets/src/mage/cards/r/RubblebeltMaaka.java b/Mage.Sets/src/mage/cards/r/RubblebeltMaaka.java index 1508fd3f519..5162436657a 100644 --- a/Mage.Sets/src/mage/cards/r/RubblebeltMaaka.java +++ b/Mage.Sets/src/mage/cards/r/RubblebeltMaaka.java @@ -4,6 +4,7 @@ package mage.cards.r; import java.util.UUID; import mage.MageInt; +import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.abilities.keyword.BloodrushAbility; import mage.cards.CardImpl; @@ -11,6 +12,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; +import mage.constants.Outcome; /** * @@ -28,7 +30,9 @@ public final class RubblebeltMaaka extends CardImpl { this.toughness = new MageInt(3); // Bloodrush — {R}, Discard Rubblebelt Maaka: Target attacking creature gets +3/+3 until end of turn. - this.addAbility(new BloodrushAbility("{R}", new BoostTargetEffect(3,3, Duration.EndOfTurn))); + Effect boostEffect = new BoostTargetEffect(3,3, Duration.EndOfTurn); + boostEffect.setOutcome(Outcome.Benefit); + this.addAbility(new BloodrushAbility("{R}", boostEffect)); } diff --git a/Mage.Sets/src/mage/cards/r/RubiniaSoulsinger.java b/Mage.Sets/src/mage/cards/r/RubiniaSoulsinger.java index bd21fd70fc6..066bbe44a1d 100644 --- a/Mage.Sets/src/mage/cards/r/RubiniaSoulsinger.java +++ b/Mage.Sets/src/mage/cards/r/RubiniaSoulsinger.java @@ -1,34 +1,30 @@ - package mage.cards.r; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SkipUntapOptionalAbility; -import mage.abilities.condition.Condition; +import mage.abilities.condition.common.SourceTappedCondition; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.common.continuous.GainControlTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; +import mage.constants.SubType; import mage.constants.SuperType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class RubiniaSoulsinger extends CardImpl { public RubiniaSoulsinger(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{G}{W}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{W}{U}"); addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.FAERIE); @@ -37,12 +33,12 @@ public final class RubiniaSoulsinger extends CardImpl { // You may choose not to untap Rubinia Soulsinger during your untap step. this.addAbility(new SkipUntapOptionalAbility()); + // {tap}: Gain control of target creature for as long as you control Rubinia and Rubinia remains tapped. - ConditionalContinuousEffect effect = new ConditionalContinuousEffect( - new GainControlTargetEffect(Duration.OneUse), - new RubiniaSoulsingerCondition(), - "Gain control of target creature for as long as you control Rubinia and Rubinia remains tapped"); - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new TapSourceCost()); + Ability ability = new SimpleActivatedAbility(new ConditionalContinuousEffect( + new GainControlTargetEffect(Duration.WhileControlled), SourceTappedCondition.TAPPED, + "gain control of target creature for as long as you control {this} and {this} remains tapped" + ), new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } @@ -56,22 +52,3 @@ public final class RubiniaSoulsinger extends CardImpl { return new RubiniaSoulsinger(this); } } - -class RubiniaSoulsingerCondition implements Condition { - - private UUID controllerId; - - @Override - public boolean apply(Game game, Ability source) { - if (controllerId == null) { - controllerId = source.getControllerId(); - } - Permanent permanent = game.getBattlefield().getPermanent(source.getSourceId()); - if (permanent != null) { - if (permanent.isTapped()) { - return controllerId.equals(source.getControllerId()); - } - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/r/RuinGrinder.java b/Mage.Sets/src/mage/cards/r/RuinGrinder.java index 71b53c1ee6b..d93947f22ab 100644 --- a/Mage.Sets/src/mage/cards/r/RuinGrinder.java +++ b/Mage.Sets/src/mage/cards/r/RuinGrinder.java @@ -32,7 +32,7 @@ public final class RuinGrinder extends CardImpl { this.toughness = new MageInt(4); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // When Ruin Grinder dies, each player may discard their hand and draw seven cards. this.addAbility(new DiesSourceTriggeredAbility(new RuinGrinderEffect())); diff --git a/Mage.Sets/src/mage/cards/r/RumblingSentry.java b/Mage.Sets/src/mage/cards/r/RumblingSentry.java index 9e2f92a2069..22e2b43db73 100644 --- a/Mage.Sets/src/mage/cards/r/RumblingSentry.java +++ b/Mage.Sets/src/mage/cards/r/RumblingSentry.java @@ -23,7 +23,7 @@ public final class RumblingSentry extends CardImpl { this.toughness = new MageInt(6); // When Rumbling Sentry enters the battlefield, scry 1. - this.addAbility(new EntersBattlefieldTriggeredAbility(new ScryEffect(1))); + this.addAbility(new EntersBattlefieldTriggeredAbility(new ScryEffect(1, false))); } private RumblingSentry(final RumblingSentry card) { diff --git a/Mage.Sets/src/mage/cards/r/RunawayTrashBot.java b/Mage.Sets/src/mage/cards/r/RunawayTrashBot.java new file mode 100644 index 00000000000..27c5338a43a --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RunawayTrashBot.java @@ -0,0 +1,57 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.common.FilterArtifactOrEnchantmentCard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RunawayTrashBot extends CardImpl { + + private static final FilterCard filter + = new FilterArtifactOrEnchantmentCard("artifact and/or enchantment card"); + private static final DynamicValue xValue = new CardsInControllerGraveyardCount(filter); + private static final Hint hint = new ValueHint("Artifacts and enchantments in your graveyard", xValue); + + public RunawayTrashBot(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}"); + + this.subtype.add(SubType.CONSTRUCT); + this.power = new MageInt(0); + this.toughness = new MageInt(4); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Runaway Trash-Bot gets +1/+0 for each artifact and/or enchantment card in your graveyard. + this.addAbility(new SimpleStaticAbility(new BoostSourceEffect( + xValue, StaticValue.get(0), Duration.WhileOnBattlefield + )).addHint(hint)); + } + + private RunawayTrashBot(final RunawayTrashBot card) { + super(card); + } + + @Override + public RunawayTrashBot copy() { + return new RunawayTrashBot(this); + } +} +// what a beautiful katamari diff --git a/Mage.Sets/src/mage/cards/r/RuneboundWolf.java b/Mage.Sets/src/mage/cards/r/RuneboundWolf.java new file mode 100644 index 00000000000..c66c59b5ec9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RuneboundWolf.java @@ -0,0 +1,68 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.target.common.TargetOpponent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RuneboundWolf extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent(); + + static { + filter.add(Predicates.or( + SubType.WOLF.getPredicate(), + SubType.WEREWOLF.getPredicate() + )); + } + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter); + private static final Hint hint = new ValueHint("Wolves and Werewolves you control", xValue); + + public RuneboundWolf(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); + + this.subtype.add(SubType.WOLF); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // {3}{R}, {T}: Runebound Wolf deals damage equal to the number of Wolves and Werewolves you control to target opponent. + Ability ability = new SimpleActivatedAbility( + new DamageTargetEffect(xValue) + .setText("{this} deals damage equal to the number of " + + "Wolves and Werewolves you control to target opponent"), + new ManaCostsImpl<>("{3}{R}") + ); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetOpponent()); + this.addAbility(ability); + } + + private RuneboundWolf(final RuneboundWolf card) { + super(card); + } + + @Override + public RuneboundWolf copy() { + return new RuneboundWolf(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RunoStromkirk.java b/Mage.Sets/src/mage/cards/r/RunoStromkirk.java new file mode 100644 index 00000000000..a5c7e229a3b --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RunoStromkirk.java @@ -0,0 +1,105 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.PutOnLibraryTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.TransformAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RunoStromkirk extends CardImpl { + + public RunoStromkirk(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.VAMPIRE); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(1); + this.toughness = new MageInt(4); + this.secondSideCardClazz = mage.cards.k.KrothussLordOfTheDeep.class; + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When Runo Stromkirk enters the battlefield, put up to one target creature card from your graveyard on top of your library. + Ability ability = new EntersBattlefieldTriggeredAbility(new PutOnLibraryTargetEffect(true)); + ability.addTarget(new TargetCardInYourGraveyard(0, 1, StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); + this.addAbility(ability); + + // At the beginning of your upkeep, look at the top card of your library. You may reveal that card. If a creature card with mana value 6 or greater is revealed this way, transform Runo Stromkirk. + this.addAbility(new TransformAbility()); + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new RunoStromkirkEffect(), TargetController.YOU, false + )); + } + + private RunoStromkirk(final RunoStromkirk card) { + super(card); + } + + @Override + public RunoStromkirk copy() { + return new RunoStromkirk(this); + } +} + +class RunoStromkirkEffect extends OneShotEffect { + + RunoStromkirkEffect() { + super(Outcome.Benefit); + staticText = "look at the top card of your library. You may reveal that card. " + + "If a creature card with mana value 6 or greater is revealed this way, transform {this}"; + } + + private RunoStromkirkEffect(final RunoStromkirkEffect effect) { + super(effect); + } + + @Override + public RunoStromkirkEffect copy() { + return new RunoStromkirkEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Card card = player.getLibrary().getFromTop(game); + if (card == null) { + return false; + } + player.lookAtCards(null, card, game); + if (!player.chooseUse(outcome, "Reveal " + card.getName() + '?', source, game)) { + return true; + } + player.revealCards(source, new CardsImpl(card), game); + if (!card.isCreature(game) || card.getManaValue() < 6) { + return true; + } + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (permanent != null) { + permanent.transform(source, game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/r/RuralRecruit.java b/Mage.Sets/src/mage/cards/r/RuralRecruit.java new file mode 100644 index 00000000000..005e24e5252 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RuralRecruit.java @@ -0,0 +1,43 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.TrainingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.Boar3Token; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RuralRecruit extends CardImpl { + + public RuralRecruit(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.PEASANT); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Training + this.addAbility(new TrainingAbility()); + + // When Rural Recruit enters the battlefield, create a 3/1 Boar creature token. + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new Boar3Token()))); + } + + private RuralRecruit(final RuralRecruit card) { + super(card); + } + + @Override + public RuralRecruit copy() { + return new RuralRecruit(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/Rust.java b/Mage.Sets/src/mage/cards/r/Rust.java index 2ff86879a7f..f85855cf8af 100644 --- a/Mage.Sets/src/mage/cards/r/Rust.java +++ b/Mage.Sets/src/mage/cards/r/Rust.java @@ -19,7 +19,7 @@ public final class Rust extends CardImpl { private static final FilterStackObject filter = new FilterStackObject("activated ability from an artifact source"); static { - filter.add(new ArtifactSourcePredicate()); + filter.add(ArtifactSourcePredicate.instance); } public Rust(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/r/RustScarab.java b/Mage.Sets/src/mage/cards/r/RustScarab.java index a3a0667b196..ceb9042d717 100644 --- a/Mage.Sets/src/mage/cards/r/RustScarab.java +++ b/Mage.Sets/src/mage/cards/r/RustScarab.java @@ -1,23 +1,17 @@ - package mage.cards.r; import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; +import mage.abilities.Ability; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.common.DestroyTargetEffect; 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.predicate.Predicates; -import mage.filter.predicate.permanent.ControllerIdPredicate; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.target.Target; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; +import mage.filter.predicate.permanent.DefendingPlayerControlsPredicate; import mage.target.TargetPermanent; /** @@ -26,6 +20,13 @@ import mage.target.TargetPermanent; */ public final class RustScarab extends CardImpl { + private static final FilterPermanent filter + = new FilterArtifactOrEnchantmentPermanent("artifact or enchantment defending player controls"); + + static { + filter.add(DefendingPlayerControlsPredicate.instance); + } + public RustScarab(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{G}"); this.subtype.add(SubType.INSECT); @@ -34,10 +35,9 @@ public final class RustScarab extends CardImpl { this.toughness = new MageInt(5); // Whenever Rust Scarab becomes blocked, you may destroy target artifact or enchantment defending player controls. - Effect effect = new DestroyTargetEffect(); - effect.setText("destroy target artifact or enchantment defending player controls"); - this.addAbility(new RustScarabBecomesBlockedTriggeredAbility(effect, true)); - + Ability ability = new BecomesBlockedSourceTriggeredAbility(new DestroyTargetEffect(), true); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); } private RustScarab(final RustScarab card) { @@ -49,47 +49,3 @@ public final class RustScarab extends CardImpl { return new RustScarab(this); } } - - -class RustScarabBecomesBlockedTriggeredAbility extends TriggeredAbilityImpl { - - public RustScarabBecomesBlockedTriggeredAbility(Effect effect, boolean optional) { - super(Zone.BATTLEFIELD, effect, optional); - } - - public RustScarabBecomesBlockedTriggeredAbility(final RustScarabBecomesBlockedTriggeredAbility ability) { - super(ability); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.CREATURE_BLOCKED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getTargetId().equals(this.getSourceId())) { - UUID defenderId = game.getState().getCombat().findGroup(this.getSourceId()).getDefenderId(); - if (defenderId != null) { - this.getTargets().clear(); - FilterPermanent filter = new FilterPermanent("artifact or enchantment defending player controls"); - filter.add(Predicates.or(CardType.ARTIFACT.getPredicate(), CardType.ENCHANTMENT.getPredicate())); - filter.add(new ControllerIdPredicate(defenderId)); - Target target = new TargetPermanent(filter); - this.addTarget(target); - return true; - } - } - return false; - } - - @Override - public String getTriggerPhrase() { - return "Whenever {this} becomes blocked, " ; - } - - @Override - public RustScarabBecomesBlockedTriggeredAbility copy() { - return new RustScarabBecomesBlockedTriggeredAbility(this); - } -} diff --git a/Mage.Sets/src/mage/cards/r/RuthlessTechnomancer.java b/Mage.Sets/src/mage/cards/r/RuthlessTechnomancer.java new file mode 100644 index 00000000000..e825319f923 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RuthlessTechnomancer.java @@ -0,0 +1,124 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeXTargetCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.GetXValue; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledArtifactPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.TreasureToken; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.common.TargetCardInYourGraveyard; +import mage.target.targetadjustment.TargetAdjuster; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RuthlessTechnomancer extends CardImpl { + + private static final FilterControlledPermanent filter = new FilterControlledArtifactPermanent("artifacts"); + + public RuthlessTechnomancer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(2); + this.toughness = new MageInt(4); + + // When Ruthless Technomancer enters the battlefield, you may sacrifice another creature you control. If you do, create a number of Treasure tokens equal to that creature's power. + this.addAbility(new EntersBattlefieldTriggeredAbility(new RuthlessTechnomancerEffect())); + + // {2}{B}, Sacrifice X artifacts: Return target creature card with power X or less from your graveyard to the battlefield. X can't be 0. + Ability ability = new SimpleActivatedAbility( + new ReturnFromGraveyardToBattlefieldTargetEffect() + .setText("return target creature card with power X or less " + + "from your graveyard to the battlefield. X can't be 0"), + new ManaCostsImpl<>("{2}{B}") + ); + ability.addCost(new SacrificeXTargetCost(filter, false, 1)); + this.addAbility(ability.setTargetAdjuster(RuthlessTechnomancerAdjuster.instance)); + } + + private RuthlessTechnomancer(final RuthlessTechnomancer card) { + super(card); + } + + @Override + public RuthlessTechnomancer copy() { + return new RuthlessTechnomancer(this); + } +} + +enum RuthlessTechnomancerAdjuster implements TargetAdjuster { + instance; + + @Override + public void adjustTargets(Ability ability, Game game) { + int xValue = GetXValue.instance.calculate(game, ability, null); + FilterCard filter = new FilterCreatureCard( + "creature card in your graveyard with mana value " + xValue + " or less" + ); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, xValue)); + ability.getTargets().clear(); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + } +} + +class RuthlessTechnomancerEffect extends OneShotEffect { + + RuthlessTechnomancerEffect() { + super(Outcome.Benefit); + staticText = "you may sacrifice another creature you control. If you do, " + + "create a number of Treasure tokens equal to that creature's power"; + } + + private RuthlessTechnomancerEffect(final RuthlessTechnomancerEffect effect) { + super(effect); + } + + @Override + public RuthlessTechnomancerEffect copy() { + return new RuthlessTechnomancerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null || !game.getBattlefield().contains( + StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE, source, game, 1 + )) { + return false; + } + TargetPermanent target = new TargetPermanent( + 0, 1, StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE, true + ); + player.choose(outcome, target, source.getSourceId(), game); + Permanent permanent = game.getPermanent(target.getFirstTarget()); + if (permanent == null || !permanent.sacrifice(source, game)) { + return false; + } + int power = permanent.getPower().getValue(); + return power < 1 || new TreasureToken().putOntoBattlefield(power, game, source); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RyuWorldWarrior.java b/Mage.Sets/src/mage/cards/r/RyuWorldWarrior.java new file mode 100644 index 00000000000..6c282b8e823 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RyuWorldWarrior.java @@ -0,0 +1,105 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.costs.common.UntapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.TrainingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetAnyTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RyuWorldWarrior extends CardImpl { + + public RyuWorldWarrior(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(2); + this.toughness = new MageInt(4); + + // Training + this.addAbility(new TrainingAbility()); + + // Hadoken—{4}{R}, {Q}, Discard a card: Ryu, World Warrior deals damage equal to his power to any target. If excess damage was dealt to a creature this way, draw a card. + Ability ability = new SimpleActivatedAbility(new RyuWorldWarriorEffect(), new ManaCostsImpl<>("{4}{R}")); + ability.addCost(new UntapSourceCost()); + ability.addCost(new DiscardCardCost()); + ability.addTarget(new TargetAnyTarget()); + this.addAbility(ability.withFlavorWord("Hadoken")); + } + + private RyuWorldWarrior(final RyuWorldWarrior card) { + super(card); + } + + @Override + public RyuWorldWarrior copy() { + return new RyuWorldWarrior(this); + } +} + +class RyuWorldWarriorEffect extends OneShotEffect { + + RyuWorldWarriorEffect() { + super(Outcome.Benefit); + staticText = "{this} deals damage equal to his power to any target. " + + "If excess damage was dealt to a creature this way, draw a card"; + } + + private RyuWorldWarriorEffect(final RyuWorldWarriorEffect effect) { + super(effect); + } + + @Override + public RyuWorldWarriorEffect copy() { + return new RyuWorldWarriorEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent sourcePermanent = source.getSourcePermanentOrLKI(game); + if (sourcePermanent == null) { + return false; + } + int amount = sourcePermanent.getPower().getValue(); + if (amount < 1) { + return false; + } + Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (permanent == null) { + Player player = game.getPlayer(getTargetPointer().getFirst(game, source)); + return player != null && player.damage(amount, source, game) > 0; + } + if (!permanent.isCreature(game)) { + return permanent.damage(amount, source, game) > 0; + } + int lethal = permanent.getLethalDamage(source.getSourceId(), game); + permanent.damage(amount, source.getSourceId(), source, game); + if (lethal >= amount) { + return true; + } + Player player = game.getPlayer(source.getControllerId()); + if (player != null) { + player.drawCards(1, source, game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SaberAnts.java b/Mage.Sets/src/mage/cards/s/SaberAnts.java index 091836e8ecf..aa3cfcca21c 100644 --- a/Mage.Sets/src/mage/cards/s/SaberAnts.java +++ b/Mage.Sets/src/mage/cards/s/SaberAnts.java @@ -30,7 +30,7 @@ public final class SaberAnts extends CardImpl { this.toughness = new MageInt(3); // Whenever Saber Ants is dealt damage, you may create that many 1/1 green Insect creature tokens. - this.addAbility(new DealtDamageToSourceTriggeredAbility(new SaberAntsEffect(), true, false, true)); + this.addAbility(new DealtDamageToSourceTriggeredAbility(new SaberAntsEffect(), true, false)); } private SaberAnts(final SaberAnts card) { diff --git a/Mage.Sets/src/mage/cards/s/SageOfAncientLore.java b/Mage.Sets/src/mage/cards/s/SageOfAncientLore.java index 1dd23df3725..0d28e9a5cf6 100644 --- a/Mage.Sets/src/mage/cards/s/SageOfAncientLore.java +++ b/Mage.Sets/src/mage/cards/s/SageOfAncientLore.java @@ -33,7 +33,6 @@ public final class SageOfAncientLore extends CardImpl { this.power = new MageInt(0); this.toughness = new MageInt(0); - this.transformable = true; this.secondSideCardClazz = mage.cards.w.WerewolfOfAncientHunger.class; // Sage of Ancient Lore's power and toughness are each equal to the number of cards in your hand. diff --git a/Mage.Sets/src/mage/cards/s/SagesRowDenizen.java b/Mage.Sets/src/mage/cards/s/SagesRowDenizen.java index 9f48bc61023..61a9c84cc97 100644 --- a/Mage.Sets/src/mage/cards/s/SagesRowDenizen.java +++ b/Mage.Sets/src/mage/cards/s/SagesRowDenizen.java @@ -1,4 +1,3 @@ - package mage.cards.s; import java.util.UUID; @@ -11,9 +10,8 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPlayer; @@ -24,12 +22,11 @@ import mage.target.TargetPlayer; */ public final class SagesRowDenizen extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("another blue creature"); + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("another blue creature"); static { - filter.add(new ColorPredicate(ObjectColor.BLUE)); filter.add(AnotherPredicate.instance); - filter.add(TargetController.YOU.getControllerPredicate()); + filter.add(new ColorPredicate(ObjectColor.BLUE)); } public SagesRowDenizen(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/s/SaheeliRai.java b/Mage.Sets/src/mage/cards/s/SaheeliRai.java index 5e3b82340f7..b707602ef8d 100644 --- a/Mage.Sets/src/mage/cards/s/SaheeliRai.java +++ b/Mage.Sets/src/mage/cards/s/SaheeliRai.java @@ -3,7 +3,6 @@ package mage.cards.s; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; @@ -37,7 +36,7 @@ public final class SaheeliRai extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.SAHEELI); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +1: Scry 1. Saheeli Rai deals 1 damage to each opponent. Effect effect = new ScryEffect(1); @@ -87,7 +86,7 @@ class SaheeliRaiCreateTokenEffect extends OneShotEffect { if (copiedPermanent != null) { CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(null, CardType.ARTIFACT, true); if (effect.apply(game, source)) { - for (Permanent copyPermanent : effect.getAddedPermanent()) { + for (Permanent copyPermanent : effect.getAddedPermanents()) { ExileTargetEffect exileEffect = new ExileTargetEffect(); exileEffect.setTargetPointer(new FixedTarget(copyPermanent, game)); DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); diff --git a/Mage.Sets/src/mage/cards/s/SaheeliSublimeArtificer.java b/Mage.Sets/src/mage/cards/s/SaheeliSublimeArtificer.java index 64e66980243..53dcfabab84 100644 --- a/Mage.Sets/src/mage/cards/s/SaheeliSublimeArtificer.java +++ b/Mage.Sets/src/mage/cards/s/SaheeliSublimeArtificer.java @@ -3,7 +3,6 @@ package mage.cards.s; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; @@ -50,7 +49,7 @@ public final class SaheeliSublimeArtificer extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.SAHEELI); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // Whenever you cast a noncreature spell, create a 1/1 colorless Servo artifact creature token. this.addAbility(new SpellCastControllerTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/s/SaheeliTheGifted.java b/Mage.Sets/src/mage/cards/s/SaheeliTheGifted.java index 99cf88d9516..3ce79c4066a 100644 --- a/Mage.Sets/src/mage/cards/s/SaheeliTheGifted.java +++ b/Mage.Sets/src/mage/cards/s/SaheeliTheGifted.java @@ -5,7 +5,6 @@ import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.SpellAbility; import mage.abilities.common.CanBeYourCommanderAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenCopyTargetEffect; import mage.abilities.effects.common.CreateTokenEffect; @@ -37,7 +36,7 @@ public final class SaheeliTheGifted extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.SAHEELI); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Create a 1/1 colorless Servo artifact creature token. this.addAbility(new LoyaltyAbility( diff --git a/Mage.Sets/src/mage/cards/s/SaheelisArtistry.java b/Mage.Sets/src/mage/cards/s/SaheelisArtistry.java index 1edb601e2e3..96137b07c5d 100644 --- a/Mage.Sets/src/mage/cards/s/SaheelisArtistry.java +++ b/Mage.Sets/src/mage/cards/s/SaheelisArtistry.java @@ -30,7 +30,7 @@ public final class SaheelisArtistry extends CardImpl { Mode mode1 = new Mode(); effect = new CreateTokenCopyTargetEffect(); effect.setBecomesArtifact(true); - effect.setText("Create a token that's a copy of target creature, except that it's an artifact in addition to its other types"); + effect.setText("Create a token that's a copy of target creature, except it's an artifact in addition to its other types"); mode1.addEffect(effect); mode1.addTarget(new TargetCreaturePermanent().withChooseHint("create copy of that, artifact type")); this.getSpellAbility().addMode(mode1); diff --git a/Mage.Sets/src/mage/cards/s/SaibaTrespassers.java b/Mage.Sets/src/mage/cards/s/SaibaTrespassers.java new file mode 100644 index 00000000000..1e0c08f8e1c --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SaibaTrespassers.java @@ -0,0 +1,48 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.effects.common.DontUntapInControllersNextUntapStepTargetEffect; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.keyword.ChannelAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SaibaTrespassers extends CardImpl { + + public SaibaTrespassers(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{4}{U}"); + + this.subtype.add(SubType.MOONFOLK); + this.subtype.add(SubType.ROGUE); + this.power = new MageInt(3); + this.toughness = new MageInt(5); + + // Channel — {3}{U}, Discard Saiba Trespassers: Tap up to two target creatures you don't control. Those creatures don't untap during their controller's next untap step. + Ability ability = new ChannelAbility( + "{3}{U}", new TapTargetEffect() + .setText("tap up to two target creatures you don't control") + ); + ability.addEffect(new DontUntapInControllersNextUntapStepTargetEffect("Those creatures")); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); + this.addAbility(ability); + } + + private SaibaTrespassers(final SaibaTrespassers card) { + super(card); + } + + @Override + public SaibaTrespassers copy() { + return new SaibaTrespassers(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SakashimasStudent.java b/Mage.Sets/src/mage/cards/s/SakashimasStudent.java index 387cbe4ff68..2b5d66f821a 100644 --- a/Mage.Sets/src/mage/cards/s/SakashimasStudent.java +++ b/Mage.Sets/src/mage/cards/s/SakashimasStudent.java @@ -30,7 +30,7 @@ public final class SakashimasStudent extends CardImpl { this.toughness = new MageInt(0); // Ninjutsu {1}{U} - this.addAbility(new NinjutsuAbility(new ManaCostsImpl("{1}{U}"))); + this.addAbility(new NinjutsuAbility("{1}{U}")); // You may have Sakashima's Student enter the battlefield as a copy of any creature on the battlefield, except it's still a Ninja in addition to its other creature types. Effect effect = new CopyPermanentEffect(StaticFilters.FILTER_PERMANENT_CREATURE, new AddSubtypeCopyApplier(SubType.NINJA)); diff --git a/Mage.Sets/src/mage/cards/s/SalvageScuttler.java b/Mage.Sets/src/mage/cards/s/SalvageScuttler.java index 4e2c6b0b1f5..02d336c2703 100644 --- a/Mage.Sets/src/mage/cards/s/SalvageScuttler.java +++ b/Mage.Sets/src/mage/cards/s/SalvageScuttler.java @@ -1,20 +1,17 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; -import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.abilities.effects.common.ReturnToHandChosenControlledPermanentEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.filter.common.FilterControlledArtifactPermanent; -import mage.target.common.TargetControlledPermanent; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * * @author fireshoes */ public final class SalvageScuttler extends CardImpl { @@ -27,9 +24,9 @@ public final class SalvageScuttler extends CardImpl { this.toughness = new MageInt(4); // Whenever Salvage Scuttler attacks, return an artifact you control to its owner's hand. - Ability ability = new AttacksTriggeredAbility(new ReturnToHandTargetEffect(), false); - ability.addTarget(new TargetControlledPermanent(new FilterControlledArtifactPermanent("an artifact you control"))); - this.addAbility(ability); + this.addAbility(new AttacksTriggeredAbility( + new ReturnToHandChosenControlledPermanentEffect(StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT) + )); } private SalvageScuttler(final SalvageScuttler card) { diff --git a/Mage.Sets/src/mage/cards/s/SamutTheTested.java b/Mage.Sets/src/mage/cards/s/SamutTheTested.java index 4bcb3147951..bf1211792e9 100644 --- a/Mage.Sets/src/mage/cards/s/SamutTheTested.java +++ b/Mage.Sets/src/mage/cards/s/SamutTheTested.java @@ -3,7 +3,6 @@ package mage.cards.s; import java.util.UUID; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.DamageMultiEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; @@ -26,12 +25,18 @@ import mage.target.common.TargetCreaturePermanent; */ public final class SamutTheTested extends CardImpl { + private static final FilterCard filter = new FilterCard("creature and/or planeswalker cards"); + + static { + filter.add(Predicates.or(CardType.CREATURE.getPredicate(), CardType.PLANESWALKER.getPredicate())); + } + public SamutTheTested(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{2}{R}{G}"); this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.SAMUT); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Up to one target creature gains double strike until end of turn. Effect effect = new GainAbilityTargetEffect(DoubleStrikeAbility.getInstance(), Duration.EndOfTurn); @@ -39,19 +44,14 @@ public final class SamutTheTested extends CardImpl { ability.addTarget(new TargetCreaturePermanent(0, 1)); this.addAbility(ability); - // -2: Samut, the Tested deals 2 damage divided as you choose among one or two target creatures and/or players. + // -2: Samut, the Tested deals 2 damage divided as you choose among one or two targets. effect = new DamageMultiEffect(2); ability = new LoyaltyAbility(effect, -2); ability.addTarget(new TargetAnyTargetAmount(2)); this.addAbility(ability); - // -7: Search your library or up to two creature and/or planeswalkercards, put them onto the battlefield, then shuffle your library. - FilterCard filterCard = new FilterCard("creature or planeswalker card"); - filterCard.add(Predicates.or( - CardType.CREATURE.getPredicate(), - CardType.PLANESWALKER.getPredicate() - )); - effect = new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(0, 2, filterCard), false, true); + // -7: Search your library for up to two creature and/or planeswalker cards, put them onto the battlefield, then shuffle your library. + effect = new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(0, 2, filter), false, true); ability = new LoyaltyAbility(effect, -7); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SamutTyrantSmasher.java b/Mage.Sets/src/mage/cards/s/SamutTyrantSmasher.java index 8468e13ebb7..901df609861 100644 --- a/Mage.Sets/src/mage/cards/s/SamutTyrantSmasher.java +++ b/Mage.Sets/src/mage/cards/s/SamutTyrantSmasher.java @@ -3,7 +3,6 @@ package mage.cards.s; 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.common.continuous.BoostTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; @@ -29,7 +28,7 @@ public final class SamutTyrantSmasher extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.SAMUT); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // Creatures you control have haste. this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( @@ -44,7 +43,7 @@ public final class SamutTyrantSmasher extends CardImpl { ability.addEffect(new GainAbilityTargetEffect( HasteAbility.getInstance(), Duration.EndOfTurn ).setText("and gains haste until end of turn")); - ability.addEffect(new ScryEffect(1)); + ability.addEffect(new ScryEffect(1, false)); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SamutsSprint.java b/Mage.Sets/src/mage/cards/s/SamutsSprint.java index 1d631dd6027..2806af7af2a 100644 --- a/Mage.Sets/src/mage/cards/s/SamutsSprint.java +++ b/Mage.Sets/src/mage/cards/s/SamutsSprint.java @@ -26,7 +26,7 @@ public final class SamutsSprint extends CardImpl { this.getSpellAbility().addEffect(new GainAbilityTargetEffect( HasteAbility.getInstance(), Duration.EndOfTurn ).setText("and gains haste until end of turn")); - this.getSpellAbility().addEffect(new ScryEffect(1)); + this.getSpellAbility().addEffect(new ScryEffect(1, false)); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } diff --git a/Mage.Sets/src/mage/cards/s/Sanctify.java b/Mage.Sets/src/mage/cards/s/Sanctify.java new file mode 100644 index 00000000000..f8405587d66 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/Sanctify.java @@ -0,0 +1,35 @@ +package mage.cards.s; + +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Sanctify extends CardImpl { + + public Sanctify(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{W}"); + + // Destroy target artifact or enchantment. You gain 3 life. + this.getSpellAbility().addEffect(new DestroyTargetEffect()); + this.getSpellAbility().addEffect(new GainLifeEffect(3)); + this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_ENCHANTMENT)); + } + + private Sanctify(final Sanctify card) { + super(card); + } + + @Override + public Sanctify copy() { + return new Sanctify(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SanctumPrelate.java b/Mage.Sets/src/mage/cards/s/SanctumPrelate.java index b7e04f22e9f..dc4e8091093 100644 --- a/Mage.Sets/src/mage/cards/s/SanctumPrelate.java +++ b/Mage.Sets/src/mage/cards/s/SanctumPrelate.java @@ -1,8 +1,5 @@ - package mage.cards.s; -import java.util.UUID; - import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; @@ -19,6 +16,8 @@ import mage.game.permanent.Permanent; import mage.game.stack.Spell; import mage.players.Player; +import java.util.UUID; + /** * @author maxlebedev */ @@ -52,7 +51,7 @@ class ChooseNumberEffect extends OneShotEffect { public ChooseNumberEffect() { super(Outcome.Detriment); - staticText = setText(); + staticText = "choose a number"; } public ChooseNumberEffect(final ChooseNumberEffect effect) { @@ -67,7 +66,7 @@ class ChooseNumberEffect extends OneShotEffect { game.getState().setValue(source.getSourceId().toString(), numberChoice); Permanent permanent = game.getPermanentEntering(source.getSourceId()); - if(permanent != null) { + if (permanent != null) { permanent.addInfo("chosen players", "Chosen Number: " + numberChoice + "", game); game.informPlayers(permanent.getLogName() + ", chosen number: " + numberChoice); @@ -80,10 +79,6 @@ class ChooseNumberEffect extends OneShotEffect { public ChooseNumberEffect copy() { return new ChooseNumberEffect(this); } - - private String setText() { - return "Choose a number. Noncreature spells with mana value equal to the chosen number can't be cast"; - } } class SanctumPrelateReplacementEffect extends ContinuousRuleModifyingEffectImpl { diff --git a/Mage.Sets/src/mage/cards/s/SandSilos.java b/Mage.Sets/src/mage/cards/s/SandSilos.java index 0a3476c43c3..49090e97bbd 100644 --- a/Mage.Sets/src/mage/cards/s/SandSilos.java +++ b/Mage.Sets/src/mage/cards/s/SandSilos.java @@ -39,7 +39,7 @@ public final class SandSilos extends CardImpl { this.addAbility(new SkipUntapOptionalAbility()); // At the beginning of your upkeep, if Sand Silos is tapped, put a storage counter on it. OneShotEffect addStorageCounter = new AddCountersSourceEffect(CounterType.STORAGE.createInstance()); - Effect effect = new ConditionalOneShotEffect(addStorageCounter, SourceTappedCondition.instance, "if {this} is tapped, put a storage counter on it"); + Effect effect = new ConditionalOneShotEffect(addStorageCounter, SourceTappedCondition.TAPPED, "if {this} is tapped, put a storage counter on it"); this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, effect, TargetController.YOU, false)); // {tap}, Remove any number of storage counters from Sand Silos: Add {U} for each storage counter removed this way. Ability ability = new DynamicManaAbility( diff --git a/Mage.Sets/src/mage/cards/s/SandstoneDeadfall.java b/Mage.Sets/src/mage/cards/s/SandstoneDeadfall.java index edbc4a19154..c5988eaeb6b 100644 --- a/Mage.Sets/src/mage/cards/s/SandstoneDeadfall.java +++ b/Mage.Sets/src/mage/cards/s/SandstoneDeadfall.java @@ -11,11 +11,9 @@ import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Zone; -import mage.filter.common.FilterAttackingCreature; import mage.filter.common.FilterControlledLandPermanent; import mage.target.common.TargetControlledPermanent; -import mage.target.common.TargetCreaturePermanent; +import mage.target.common.TargetAttackingCreature; /** * @@ -27,10 +25,10 @@ public final class SandstoneDeadfall extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}"); // {tap}, Sacrifice two lands and Sandstone Deadfall: Destroy target attacking creature. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new TapSourceCost()); - ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(2, 2, new FilterControlledLandPermanent("two lands"), true))); + Ability ability = new SimpleActivatedAbility(new DestroyTargetEffect(), new TapSourceCost()); + ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(2, 2, new FilterControlledLandPermanent("lands"), true))); ability.addCost(new SacrificeSourceCost()); - ability.addTarget(new TargetCreaturePermanent(new FilterAttackingCreature())); + ability.addTarget(new TargetAttackingCreature()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SanguineStatuette.java b/Mage.Sets/src/mage/cards/s/SanguineStatuette.java new file mode 100644 index 00000000000..5452de4839e --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SanguineStatuette.java @@ -0,0 +1,56 @@ +package mage.cards.s; + +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SacrificePermanentTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.continuous.BecomesCreatureSourceEffect; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.permanent.token.BloodToken; +import mage.game.permanent.token.custom.CreatureToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SanguineStatuette extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.BLOOD, "a Blood token"); + + static { + filter.add(TokenPredicate.TRUE); + } + + public SanguineStatuette(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}{R}"); + + // When Sanguine Statuette enters the battlefield, create a Blood token. + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new BloodToken()))); + + // Whenever you sacrifice a Blood token, you may have Sanguine Statuette become a 3/3 Vampire artifact creature with haste until end of turn. + this.addAbility(new SacrificePermanentTriggeredAbility(new BecomesCreatureSourceEffect( + new CreatureToken(3, 3, "3/3 Vampire artifact creature with haste") + .withType(CardType.ARTIFACT) + .withSubType(SubType.VAMPIRE) + .withAbility(HasteAbility.getInstance()), + "", Duration.EndOfTurn + ).setText("have {this} become a 3/3 Vampire artifact creature with haste until end of turn"), filter, false, true)); + } + + private SanguineStatuette(final SanguineStatuette card) { + super(card); + } + + @Override + public SanguineStatuette copy() { + return new SanguineStatuette(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SapphireCharm.java b/Mage.Sets/src/mage/cards/s/SapphireCharm.java index dad11956232..650bc353d1b 100644 --- a/Mage.Sets/src/mage/cards/s/SapphireCharm.java +++ b/Mage.Sets/src/mage/cards/s/SapphireCharm.java @@ -14,8 +14,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.TargetPlayer; import mage.target.common.TargetCreaturePermanent; @@ -24,12 +23,6 @@ import mage.target.common.TargetCreaturePermanent; * @author fireshoes */ public final class SapphireCharm extends CardImpl { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } public SapphireCharm(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{U}"); @@ -50,7 +43,7 @@ public final class SapphireCharm extends CardImpl { // or target creature an opponent controls phases out. mode = new Mode(); mode.addEffect(new PhaseOutTargetEffect()); - mode.addTarget(new TargetCreaturePermanent(filter)); + mode.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); this.getSpellAbility().addMode(mode); } diff --git a/Mage.Sets/src/mage/cards/s/SapphireDrake.java b/Mage.Sets/src/mage/cards/s/SapphireDrake.java index 2b2e0a2f59f..266aef59b1b 100644 --- a/Mage.Sets/src/mage/cards/s/SapphireDrake.java +++ b/Mage.Sets/src/mage/cards/s/SapphireDrake.java @@ -9,24 +9,13 @@ import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.counters.CounterType; -import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; /** * * @author jeffwadsworth */ public final class SapphireDrake 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 flying"; public SapphireDrake(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{5}{U}"); @@ -39,7 +28,13 @@ public final class SapphireDrake extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // 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, rule))); + this.addAbility(new SimpleStaticAbility( + Zone.BATTLEFIELD, + new GainAbilityAllEffect(FlyingAbility.getInstance(), + Duration.WhileOnBattlefield, + StaticFilters.FILTER_EACH_CONTROLLED_CREATURE_P1P1) + ) + ); } private SapphireDrake(final SapphireDrake card) { diff --git a/Mage.Sets/src/mage/cards/s/SarkhanDragonsoul.java b/Mage.Sets/src/mage/cards/s/SarkhanDragonsoul.java index ff556006acf..28e355e1dcc 100644 --- a/Mage.Sets/src/mage/cards/s/SarkhanDragonsoul.java +++ b/Mage.Sets/src/mage/cards/s/SarkhanDragonsoul.java @@ -3,7 +3,6 @@ package mage.cards.s; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.common.DamageAllEffect; import mage.abilities.effects.common.DamagePlayersEffect; import mage.abilities.effects.common.DamageTargetEffect; @@ -36,7 +35,7 @@ public final class SarkhanDragonsoul extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.SARKHAN); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +2: Sarkhan, Dragonsoul deals 1 damage to each opponent and each creature your opponents control. Ability ability = new LoyaltyAbility(new DamagePlayersEffect(1, TargetController.OPPONENT), 2); diff --git a/Mage.Sets/src/mage/cards/s/SarkhanFireblood.java b/Mage.Sets/src/mage/cards/s/SarkhanFireblood.java index 52bb787e3a0..b55b3ff9951 100644 --- a/Mage.Sets/src/mage/cards/s/SarkhanFireblood.java +++ b/Mage.Sets/src/mage/cards/s/SarkhanFireblood.java @@ -2,7 +2,6 @@ package mage.cards.s; import java.util.UUID; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.costs.common.DiscardCardCost; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.common.CreateTokenEffect; @@ -35,7 +34,7 @@ public final class SarkhanFireblood extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.SARKHAN); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +1: You may discard a card. If you do, draw a card. this.addAbility(new LoyaltyAbility(new DoIfCostPaid( diff --git a/Mage.Sets/src/mage/cards/s/SarkhanTheDragonspeaker.java b/Mage.Sets/src/mage/cards/s/SarkhanTheDragonspeaker.java index 628e26ff4e8..3bc34e6da73 100644 --- a/Mage.Sets/src/mage/cards/s/SarkhanTheDragonspeaker.java +++ b/Mage.Sets/src/mage/cards/s/SarkhanTheDragonspeaker.java @@ -6,7 +6,6 @@ import mage.MageObjectReference; import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.Effect; import mage.abilities.effects.common.DamageTargetEffect; @@ -34,7 +33,7 @@ public final class SarkhanTheDragonspeaker extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.SARKHAN); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Until end of turn, Sarkhan, the Dragonspeaker becomes a legendary 4/4 red Dragon creature with flying, indestructible, and haste. this.addAbility(new LoyaltyAbility(new SarkhanTheDragonspeakerEffect(), 1)); diff --git a/Mage.Sets/src/mage/cards/s/SarkhanTheMad.java b/Mage.Sets/src/mage/cards/s/SarkhanTheMad.java index 7d51f4caf2f..a3f3627df24 100644 --- a/Mage.Sets/src/mage/cards/s/SarkhanTheMad.java +++ b/Mage.Sets/src/mage/cards/s/SarkhanTheMad.java @@ -6,7 +6,6 @@ import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; import mage.cards.CardImpl; @@ -37,7 +36,7 @@ public final class SarkhanTheMad extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{3}{B}{R}"); this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.SARKHAN); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(7)); + this.setStartingLoyalty(7); this.addAbility(new LoyaltyAbility(new SarkhanTheMadRevealAndDrawEffect(), 0)); diff --git a/Mage.Sets/src/mage/cards/s/SarkhanTheMasterless.java b/Mage.Sets/src/mage/cards/s/SarkhanTheMasterless.java index 2751d320e68..0a3a0bb2228 100644 --- a/Mage.Sets/src/mage/cards/s/SarkhanTheMasterless.java +++ b/Mage.Sets/src/mage/cards/s/SarkhanTheMasterless.java @@ -5,7 +5,6 @@ import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.common.AttacksAllTriggeredAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; @@ -30,7 +29,7 @@ public final class SarkhanTheMasterless extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.SARKHAN); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // Whenever a creature attacks you or a planeswalker you control, each Dragon you control deals 1 damage to that creature. this.addAbility(new AttacksAllTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/s/SarkhanUnbroken.java b/Mage.Sets/src/mage/cards/s/SarkhanUnbroken.java index 4b3eeb7774a..f756e26b03d 100644 --- a/Mage.Sets/src/mage/cards/s/SarkhanUnbroken.java +++ b/Mage.Sets/src/mage/cards/s/SarkhanUnbroken.java @@ -7,7 +7,6 @@ import java.util.UUID; import mage.Mana; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; @@ -42,7 +41,7 @@ public final class SarkhanUnbroken extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.SARKHAN); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Draw a card, then add one mana of any color. this.addAbility(new LoyaltyAbility(new SarkhanUnbrokenAbility1(), 1)); diff --git a/Mage.Sets/src/mage/cards/s/SarkhanVol.java b/Mage.Sets/src/mage/cards/s/SarkhanVol.java index f9bfb4fc45e..98dbf173030 100644 --- a/Mage.Sets/src/mage/cards/s/SarkhanVol.java +++ b/Mage.Sets/src/mage/cards/s/SarkhanVol.java @@ -1,7 +1,6 @@ package mage.cards.s; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.Effects; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.UntapTargetEffect; @@ -34,7 +33,7 @@ public final class SarkhanVol extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.SARKHAN); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Creatures you control get +1/+1 and gain haste until end of turn. Effects effects1 = new Effects(); diff --git a/Mage.Sets/src/mage/cards/s/SarkhansDragonfire.java b/Mage.Sets/src/mage/cards/s/SarkhansDragonfire.java index b07b9f313ac..a818b96cdb3 100644 --- a/Mage.Sets/src/mage/cards/s/SarkhansDragonfire.java +++ b/Mage.Sets/src/mage/cards/s/SarkhansDragonfire.java @@ -36,7 +36,7 @@ public final class SarkhansDragonfire 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("
Look at the top five cards of your library. " + "You may reveal a red card from among them and put it into your hand. " + "Put the rest on the bottom of your library in a random order.") ); diff --git a/Mage.Sets/src/mage/cards/s/SasayaOrochiAscendant.java b/Mage.Sets/src/mage/cards/s/SasayaOrochiAscendant.java index 1c78594bd08..21835cb661b 100644 --- a/Mage.Sets/src/mage/cards/s/SasayaOrochiAscendant.java +++ b/Mage.Sets/src/mage/cards/s/SasayaOrochiAscendant.java @@ -27,6 +27,7 @@ import mage.players.Player; import java.util.List; import java.util.UUID; +import mage.abilities.effects.Effect; import mage.choices.Choice; import mage.choices.ChoiceColor; @@ -48,7 +49,9 @@ public final class SasayaOrochiAscendant extends CardImpl { this.flipCardName = "Sasaya's Essence"; // Reveal your hand: If you have seven or more land cards in your hand, flip Sasaya, Orochi Ascendant. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new SasayaOrochiAscendantFlipEffect(), new RevealHandSourceControllerCost())); + Effect effect = new SasayaOrochiAscendantFlipEffect(); + effect.setOutcome(Outcome.AIDontUseIt); // repetition issues need to be fixed for the AI to use this effectively + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new RevealHandSourceControllerCost())); } private SasayaOrochiAscendant(final SasayaOrochiAscendant card) { diff --git a/Mage.Sets/src/mage/cards/s/SaskiaTheUnyielding.java b/Mage.Sets/src/mage/cards/s/SaskiaTheUnyielding.java index c9815590081..1e1f908901a 100644 --- a/Mage.Sets/src/mage/cards/s/SaskiaTheUnyielding.java +++ b/Mage.Sets/src/mage/cards/s/SaskiaTheUnyielding.java @@ -11,6 +11,7 @@ import mage.abilities.keyword.VigilanceAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; +import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; import mage.game.Game; import mage.players.Player; @@ -40,7 +41,7 @@ public final class SaskiaTheUnyielding extends CardImpl { // Whenever a creature you control deals combat damage to a player, it deals that much damage to the chosen player. this.addAbility(new DealsDamageToAPlayerAllTriggeredAbility( new SaskiaTheUnyieldingEffect(), - new FilterControlledCreaturePermanent("a creature you control"), false, SetTargetPointer.NONE, true + StaticFilters.FILTER_CONTROLLED_A_CREATURE, false, SetTargetPointer.NONE, true )); } diff --git a/Mage.Sets/src/mage/cards/s/SatoruUmezawa.java b/Mage.Sets/src/mage/cards/s/SatoruUmezawa.java new file mode 100644 index 00000000000..ebb07c03d22 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SatoruUmezawa.java @@ -0,0 +1,122 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.common.LookLibraryAndPickControllerEffect; +import mage.abilities.keyword.NinjutsuAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.stack.StackAbility; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SatoruUmezawa extends CardImpl { + + public SatoruUmezawa(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.NINJA); + this.power = new MageInt(2); + this.toughness = new MageInt(4); + + // Whenever you activate a ninjutsu ability, look at the top three cards of your library. Put one of them into your hand and the rest on the bottom of your library in any order. This ability triggers only once each turn. + this.addAbility(new SatoruUmezawaTriggeredAbility()); + + // Each creature card in your hand has ninjutsu {2}{U}{B}. + this.addAbility(new SimpleStaticAbility(new SatoruUmezawaEffect())); + } + + private SatoruUmezawa(final SatoruUmezawa card) { + super(card); + } + + @Override + public SatoruUmezawa copy() { + return new SatoruUmezawa(this); + } +} + +class SatoruUmezawaTriggeredAbility extends TriggeredAbilityImpl { + + SatoruUmezawaTriggeredAbility() { + super(Zone.BATTLEFIELD, new LookLibraryAndPickControllerEffect( + StaticValue.get(3), false, StaticValue.get(1), + StaticFilters.FILTER_CARD, Zone.LIBRARY, false, false + )); + this.setTriggersOnce(true); + } + + private SatoruUmezawaTriggeredAbility(final SatoruUmezawaTriggeredAbility ability) { + super(ability); + } + + @Override + public SatoruUmezawaTriggeredAbility copy() { + return new SatoruUmezawaTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ACTIVATED_ABILITY; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (!isControlledBy(event.getPlayerId())) { + return false; + } + StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(event.getTargetId()); + return stackAbility.getStackAbility() instanceof NinjutsuAbility; + } + + @Override + public String getRule() { + return "Whenever you activate a ninjutsu ability, look at the top three cards of your library. " + + "Put one of them into your hand and the rest on the bottom of your library in any order. " + + "This ability triggers only once each turn."; + } +} + +class SatoruUmezawaEffect extends ContinuousEffectImpl { + + public SatoruUmezawaEffect() { + super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); + this.staticText = "each creature card in your hand has ninjutsu {2}{U}{B}"; + } + + public SatoruUmezawaEffect(final SatoruUmezawaEffect effect) { + super(effect); + } + + @Override + public SatoruUmezawaEffect copy() { + return new SatoruUmezawaEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + for (Card card : player.getHand().getCards(StaticFilters.FILTER_CARD_CREATURE, game)) { + game.getState().addOtherAbility(card, new NinjutsuAbility("{2}{U}{B}")); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SatsukiTheLivingLore.java b/Mage.Sets/src/mage/cards/s/SatsukiTheLivingLore.java new file mode 100644 index 00000000000..30d3a673157 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SatsukiTheLivingLore.java @@ -0,0 +1,86 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.abilities.effects.common.counter.AddCountersAllEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.CounterType; +import mage.filter.FilterCard; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.target.TargetPermanent; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SatsukiTheLivingLore extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledPermanent(SubType.SAGA); + private static final FilterPermanent filter2 + = new FilterControlledPermanent("Saga or enchantment creature you control"); + private static final FilterCard filter3 + = new FilterCard("Saga card from your graveyard"); + + static { + filter2.add(Predicates.or( + SubType.SAGA.getPredicate(), + Predicates.and( + CardType.ENCHANTMENT.getPredicate(), + CardType.CREATURE.getPredicate() + ) + )); + filter3.add(SubType.SAGA.getPredicate()); + } + + public SatsukiTheLivingLore(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.DRUID); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // {T}: Put a lore counter on each Saga you control. Activate only as a sorcery. + this.addAbility(new ActivateAsSorceryActivatedAbility(new AddCountersAllEffect( + CounterType.LORE.createInstance(), filter + ), new TapSourceCost())); + + // When Satsuki, the Living Lore dies, choose up to one — + // • Return target Saga or enchantment creature you control to its owner's hand. + Ability ability = new DiesSourceTriggeredAbility(new ReturnToHandTargetEffect()); + ability.getModes().setMinModes(0); + ability.getModes().setMaxModes(1); + ability.addTarget(new TargetPermanent(filter2)); + + // • Return target Saga card from your graveyard to your hand. + Mode mode = new Mode(new ReturnFromGraveyardToHandTargetEffect()); + mode.addTarget(new TargetCardInYourGraveyard(filter3)); + ability.addMode(mode); + this.addAbility(ability); + } + + private SatsukiTheLivingLore(final SatsukiTheLivingLore card) { + super(card); + } + + @Override + public SatsukiTheLivingLore copy() { + return new SatsukiTheLivingLore(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SavagePackmate.java b/Mage.Sets/src/mage/cards/s/SavagePackmate.java new file mode 100644 index 00000000000..f60351ab25a --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SavagePackmate.java @@ -0,0 +1,51 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.abilities.keyword.NightboundAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SavagePackmate extends CardImpl { + + public SavagePackmate(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); + + this.subtype.add(SubType.WEREWOLF); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + this.color.setRed(true); + this.color.setGreen(true); + this.nightCard = true; + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Other creatures you control get +1/+0. + this.addAbility(new SimpleStaticAbility(new BoostControlledEffect( + 1, 0, Duration.WhileOnBattlefield, true + ))); + + // Nightbound + this.addAbility(new NightboundAbility()); + } + + private SavagePackmate(final SavagePackmate card) { + super(card); + } + + @Override + public SavagePackmate copy() { + return new SavagePackmate(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SavageSmash.java b/Mage.Sets/src/mage/cards/s/SavageSmash.java index 79533c4e61f..b989ee1d17d 100644 --- a/Mage.Sets/src/mage/cards/s/SavageSmash.java +++ b/Mage.Sets/src/mage/cards/s/SavageSmash.java @@ -23,7 +23,8 @@ public final class SavageSmash extends CardImpl { // Target creature you control gets +2/+2 until end of turn. It fights target creature you don't control. this.getSpellAbility().addEffect(new BoostTargetEffect(2, 2, Duration.EndOfTurn)); this.getSpellAbility().addEffect( - new FightTargetsEffect().setText("It fights target creature you don't control") + new FightTargetsEffect().setText("It fights target creature you don't control." + + "(Each deals damage equal to its power to the other.)") ); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); diff --git a/Mage.Sets/src/mage/cards/s/SavageStomp.java b/Mage.Sets/src/mage/cards/s/SavageStomp.java index d2a8f044d1e..a054c9082f0 100644 --- a/Mage.Sets/src/mage/cards/s/SavageStomp.java +++ b/Mage.Sets/src/mage/cards/s/SavageStomp.java @@ -43,7 +43,8 @@ public final class SavageStomp extends CardImpl { Effect effect = new AddCountersTargetEffect(CounterType.P1P1.createInstance()); this.getSpellAbility().addEffect(effect); effect = new FightTargetsEffect(); - effect.setText("Then that creature fights target creature you don't control"); + effect.setText("Then that creature fights target creature you don't control. " + + "(Each deals damage equal to its power to the other.)"); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); diff --git a/Mage.Sets/src/mage/cards/s/SavageSurge.java b/Mage.Sets/src/mage/cards/s/SavageSurge.java index 0446282bb82..28c9788b578 100644 --- a/Mage.Sets/src/mage/cards/s/SavageSurge.java +++ b/Mage.Sets/src/mage/cards/s/SavageSurge.java @@ -1,4 +1,3 @@ - package mage.cards.s; import java.util.UUID; @@ -9,6 +8,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; /** @@ -18,15 +18,17 @@ import mage.target.common.TargetCreaturePermanent; public final class SavageSurge extends CardImpl { public SavageSurge(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{G}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}"); // Target creature gets +2/+2 until end of turn. Untap that creature. this.getSpellAbility().addTarget(new TargetCreaturePermanent()); - this.getSpellAbility().addEffect(new BoostTargetEffect(2, 2, Duration.EndOfTurn)); - Effect effect = new UntapTargetEffect(); - effect.setText("Untap that creature"); - this.getSpellAbility().addEffect(effect); + Effect boostEffect = new BoostTargetEffect(2, 2, Duration.EndOfTurn); + boostEffect.setOutcome(Outcome.Benefit); + this.getSpellAbility().addEffect(boostEffect); + Effect untapEffect = new UntapTargetEffect(); + untapEffect.setOutcome(Outcome.Benefit); + untapEffect.setText("Untap that creature"); + this.getSpellAbility().addEffect(untapEffect); } private SavageSurge(final SavageSurge card) { diff --git a/Mage.Sets/src/mage/cards/s/SavageSwipe.java b/Mage.Sets/src/mage/cards/s/SavageSwipe.java index 65caea08557..2709e5f681f 100644 --- a/Mage.Sets/src/mage/cards/s/SavageSwipe.java +++ b/Mage.Sets/src/mage/cards/s/SavageSwipe.java @@ -48,7 +48,8 @@ class SavageSwipeEffect extends OneShotEffect { SavageSwipeEffect() { super(Outcome.Benefit); staticText = "Target creature you control gets +2/+2 until end of turn if its power is 2. " + - "Then it fights target creature you don't control."; + "Then it fights target creature you don't control. " + + "(Each deals damage equal to its power to the other.)"; } private SavageSwipeEffect(final SavageSwipeEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/SavaiTriome.java b/Mage.Sets/src/mage/cards/s/SavaiTriome.java index bf636c48097..b86a70f3d94 100644 --- a/Mage.Sets/src/mage/cards/s/SavaiTriome.java +++ b/Mage.Sets/src/mage/cards/s/SavaiTriome.java @@ -1,7 +1,7 @@ package mage.cards.s; import mage.abilities.common.EntersBattlefieldTappedAbility; -import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.keyword.CyclingAbility; import mage.abilities.mana.BlackManaAbility; import mage.abilities.mana.RedManaAbility; @@ -34,7 +34,7 @@ public final class SavaiTriome extends CardImpl { this.addAbility(new EntersBattlefieldTappedAbility()); // Cycling {3} - this.addAbility(new CyclingAbility(new ManaCostsImpl("{3}"))); + this.addAbility(new CyclingAbility(new GenericManaCost(3))); } private SavaiTriome(final SavaiTriome card) { diff --git a/Mage.Sets/src/mage/cards/s/SaviorOfOllenbock.java b/Mage.Sets/src/mage/cards/s/SaviorOfOllenbock.java new file mode 100644 index 00000000000..98b39e5a0b7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SaviorOfOllenbock.java @@ -0,0 +1,121 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.LeavesBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ExileTargetForSourceEffect; +import mage.abilities.keyword.TrainingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.ExileZone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.players.Player; +import mage.target.common.TargetCardInGraveyardOrBattlefield; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SaviorOfOllenbock extends CardImpl { + + public SaviorOfOllenbock(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(1); + this.toughness = new MageInt(2); + + // Training + this.addAbility(new TrainingAbility()); + + // Whenever Savior of Ollenbock trains, exile up to one other target creature from the battlefield or creature card from a graveyard. + this.addAbility(new SaviorOfOllenbockTriggeredAbility()); + + // When Savior of Ollenbock leaves the battlefield, put the exiled cards onto the battlefield under their owners' control. + this.addAbility(new LeavesBattlefieldTriggeredAbility(new SaviorOfOllenbockEffect(), false)); + } + + private SaviorOfOllenbock(final SaviorOfOllenbock card) { + super(card); + } + + @Override + public SaviorOfOllenbock copy() { + return new SaviorOfOllenbock(this); + } +} + +class SaviorOfOllenbockTriggeredAbility extends TriggeredAbilityImpl { + + SaviorOfOllenbockTriggeredAbility() { + super(Zone.BATTLEFIELD, new ExileTargetForSourceEffect()); + this.addTarget(new TargetCardInGraveyardOrBattlefield( + 0, 1, + StaticFilters.FILTER_CARD_CREATURE, + StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE + )); + } + + private SaviorOfOllenbockTriggeredAbility(final SaviorOfOllenbockTriggeredAbility ability) { + super(ability); + } + + @Override + public SaviorOfOllenbockTriggeredAbility copy() { + return new SaviorOfOllenbockTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.TRAINED_CREATURE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return event.getTargetId().equals(getSourceId()); + } + + @Override + public String getRule() { + return "Whenever {this} trains, exile up to one other target " + + "creature from the battlefield or creature card from a graveyard."; + } +} + +class SaviorOfOllenbockEffect extends OneShotEffect { + + SaviorOfOllenbockEffect() { + super(Outcome.Benefit); + staticText = "put the exiled cards onto the battlefield under their owners' control"; + } + + private SaviorOfOllenbockEffect(final SaviorOfOllenbockEffect effect) { + super(effect); + } + + @Override + public SaviorOfOllenbockEffect copy() { + return new SaviorOfOllenbockEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source)); + return player != null && exileZone != null && !exileZone.isEmpty() && player.moveCards( + exileZone.getCards(game), Zone.BATTLEFIELD, source, game, + false, false, true, null + ); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SawInHalf.java b/Mage.Sets/src/mage/cards/s/SawInHalf.java new file mode 100644 index 00000000000..5db1f376890 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SawInHalf.java @@ -0,0 +1,76 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenCopyTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SawInHalf extends CardImpl { + + public SawInHalf(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{B}"); + + // Destroy target creature. If that creature dies this way, its controller creates two tokens that are copies of that creature, except their base power is half that creature's power and their base toughness is half that creature's toughness. Round up each time. + this.getSpellAbility().addEffect(new SawInHalfEffect()); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + private SawInHalf(final SawInHalf card) { + super(card); + } + + @Override + public SawInHalf copy() { + return new SawInHalf(this); + } +} + +class SawInHalfEffect extends OneShotEffect { + + SawInHalfEffect() { + super(Outcome.Benefit); + staticText = "destroy target creature. If that creature dies this way, its controller creates " + + "two tokens that are copies of that creature, except their base power is half that creature's " + + "power and their base toughness is half that creature's toughness. Round up each time"; + } + + private SawInHalfEffect(final SawInHalfEffect effect) { + super(effect); + } + + @Override + public SawInHalfEffect copy() { + return new SawInHalfEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent == null + || !permanent.destroy(source, game) + || game.getState().getZone(permanent.getId()) != Zone.GRAVEYARD) { + return false; + } + return new CreateTokenCopyTargetEffect( + permanent.getControllerId(), null, false, 2, false, false, null, + divide(permanent.getPower()), divide(permanent.getToughness()), false + ).setSavedPermanent(permanent).apply(game, source); + } + + private static final int divide(MageInt mageInt) { + return Math.floorDiv(mageInt.getValue(), 2) + mageInt.getValue() % 2; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SawbladeSlinger.java b/Mage.Sets/src/mage/cards/s/SawbladeSlinger.java new file mode 100644 index 00000000000..1a3a454b37d --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SawbladeSlinger.java @@ -0,0 +1,63 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.FightTargetSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterArtifactPermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SawbladeSlinger extends CardImpl { + + private static final FilterPermanent filter = new FilterArtifactPermanent("artifact an opponent controls"); + private static final FilterPermanent filter2 = new FilterPermanent(SubType.ZOMBIE, "Zombie an opponent controls"); + + static { + filter.add(TargetController.OPPONENT.getControllerPredicate()); + filter2.add(TargetController.OPPONENT.getControllerPredicate()); + } + + public SawbladeSlinger(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ARCHER); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + + // When Sawblade Slinger enters the battlefield, choose up to one — + // • Destroy target artifact an opponent controls. + Ability ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect()); + ability.addTarget(new TargetPermanent(filter)); + ability.getModes().setMinModes(0); + ability.getModes().setMaxModes(1); + + // • Sawblade Slinger fights target Zombie an opponent controls. + Mode mode = new Mode(new FightTargetSourceEffect()); + mode.addTarget(new TargetPermanent(filter2)); + ability.addMode(mode); + this.addAbility(ability); + } + + private SawbladeSlinger(final SawbladeSlinger card) { + super(card); + } + + @Override + public SawbladeSlinger copy() { + return new SawbladeSlinger(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/ScabClanGiant.java b/Mage.Sets/src/mage/cards/s/ScabClanGiant.java index 457a02c4a10..7c85ea2260e 100644 --- a/Mage.Sets/src/mage/cards/s/ScabClanGiant.java +++ b/Mage.Sets/src/mage/cards/s/ScabClanGiant.java @@ -9,8 +9,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.Target; import mage.target.common.TargetCreaturePermanent; @@ -21,12 +20,6 @@ import java.util.UUID; */ public final class ScabClanGiant extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public ScabClanGiant(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}{G}"); this.subtype.add(SubType.GIANT); @@ -38,7 +31,7 @@ public final class ScabClanGiant extends CardImpl { // When Scab-Clan Giant enters the battlefield, it fights target creature an opponent controls chosen at random. Ability ability = new EntersBattlefieldTriggeredAbility(new FightTargetSourceEffect() .setText("it fights target creature an opponent controls chosen at random")); - Target target = new TargetCreaturePermanent(filter); + Target target = new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE); target.setRandom(true); ability.addTarget(target); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/s/ScaleBlessing.java b/Mage.Sets/src/mage/cards/s/ScaleBlessing.java index 47786c4a415..02a31c40dff 100644 --- a/Mage.Sets/src/mage/cards/s/ScaleBlessing.java +++ b/Mage.Sets/src/mage/cards/s/ScaleBlessing.java @@ -2,20 +2,13 @@ package mage.cards.s; import java.util.UUID; -import mage.MageObject; -import mage.abilities.Ability; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.counter.AddCountersAllEffect; import mage.abilities.effects.keyword.BolsterEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.counters.CounterType; -import mage.filter.common.FilterCreaturePermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; +import mage.filter.StaticFilters; /** * @@ -26,12 +19,15 @@ public final class ScaleBlessing extends CardImpl { public ScaleBlessing(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{W}"); - // Bolster 1, then put a +1/+1 counter on each creature you control with a +1/+1 counter on it. - Effect effect = new BolsterEffect(1); - effect.setText("Bolster 1"); - this.getSpellAbility().addEffect(effect); - this.getSpellAbility().addEffect(new ScaleBlessingEffect()); + // Bolster 1, + this.getSpellAbility().addEffect(new BolsterEffect(1).setText("Bolster 1, ")); + // then put a +1/+1 counter on each creature you control with a +1/+1 counter on it. + this.getSpellAbility().addEffect(new AddCountersAllEffect( + CounterType.P1P1.createInstance(), StaticFilters.FILTER_EACH_CONTROLLED_CREATURE_P1P1 + ).setText("then put a +1/+1 counter on each creature you control with a +1/+1 counter on it. " + + "(To bolster 1, choose a creature with the least toughness among creatures you control " + + "and put a +1/+1 counter on it.)")); } private ScaleBlessing(final ScaleBlessing card) { @@ -43,39 +39,3 @@ public final class ScaleBlessing extends CardImpl { return new ScaleBlessing(this); } } - -class ScaleBlessingEffect extends OneShotEffect { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); - - static { - filter.add(CounterType.P1P1.getPredicate()); - } - - public ScaleBlessingEffect() { - super(Outcome.Benefit); - this.staticText = ", then put a +1/+1 counter on each creature you control with a +1/+1 counter on it. (To bolster 1, choose a creature with the least toughness among creatures you control and put +1/+1 counter on it.)"; - } - - public ScaleBlessingEffect(final ScaleBlessingEffect effect) { - super(effect); - } - - @Override - public ScaleBlessingEffect copy() { - return new ScaleBlessingEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = source.getSourceObject(game); - if (controller != null && sourceObject != null) { - for (Permanent permanent : game.getState().getBattlefield().getAllActivePermanents(filter, controller.getId(), game)) { - permanent.addCounters(CounterType.P1P1.createInstance(), source.getControllerId(), source, game); - game.informPlayers(sourceObject.getName() + ": Put a +1/+1 counter on " + permanent.getLogName()); - } - } - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/s/ScaleTheHeights.java b/Mage.Sets/src/mage/cards/s/ScaleTheHeights.java index 838736eedf5..ce529bc7b70 100644 --- a/Mage.Sets/src/mage/cards/s/ScaleTheHeights.java +++ b/Mage.Sets/src/mage/cards/s/ScaleTheHeights.java @@ -28,7 +28,7 @@ public final class ScaleTheHeights extends CardImpl { this.getSpellAbility().addTarget(new TargetCreaturePermanent(0, 1)); // Draw a card. - this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("
")); } private ScaleTheHeights(final ScaleTheHeights card) { diff --git a/Mage.Sets/src/mage/cards/s/ScaledHulk.java b/Mage.Sets/src/mage/cards/s/ScaledHulk.java index d26a4206532..1bb3ca44ed0 100644 --- a/Mage.Sets/src/mage/cards/s/ScaledHulk.java +++ b/Mage.Sets/src/mage/cards/s/ScaledHulk.java @@ -25,7 +25,7 @@ public final class ScaledHulk extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(4); // Whenever you cast a Spirit or Arcane spell, Scaled Hulk gets +2/+2 until end of turn. - this.addAbility(new SpellCastControllerTriggeredAbility(new BoostSourceEffect(2, 2, Duration.EndOfTurn), StaticFilters.SPIRIT_OR_ARCANE_CARD, false)); + this.addAbility(new SpellCastControllerTriggeredAbility(new BoostSourceEffect(2, 2, Duration.EndOfTurn), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false)); } private ScaledHulk(final ScaledHulk card) { diff --git a/Mage.Sets/src/mage/cards/s/ScatterArc.java b/Mage.Sets/src/mage/cards/s/ScatterArc.java index 443bf49d7f2..dfe0807614d 100644 --- a/Mage.Sets/src/mage/cards/s/ScatterArc.java +++ b/Mage.Sets/src/mage/cards/s/ScatterArc.java @@ -7,8 +7,7 @@ import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.FilterSpell; -import mage.filter.predicate.Predicates; +import mage.filter.StaticFilters; import mage.target.TargetSpell; /** @@ -17,18 +16,12 @@ import mage.target.TargetSpell; */ public final class ScatterArc extends CardImpl { - private static final FilterSpell filter = new FilterSpell("noncreature spell"); - - static { - filter.add(Predicates.not(CardType.CREATURE.getPredicate())); - } - public ScatterArc(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{3}{U}"); // Counter target noncreature spell. this.getSpellAbility().addEffect(new CounterTargetEffect()); - this.getSpellAbility().addTarget(new TargetSpell(filter)); + this.getSpellAbility().addTarget(new TargetSpell(StaticFilters.FILTER_SPELL_NON_CREATURE)); // Draw a card. this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); diff --git a/Mage.Sets/src/mage/cards/s/ScatteredThoughts.java b/Mage.Sets/src/mage/cards/s/ScatteredThoughts.java new file mode 100644 index 00000000000..50d1799fe40 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/ScatteredThoughts.java @@ -0,0 +1,36 @@ +package mage.cards.s; + +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.common.LookLibraryAndPickControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ScatteredThoughts extends CardImpl { + + public ScatteredThoughts(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{U}"); + + // Look at the top four cards of your library. Put two of those cards into your hand and the rest into your graveyard. + this.getSpellAbility().addEffect(new LookLibraryAndPickControllerEffect( + StaticValue.get(4), false, StaticValue.get(2), + StaticFilters.FILTER_CARD, Zone.LIBRARY, false, false + )); + } + + private ScatteredThoughts(final ScatteredThoughts card) { + super(card); + } + + @Override + public ScatteredThoughts copy() { + return new ScatteredThoughts(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/ScionOfOpulence.java b/Mage.Sets/src/mage/cards/s/ScionOfOpulence.java new file mode 100644 index 00000000000..2233763e73f --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/ScionOfOpulence.java @@ -0,0 +1,67 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DiesThisOrAnotherCreatureTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.ExileTopXMayPlayUntilEndOfTurnEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledArtifactPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.permanent.token.TreasureToken; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ScionOfOpulence extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledPermanent(SubType.VAMPIRE, "nontoken Vampire you control"); + private static final FilterControlledPermanent filter2 + = new FilterControlledArtifactPermanent("artifacts"); + + static { + filter.add(TokenPredicate.FALSE); + } + + public ScionOfOpulence(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); + + this.subtype.add(SubType.VAMPIRE); + this.subtype.add(SubType.NOBLE); + this.power = new MageInt(3); + this.toughness = new MageInt(1); + + // Whenever Scion of Opulence or another nontoken Vampire you control dies, create a Treasure token. + this.addAbility(new DiesThisOrAnotherCreatureTriggeredAbility( + new CreateTokenEffect(new TreasureToken()), false, filter + )); + + // {R}, Sacrifice two artifacts: Exile the top card of your library. You may play that card this turn. + Ability ability = new SimpleActivatedAbility( + new ExileTopXMayPlayUntilEndOfTurnEffect(1), new ManaCostsImpl<>("{R}") + ); + ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(2, filter2))); + this.addAbility(ability); + } + + private ScionOfOpulence(final ScionOfOpulence card) { + super(card); + } + + @Override + public ScionOfOpulence copy() { + return new ScionOfOpulence(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/ScionOfTheWild.java b/Mage.Sets/src/mage/cards/s/ScionOfTheWild.java index a026849aa62..3d136fe50fc 100644 --- a/Mage.Sets/src/mage/cards/s/ScionOfTheWild.java +++ b/Mage.Sets/src/mage/cards/s/ScionOfTheWild.java @@ -12,22 +12,20 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Zone; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; /** * @author anonymous */ public final class ScionOfTheWild extends CardImpl { - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("creatures you control"); - public ScionOfTheWild(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{G}{G}"); this.subtype.add(SubType.AVATAR); this.power = new MageInt(0); this.toughness = new MageInt(0); - this.addAbility(new SimpleStaticAbility(Zone.ALL, new SetPowerToughnessSourceEffect(new PermanentsOnBattlefieldCount(filter), Duration.EndOfGame))); + this.addAbility(new SimpleStaticAbility(Zone.ALL, new SetPowerToughnessSourceEffect(new PermanentsOnBattlefieldCount(StaticFilters.FILTER_CONTROLLED_CREATURES), Duration.EndOfGame))); } private ScionOfTheWild(final ScionOfTheWild card) { diff --git a/Mage.Sets/src/mage/cards/s/ScornedVillager.java b/Mage.Sets/src/mage/cards/s/ScornedVillager.java index 69cce60517a..2092c0575b1 100644 --- a/Mage.Sets/src/mage/cards/s/ScornedVillager.java +++ b/Mage.Sets/src/mage/cards/s/ScornedVillager.java @@ -24,7 +24,6 @@ public final class ScornedVillager extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); - this.transformable = true; this.secondSideCardClazz = mage.cards.m.MoonscarredWerewolf.class; // {tap}: Add {G}. diff --git a/Mage.Sets/src/mage/cards/s/ScourTheDesert.java b/Mage.Sets/src/mage/cards/s/ScourTheDesert.java index 2b2124b6eca..637dba44dda 100644 --- a/Mage.Sets/src/mage/cards/s/ScourTheDesert.java +++ b/Mage.Sets/src/mage/cards/s/ScourTheDesert.java @@ -58,7 +58,7 @@ class ScourTheDesertEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getFirstTarget()); + Player player = game.getPlayer(source.getControllerId()); Card card = game.getCard(source.getFirstTarget()); if (player == null || card == null) { return false; diff --git a/Mage.Sets/src/mage/cards/s/ScrapWelder.java b/Mage.Sets/src/mage/cards/s/ScrapWelder.java new file mode 100644 index 00000000000..1b42fd60286 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/ScrapWelder.java @@ -0,0 +1,136 @@ +package mage.cards.s; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.VariableCostImpl; +import mage.abilities.costs.VariableCostType; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.HasteAbility; +import mage.cards.Card; +import mage.constants.*; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.filter.common.FilterArtifactCard; +import mage.filter.common.FilterControlledArtifactPermanent; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCardInYourGraveyard; +import mage.target.targetadjustment.TargetAdjuster; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author weirddan455 + */ +public final class ScrapWelder extends CardImpl { + + public ScrapWelder(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); + + this.subtype.add(SubType.GOBLIN); + this.subtype.add(SubType.ARTIFICER); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // {T}, Sacrifice an artifact with mana value X: Return target artifact card with mana value less than X from your graveyard to the battlefield. It gains haste until end of turn. + Ability ability = new SimpleActivatedAbility(new ScrapWelderEffect(), new TapSourceCost()); + ability.addCost(new ScrapWelderCost()); + ability.setTargetAdjuster(ScrapWelderTargetAdjuster.instance); + this.addAbility(ability); + } + + private ScrapWelder(final ScrapWelder card) { + super(card); + } + + @Override + public ScrapWelder copy() { + return new ScrapWelder(this); + } +} + +class ScrapWelderEffect extends OneShotEffect { + + public ScrapWelderEffect() { + super(Outcome.PutCardInPlay); + this.staticText = "Return target artifact card with mana value less than X from your graveyard to the battlefield. It gains haste until end of turn"; + } + + private ScrapWelderEffect(final ScrapWelderEffect effect) { + super(effect); + } + + @Override + public ScrapWelderEffect copy() { + return new ScrapWelderEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Card card = game.getCard(source.getFirstTarget()); + if (controller == null || card == null || game.getState().getZone(card.getId()) != Zone.GRAVEYARD) { + return false; + } + controller.moveCards(card, Zone.BATTLEFIELD, source, game); + Permanent permanent = game.getPermanent(card.getId()); + if (permanent != null) { + ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance()); + effect.setTargetPointer(new FixedTarget(permanent, game)); + game.addEffect(effect, source); + } + return true; + } +} + +class ScrapWelderCost extends VariableCostImpl { + + public ScrapWelderCost() { + super(VariableCostType.NORMAL, "mana value"); + this.text = "Sacrifice an artifact with mana value X"; + } + + private ScrapWelderCost(final ScrapWelderCost cost) { + super(cost); + } + + @Override + public ScrapWelderCost copy() { + return new ScrapWelderCost(this); + } + + @Override + public Cost getFixedCostsFromAnnouncedValue(int xValue) { + FilterControlledArtifactPermanent filter = new FilterControlledArtifactPermanent("an artifact with mana value " + xValue); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, xValue)); + return new SacrificeTargetCost(filter); + } +} + +enum ScrapWelderTargetAdjuster implements TargetAdjuster { + instance; + + @Override + public void adjustTargets(Ability ability, Game game) { + int xValue = 0; + for (Cost cost : ability.getCosts()) { + if (cost instanceof ScrapWelderCost) { + xValue = ((ScrapWelderCost) cost).getAmount(); + break; + } + } + FilterArtifactCard filter = new FilterArtifactCard("artifact card with mana value less than " + xValue + " from your graveyard"); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, xValue)); + ability.getTargets().clear(); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + } +} diff --git a/Mage.Sets/src/mage/cards/s/ScrapheapScrounger.java b/Mage.Sets/src/mage/cards/s/ScrapheapScrounger.java index ab283fbae6b..158189b471c 100644 --- a/Mage.Sets/src/mage/cards/s/ScrapheapScrounger.java +++ b/Mage.Sets/src/mage/cards/s/ScrapheapScrounger.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.CantBlockAbility; @@ -19,8 +17,9 @@ import mage.filter.common.FilterCreatureCard; import mage.filter.predicate.mageobject.AnotherCardPredicate; import mage.target.common.TargetCardInYourGraveyard; +import java.util.UUID; + /** - * * @author emerald000 */ public final class ScrapheapScrounger extends CardImpl { @@ -41,7 +40,7 @@ public final class ScrapheapScrounger extends CardImpl { this.addAbility(new CantBlockAbility()); // {1}{B}, Exile another creature card from your graveyard: Return Scrapheap Scrounger from your graveyard to the battlefield. - Ability ability = new SimpleActivatedAbility(Zone.GRAVEYARD, new ReturnSourceFromGraveyardToBattlefieldEffect(false), new ManaCostsImpl<>("{1}{B}")); + Ability ability = new SimpleActivatedAbility(Zone.GRAVEYARD, new ReturnSourceFromGraveyardToBattlefieldEffect(false, false), new ManaCostsImpl<>("{1}{B}")); ability.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard(filter))); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/ScrapyardSteelbreaker.java b/Mage.Sets/src/mage/cards/s/ScrapyardSteelbreaker.java new file mode 100644 index 00000000000..50bca8eac09 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/ScrapyardSteelbreaker.java @@ -0,0 +1,57 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.common.FilterControlledArtifactPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ScrapyardSteelbreaker extends CardImpl { + + private static final FilterControlledPermanent filter + = new FilterControlledArtifactPermanent("another artifact"); + + static { + filter.add(AnotherPredicate.instance); + } + + public ScrapyardSteelbreaker(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}{R}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // {1}, Sacrifice another artifact: Scrapyard Steelbreaker gets +2/+1 until end of turn. + Ability ability = new SimpleActivatedAbility( + new BoostSourceEffect(2, 1, Duration.EndOfTurn), new GenericManaCost(1) + ); + ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(filter))); + this.addAbility(ability); + } + + private ScrapyardSteelbreaker(final ScrapyardSteelbreaker card) { + super(card); + } + + @Override + public ScrapyardSteelbreaker copy() { + return new ScrapyardSteelbreaker(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/ScreamingSwarm.java b/Mage.Sets/src/mage/cards/s/ScreamingSwarm.java new file mode 100644 index 00000000000..51915a260d3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/ScreamingSwarm.java @@ -0,0 +1,111 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.AttacksWithCreaturesTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.MillCardsTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetPlayer; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ScreamingSwarm extends CardImpl { + + public ScreamingSwarm(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{U}"); + + this.subtype.add(SubType.BIRD); + this.subtype.add(SubType.HORROR); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever you attack with one or more creatures, target player mills that many cards. + Ability ability = new AttacksWithCreaturesTriggeredAbility( + new MillCardsTargetEffect(ScreamingSwarmValue.instance) + .setText("target player mills that many cards"), + 0 + ).setTriggerPhrase("Whenever you attack with one or more creatures, "); + ability.addTarget(new TargetPlayer()); + this.addAbility(ability); + + // {2}{U}: Put Screaming Swarm from your graveyard into your library second from the top. + this.addAbility(new SimpleActivatedAbility(Zone.GRAVEYARD, new ScreamingSwarmEffect(), new ManaCostsImpl<>("{2}{U}"))); + } + + private ScreamingSwarm(final ScreamingSwarm card) { + super(card); + } + + @Override + public ScreamingSwarm copy() { + return new ScreamingSwarm(this); + } +} + +enum ScreamingSwarmValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + return (Integer) effect.getValue("attackers"); + } + + @Override + public ScreamingSwarmValue copy() { + return this; + } + + @Override + public String getMessage() { + return ""; + } +} + +class ScreamingSwarmEffect extends OneShotEffect { + + ScreamingSwarmEffect() { + super(Outcome.Benefit); + staticText = "put {this} from your graveyard into your library second from the top"; + } + + private ScreamingSwarmEffect(final ScreamingSwarmEffect effect) { + super(effect); + } + + @Override + public ScreamingSwarmEffect copy() { + return new ScreamingSwarmEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + MageObject sourceObject = source.getSourceObjectIfItStillExists(game); + return player != null + && sourceObject instanceof Card + && player.putCardOnTopXOfLibrary( + (Card) sourceObject, game, source, 2, true + ); + } +} diff --git a/Mage.Sets/src/mage/cards/s/ScreechingBat.java b/Mage.Sets/src/mage/cards/s/ScreechingBat.java index 8c1c31fd8b7..ddfabda8560 100644 --- a/Mage.Sets/src/mage/cards/s/ScreechingBat.java +++ b/Mage.Sets/src/mage/cards/s/ScreechingBat.java @@ -1,12 +1,9 @@ package mage.cards.s; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.costs.Cost; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DoIfCostPaid; import mage.abilities.effects.common.TransformSourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.TransformAbility; @@ -14,11 +11,9 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; +import mage.constants.TargetController; + +import java.util.UUID; /** * @author nantuko @@ -29,7 +24,6 @@ public final class ScreechingBat extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); this.subtype.add(SubType.BAT); - this.transformable = true; this.secondSideCardClazz = StalkingVampire.class; this.power = new MageInt(2); @@ -39,7 +33,10 @@ public final class ScreechingBat extends CardImpl { // At the beginning of your upkeep, you may pay {2}{B}{B}. If you do, transform Screeching Bat. this.addAbility(new TransformAbility()); - this.addAbility(new ScreechingBatBeginningOfUpkeepTriggeredAbility()); + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new DoIfCostPaid( + new TransformSourceEffect(), + new ManaCostsImpl<>("{2}{B}{B}") + ), TargetController.YOU, false)); } private ScreechingBat(final ScreechingBat card) { @@ -51,65 +48,3 @@ public final class ScreechingBat extends CardImpl { return new ScreechingBat(this); } } - -class ScreechingBatBeginningOfUpkeepTriggeredAbility extends TriggeredAbilityImpl { - - public ScreechingBatBeginningOfUpkeepTriggeredAbility() { - super(Zone.BATTLEFIELD, new ScreechingBatTransformSourceEffect(), true); - } - - public ScreechingBatBeginningOfUpkeepTriggeredAbility(final ScreechingBatBeginningOfUpkeepTriggeredAbility ability) { - super(ability); - } - - @Override - public ScreechingBatBeginningOfUpkeepTriggeredAbility copy() { - return new ScreechingBatBeginningOfUpkeepTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.UPKEEP_STEP_PRE; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return event.getPlayerId().equals(this.controllerId); - } - - @Override - public String getRule() { - return "At the beginning of your upkeep, you may pay {2}{B}{B}. If you do, transform {this}."; - } -} - -class ScreechingBatTransformSourceEffect extends OneShotEffect { - - public ScreechingBatTransformSourceEffect() { - super(Outcome.Transform); - staticText = "transform {this}"; - } - - public ScreechingBatTransformSourceEffect(final ScreechingBatTransformSourceEffect effect) { - super(effect); - } - - @Override - public ScreechingBatTransformSourceEffect copy() { - return new ScreechingBatTransformSourceEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent != null) { - Cost cost = new ManaCostsImpl("{2}{B}{B}"); - if (cost.pay(source, game, source, permanent.getControllerId(), false, null)) { - new TransformSourceEffect(true).apply(game, source); - } - return true; - } - return false; - } - -} diff --git a/Mage.Sets/src/mage/cards/s/ScroungedScythe.java b/Mage.Sets/src/mage/cards/s/ScroungedScythe.java index 56640dbf400..8426082f857 100644 --- a/Mage.Sets/src/mage/cards/s/ScroungedScythe.java +++ b/Mage.Sets/src/mage/cards/s/ScroungedScythe.java @@ -21,8 +21,6 @@ import mage.constants.*; */ public final class ScroungedScythe extends CardImpl { - private static final String staticText = "As long as equipped creature is a Human, it has menace"; - public ScroungedScythe(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},""); this.subtype.add(SubType.EQUIPMENT); @@ -33,9 +31,13 @@ public final class ScroungedScythe extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(1, 1))); // As long as equipped creature is a Human, it has menace. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, - new ConditionalContinuousEffect(new GainAbilityAttachedEffect(new MenaceAbility(), AttachmentType.EQUIPMENT), - new EquippedHasSubtypeCondition(SubType.HUMAN), staticText))); + this.addAbility(new SimpleStaticAbility( + Zone.BATTLEFIELD, + new ConditionalContinuousEffect( + new GainAbilityAttachedEffect(new MenaceAbility(), AttachmentType.EQUIPMENT), + new EquippedHasSubtypeCondition(SubType.HUMAN), + "As long as equipped creature is a Human, it has menace. " + + "(It can't be blocked except by two or more creatures.)"))); // Equip {2} this.addAbility(new EquipAbility(Outcome.AddAbility, new GenericManaCost(2))); diff --git a/Mage.Sets/src/mage/cards/s/ScroungingBandar.java b/Mage.Sets/src/mage/cards/s/ScroungingBandar.java index c4b96834e73..e56ee36c4b4 100644 --- a/Mage.Sets/src/mage/cards/s/ScroungingBandar.java +++ b/Mage.Sets/src/mage/cards/s/ScroungingBandar.java @@ -65,7 +65,7 @@ class ScroungingBandarEffect extends OneShotEffect { public ScroungingBandarEffect() { super(Outcome.Benefit); - this.staticText = "move any number of +1/+1 counters from Scrounging Bandar onto another target creature"; + this.staticText = "you may move any number of +1/+1 counters from Scrounging Bandar onto another target creature"; } public ScroungingBandarEffect(final ScroungingBandarEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/ScuttlingSliver.java b/Mage.Sets/src/mage/cards/s/ScuttlingSliver.java index 9532d22d0f3..ba1be036681 100644 --- a/Mage.Sets/src/mage/cards/s/ScuttlingSliver.java +++ b/Mage.Sets/src/mage/cards/s/ScuttlingSliver.java @@ -32,7 +32,7 @@ public final class ScuttlingSliver extends CardImpl { this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( new SimpleActivatedAbility( new UntapSourceEffect().setText("untap this creature"), new GenericManaCost(2) - ), Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS) + ), Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_SLIVERS) .withForceQuotes() )); } diff --git a/Mage.Sets/src/mage/cards/s/SeaDasherOctopus.java b/Mage.Sets/src/mage/cards/s/SeaDasherOctopus.java index a79023fef81..0fe597505f0 100644 --- a/Mage.Sets/src/mage/cards/s/SeaDasherOctopus.java +++ b/Mage.Sets/src/mage/cards/s/SeaDasherOctopus.java @@ -33,7 +33,7 @@ public final class SeaDasherOctopus extends CardImpl { // Whenever this creature deals combat damage to a player, draw a card. this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility( new DrawCardSourceControllerEffect(1), false - )); + ).setTriggerPhrase("Whenever this creature deals combat damage to a player, ")); } private SeaDasherOctopus(final SeaDasherOctopus card) { diff --git a/Mage.Sets/src/mage/cards/s/SeafaringWerewolf.java b/Mage.Sets/src/mage/cards/s/SeafaringWerewolf.java index c075a58ed96..56b3b8c7054 100644 --- a/Mage.Sets/src/mage/cards/s/SeafaringWerewolf.java +++ b/Mage.Sets/src/mage/cards/s/SeafaringWerewolf.java @@ -24,7 +24,6 @@ public final class SeafaringWerewolf extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(1); this.color.setGreen(true); - this.transformable = true; this.nightCard = true; // Seafaring Werewolf can't be blocked. diff --git a/Mage.Sets/src/mage/cards/s/SealOfDoom.java b/Mage.Sets/src/mage/cards/s/SealOfDoom.java index 4abfdc76259..aa520bbe9fc 100644 --- a/Mage.Sets/src/mage/cards/s/SealOfDoom.java +++ b/Mage.Sets/src/mage/cards/s/SealOfDoom.java @@ -1,8 +1,6 @@ - package mage.cards.s; import java.util.UUID; -import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; @@ -11,9 +9,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Zone; -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; /** @@ -21,17 +17,11 @@ import mage.target.common.TargetCreaturePermanent; */ public final class SealOfDoom extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public SealOfDoom(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{B}"); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(true), new SacrificeSourceCost()); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SealedFate.java b/Mage.Sets/src/mage/cards/s/SealedFate.java index ac53d3c82d3..ff9c4e84afd 100644 --- a/Mage.Sets/src/mage/cards/s/SealedFate.java +++ b/Mage.Sets/src/mage/cards/s/SealedFate.java @@ -7,7 +7,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Zone; -import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.target.common.TargetOpponent; import java.util.UUID; @@ -22,11 +22,12 @@ public final class SealedFate extends CardImpl { // Look at the top X cards of target opponent's library. Exile one of those cards and put the rest back on top of that player's library in any order. this.getSpellAbility().addTarget(new TargetOpponent()); - this.getSpellAbility().addEffect(new LookLibraryAndPickControllerEffect(ManacostVariableValue.REGULAR, - false, StaticValue.get(1), - new FilterCard("a card to exile"), Zone.LIBRARY, true, - false, false, Zone.EXILED, - false, true, true)); + this.getSpellAbility().addEffect(new LookLibraryAndPickControllerEffect( + ManacostVariableValue.REGULAR, false, StaticValue.get(1), + StaticFilters.FILTER_CARD, Zone.LIBRARY, true, false, + false, Zone.EXILED, false, true, true + ).setText("look at the top X cards of target opponent's library. Exile one of those cards " + + "and put the rest back on top of that player's library in any order")); } private SealedFate(final SealedFate card) { diff --git a/Mage.Sets/src/mage/cards/s/SealockMonster.java b/Mage.Sets/src/mage/cards/s/SealockMonster.java index 79b76c83814..448b3d7e90c 100644 --- a/Mage.Sets/src/mage/cards/s/SealockMonster.java +++ b/Mage.Sets/src/mage/cards/s/SealockMonster.java @@ -5,7 +5,7 @@ import mage.abilities.Ability; import mage.abilities.common.BecomesMonstrousSourceTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.combat.CantAttackUnlessDefenderControllsPermanent; -import mage.abilities.effects.common.continuous.AddCardSubTypeTargetEffect; +import mage.abilities.effects.common.continuous.BecomesBasicLandTargetEffect; import mage.abilities.keyword.MonstrosityAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -40,7 +40,7 @@ public final class SealockMonster extends CardImpl { // When Sealock Monster becomes monstrous, target land becomes an island in addition to its other types. Ability ability = new BecomesMonstrousSourceTriggeredAbility( - new AddCardSubTypeTargetEffect(SubType.ISLAND, Duration.EndOfTurn) + new BecomesBasicLandTargetEffect(Duration.EndOfGame, false, false, SubType.ISLAND) ); ability.addTarget(new TargetLandPermanent()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/s/Seance.java b/Mage.Sets/src/mage/cards/s/Seance.java index 5795c7722ec..e40245e171d 100644 --- a/Mage.Sets/src/mage/cards/s/Seance.java +++ b/Mage.Sets/src/mage/cards/s/Seance.java @@ -75,7 +75,7 @@ class SeanceEffect extends OneShotEffect { effect.setAdditionalSubType(SubType.SPIRIT); effect.apply(game, source); ExileTargetEffect exileEffect = new ExileTargetEffect(); - exileEffect.setTargetPointer(new FixedTargets(effect.getAddedPermanent(), game)); + exileEffect.setTargetPointer(new FixedTargets(effect.getAddedPermanents(), game)); DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); game.addDelayedTriggeredAbility(delayedAbility, source); return true; diff --git a/Mage.Sets/src/mage/cards/s/SearchForAzcanta.java b/Mage.Sets/src/mage/cards/s/SearchForAzcanta.java index 32cb3ac1446..6973145726a 100644 --- a/Mage.Sets/src/mage/cards/s/SearchForAzcanta.java +++ b/Mage.Sets/src/mage/cards/s/SearchForAzcanta.java @@ -29,7 +29,6 @@ public final class SearchForAzcanta extends CardImpl { public SearchForAzcanta(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}"); - this.transformable = true; this.secondSideCardClazz = mage.cards.a.AzcantaTheSunkenRuin.class; this.addSuperType(SuperType.LEGENDARY); @@ -80,7 +79,7 @@ class SearchForAzcantaLookLibraryEffect extends OneShotEffect { controller.moveCards(card, Zone.GRAVEYARD, source, game); } if (controller.getGraveyard().size() > 6 && controller.chooseUse(Outcome.Neutral, "Transform " + sourceObject.getLogName() + "?", source, game)) { - new TransformSourceEffect(true).apply(game, source); + new TransformSourceEffect().apply(game, source); } } } diff --git a/Mage.Sets/src/mage/cards/s/SearchlightCompanion.java b/Mage.Sets/src/mage/cards/s/SearchlightCompanion.java new file mode 100644 index 00000000000..deec3dc21af --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SearchlightCompanion.java @@ -0,0 +1,42 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.SpiritToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SearchlightCompanion extends CardImpl { + + public SearchlightCompanion(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}"); + + this.subtype.add(SubType.DRONE); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When Searchlight Companion enters the battlefield, create a 1/1 colorless Spirit creature token. + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new SpiritToken()))); + } + + private SearchlightCompanion(final SearchlightCompanion card) { + super(card); + } + + @Override + public SearchlightCompanion copy() { + return new SearchlightCompanion(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/Seasinger.java b/Mage.Sets/src/mage/cards/s/Seasinger.java index 229e667271e..8c934c32b80 100644 --- a/Mage.Sets/src/mage/cards/s/Seasinger.java +++ b/Mage.Sets/src/mage/cards/s/Seasinger.java @@ -1,13 +1,10 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.ControlsPermanentsControllerTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SkipUntapOptionalAbility; -import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.condition.common.SourceTappedCondition; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.decorator.ConditionalContinuousEffect; @@ -17,53 +14,54 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.common.FilterLandPermanent; -import mage.filter.predicate.mageobject.CardIdPredicate; -import mage.filter.predicate.permanent.ControllerControlsIslandPredicate; -import mage.target.common.TargetCreaturePermanent; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; + +import java.util.UUID; /** - * * @author jeffwadsworth */ public final class Seasinger extends CardImpl { - private static final String rule = "Gain control of target creature whose controller controls an Island for as long as you control Seasinger and Seasinger remains tapped"; - private static final FilterPermanent islandYouControl = new FilterPermanent("Island"); - private static final FilterCreaturePermanent creatureWhoseControllerControlsIsland = new FilterCreaturePermanent("creature whose controller controls an island"); + private static final FilterPermanent filter + = new FilterPermanent("Island"); + private static final FilterPermanent filter2 + = new FilterCreaturePermanent("creature whose controler controls an Island"); static { - islandYouControl.add(SubType.ISLAND.getPredicate()); - islandYouControl.add(TargetController.YOU.getControllerPredicate()); + filter.add(SubType.ISLAND.getPredicate()); + filter.add(TargetController.YOU.getControllerPredicate()); + filter2.add(SeasingerPredicate.instance); } public Seasinger(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{U}"); this.subtype.add(SubType.MERFOLK); this.power = new MageInt(0); this.toughness = new MageInt(1); - FilterPermanent seasinger = new FilterPermanent(); - seasinger.add(TargetController.YOU.getControllerPredicate()); - seasinger.add(new CardIdPredicate(this.getId())); - // When you control no Islands, sacrifice Seasinger. this.addAbility(new ControlsPermanentsControllerTriggeredAbility( - new FilterLandPermanent(SubType.ISLAND, "no Islands"), ComparisonType.EQUAL_TO, 0, - new SacrificeSourceEffect())); + filter, ComparisonType.EQUAL_TO, 0, new SacrificeSourceEffect() + )); // You may choose not to untap Seasinger during your untap step. this.addAbility(new SkipUntapOptionalAbility()); // {tap}: Gain control of target creature whose controller controls an Island for as long as you control Seasinger and Seasinger remains tapped. - ConditionalContinuousEffect effect = new ConditionalContinuousEffect( - new GainControlTargetEffect(Duration.Custom), - new PermanentsOnTheBattlefieldCondition(seasinger, ComparisonType.EQUAL_TO, 1, SourceTappedCondition.instance), rule); - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new TapSourceCost()); - creatureWhoseControllerControlsIsland.add(new ControllerControlsIslandPredicate()); - ability.addTarget(new TargetCreaturePermanent(creatureWhoseControllerControlsIsland)); + Ability ability = new SimpleActivatedAbility(new ConditionalContinuousEffect( + new GainControlTargetEffect(Duration.WhileControlled), SourceTappedCondition.TAPPED, + "gain control of target creature whose controller controls " + + "an Island for as long as you control {this} and {this} remains tapped" + ), new TapSourceCost()); + ability.addTarget(new TargetPermanent(filter2)); this.addAbility(ability); } @@ -76,3 +74,13 @@ public final class Seasinger extends CardImpl { return new Seasinger(this); } } + +enum SeasingerPredicate implements ObjectSourcePlayerPredicate { + instance; + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.ISLAND); + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + return game.getBattlefield().contains(filter, input.getSourceId(), input.getObject().getControllerId(), game, 1); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SeasonOfGrowth.java b/Mage.Sets/src/mage/cards/s/SeasonOfGrowth.java index dcee18948d5..f4a24c3e18e 100644 --- a/Mage.Sets/src/mage/cards/s/SeasonOfGrowth.java +++ b/Mage.Sets/src/mage/cards/s/SeasonOfGrowth.java @@ -29,7 +29,7 @@ public final class SeasonOfGrowth extends CardImpl { // Whenever a creature enters the battlefield under your control, scry 1. this.addAbility(new EntersBattlefieldControlledTriggeredAbility( - new ScryEffect(1), StaticFilters.FILTER_PERMANENT_CREATURE_A + new ScryEffect(1), StaticFilters.FILTER_PERMANENT_A_CREATURE )); // Whenever you cast a spell that targets a creature you control, draw a card. diff --git a/Mage.Sets/src/mage/cards/s/SeasonOfRenewal.java b/Mage.Sets/src/mage/cards/s/SeasonOfRenewal.java new file mode 100644 index 00000000000..fc58cbc6177 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SeasonOfRenewal.java @@ -0,0 +1,47 @@ +package mage.cards.s; + +import mage.abilities.Mode; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.common.FilterEnchantmentCard; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SeasonOfRenewal extends CardImpl { + + private static final FilterCard filter = new FilterEnchantmentCard("enchantment card from your graveyard"); + + public SeasonOfRenewal(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{G}"); + + // Choose one or both - + this.getSpellAbility().getModes().setMinModes(1); + this.getSpellAbility().getModes().setMaxModes(2); + + // • Return target creature card from your graveyard to your hand. + this.getSpellAbility().addEffect(new ReturnFromGraveyardToHandTargetEffect()); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); + + // • Return target enchantment card from your graveyard to your hand. + Mode mode = new Mode(new ReturnFromGraveyardToHandTargetEffect()); + mode.addTarget(new TargetCardInYourGraveyard(filter)); + this.getSpellAbility().addMode(mode); + } + + private SeasonOfRenewal(final SeasonOfRenewal card) { + super(card); + } + + @Override + public SeasonOfRenewal copy() { + return new SeasonOfRenewal(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SeasonedCathar.java b/Mage.Sets/src/mage/cards/s/SeasonedCathar.java index 85d9b7e5876..57397897f3d 100644 --- a/Mage.Sets/src/mage/cards/s/SeasonedCathar.java +++ b/Mage.Sets/src/mage/cards/s/SeasonedCathar.java @@ -22,7 +22,6 @@ public final class SeasonedCathar extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(3); this.color.setWhite(true); - this.transformable = true; this.nightCard = true; // Lifelink diff --git a/Mage.Sets/src/mage/cards/s/SecludedCourtyard.java b/Mage.Sets/src/mage/cards/s/SecludedCourtyard.java new file mode 100644 index 00000000000..ad47df46e9c --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SecludedCourtyard.java @@ -0,0 +1,118 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.cards.s; + +import java.util.UUID; +import mage.ConditionalMana; +import mage.MageObject; +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.condition.Condition; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.ChooseCreatureTypeEffect; +import mage.abilities.mana.ColorlessManaAbility; +import mage.abilities.mana.ConditionalAnyColorManaAbility; +import mage.abilities.mana.builder.ConditionalManaBuilder; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.game.Game; +import mage.players.Player; + +/** + * @author jeffwadsworth + */ +public final class SecludedCourtyard extends CardImpl { + + public SecludedCourtyard(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // As Secluded Courtyard enters the battlefield, choose a creature type. + this.addAbility(new AsEntersBattlefieldAbility(new ChooseCreatureTypeEffect(Outcome.Benefit))); + + // // {T}: Add {C}. + this.addAbility(new ColorlessManaAbility()); + + // Add one mana of any color. Spend this mana only to cast a creature spell of the chosen type or activate an ability of a creature or creature card of the chosen type. + this.addAbility(new ConditionalAnyColorManaAbility(new TapSourceCost(), 1, new SecludedCourtyardManaBuilder(), true)); + } + + private SecludedCourtyard(final SecludedCourtyard card) { + super(card); + } + + @Override + public SecludedCourtyard copy() { + return new SecludedCourtyard(this); + } +} + +class SecludedCourtyardManaBuilder extends ConditionalManaBuilder { + + SubType creatureType; + + @Override + public ConditionalManaBuilder setMana(Mana mana, Ability source, Game game) { + SubType subType = ChooseCreatureTypeEffect.getChosenCreatureType(source.getSourceId(), game); + if (subType != null) { + creatureType = subType; + } + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = game.getObject(source.getSourceId()); + if (controller != null + && sourceObject != null + && mana.getAny() == 0) { + game.informPlayers(controller.getLogName() + " produces " + mana.toString() + " with " + sourceObject.getLogName() + + " (can only be spent to cast creatures of type " + creatureType + " and activate an ability of a creature or creature card of the chosen type)"); + } + return super.setMana(mana, source, game); + } + + @Override + public ConditionalMana build(Object... options) { + return new SecludedCourtyardConditionalMana(this.mana, creatureType); + } + + @Override + public String getRule() { + return "Spend this mana only to cast a creature spell of the chosen type or activate an ability of a creature or creature card of the chosen type"; + } +} + +class SecludedCourtyardConditionalMana extends ConditionalMana { + + SubType creatureType; + + public SecludedCourtyardConditionalMana(Mana mana, SubType creatureType) { + super(mana); + staticText = "Spend this mana only to cast a creature spell of the chosen type or activate an ability of a creature or creature card of the chosen type"; + addCondition(new SecludedCourtyardManaCondition(creatureType)); + } +} + +class SecludedCourtyardManaCondition implements Condition { + + SubType creatureType; + + SecludedCourtyardManaCondition(SubType creatureType) { + this.creatureType = creatureType; + } + + @Override + public boolean apply(Game game, Ability source) { + MageObject object = game.getObject(source.getSourceId()); + // casting a creature card or using its ability + if (creatureType != null + && object != null + && object.hasSubtype(creatureType, game)) { + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SecretSalvage.java b/Mage.Sets/src/mage/cards/s/SecretSalvage.java index b348f065587..21c94daad82 100644 --- a/Mage.Sets/src/mage/cards/s/SecretSalvage.java +++ b/Mage.Sets/src/mage/cards/s/SecretSalvage.java @@ -47,7 +47,7 @@ class SecretSalvageEffect extends OneShotEffect { public SecretSalvageEffect() { super(Outcome.DrawCard); staticText = "Exile target nonland card from your graveyard. Search your library for any number of cards with the same name as that card, " - + "reveal them, and put them into your hand. Then shuffle"; + + "reveal them, put them into your hand, then shuffle"; } public SecretSalvageEffect(final SecretSalvageEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/SedgemoorWitch.java b/Mage.Sets/src/mage/cards/s/SedgemoorWitch.java index ad6c0172011..51a94cb422e 100644 --- a/Mage.Sets/src/mage/cards/s/SedgemoorWitch.java +++ b/Mage.Sets/src/mage/cards/s/SedgemoorWitch.java @@ -28,7 +28,7 @@ public final class SedgemoorWitch extends CardImpl { this.toughness = new MageInt(2); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // Ward—Pay 3 life. this.addAbility(new WardAbility(new PayLifeCost(3))); diff --git a/Mage.Sets/src/mage/cards/s/SeeRed.java b/Mage.Sets/src/mage/cards/s/SeeRed.java index 49e4f259f2b..1670a99ed80 100644 --- a/Mage.Sets/src/mage/cards/s/SeeRed.java +++ b/Mage.Sets/src/mage/cards/s/SeeRed.java @@ -55,7 +55,7 @@ public final class SeeRed extends CardImpl { // At the beginning of your end step, if you didn't attack with a creature this turn, sacrifice See Red. this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new AtTheBeginOfNextEndStepDelayedTriggeredAbility(Zone.BATTLEFIELD, new SacrificeSourceEffect(), TargetController.YOU), + new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new SacrificeSourceEffect(), TargetController.YOU), new InvertCondition(ControllerAttackedThisTurnCondition.instance), "At the beginning of your end step, if you didn't attack with a creature this turn, sacrifice {this}."), new AttackedThisTurnWatcher()); } diff --git a/Mage.Sets/src/mage/cards/s/SefrisOfTheHiddenWays.java b/Mage.Sets/src/mage/cards/s/SefrisOfTheHiddenWays.java index 6ddb6ca649c..eeb7310b841 100644 --- a/Mage.Sets/src/mage/cards/s/SefrisOfTheHiddenWays.java +++ b/Mage.Sets/src/mage/cards/s/SefrisOfTheHiddenWays.java @@ -1,9 +1,9 @@ package mage.cards.s; +import java.util.Set; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.CompletedDungeonTriggeredAbility; -import mage.abilities.common.PutCardIntoGraveFromAnywhereAllTriggeredAbility; import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; import mage.abilities.effects.keyword.VentureIntoTheDungeonEffect; import mage.cards.CardImpl; @@ -11,21 +11,24 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; -import mage.constants.TargetController; -import mage.filter.FilterCard; import mage.filter.StaticFilters; -import mage.filter.common.FilterCreatureCard; import mage.target.common.TargetCardInYourGraveyard; import java.util.UUID; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.cards.Card; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeGroupEvent; +import mage.game.permanent.Permanent; /** * @author TheElk801 */ public final class SefrisOfTheHiddenWays extends CardImpl { - private static final FilterCard filter = new FilterCreatureCard("one or more creature cards"); - public SefrisOfTheHiddenWays(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}{U}{B}"); @@ -36,8 +39,8 @@ public final class SefrisOfTheHiddenWays extends CardImpl { this.toughness = new MageInt(3); // Whenever one or more creature cards are put into your graveyard from anywhere, venture into the dungeon. This ability triggers only once each turn. - this.addAbility(new PutCardIntoGraveFromAnywhereAllTriggeredAbility( - new VentureIntoTheDungeonEffect(), false, filter, TargetController.YOU + this.addAbility(new SefrisOfTheHiddenWaysTriggeredAbility( + new VentureIntoTheDungeonEffect().setText("") ).setTriggersOnce(true)); // Create Undead — Whenever you complete a dungeon, return target creature card from your graveyard to the battlefield. @@ -55,3 +58,55 @@ public final class SefrisOfTheHiddenWays extends CardImpl { return new SefrisOfTheHiddenWays(this); } } + +class SefrisOfTheHiddenWaysTriggeredAbility extends TriggeredAbilityImpl { + + public SefrisOfTheHiddenWaysTriggeredAbility(Effect effect) { + super(Zone.ALL, effect, false); + } + + public SefrisOfTheHiddenWaysTriggeredAbility(final SefrisOfTheHiddenWaysTriggeredAbility ability) { + super(ability); + } + + @Override + public SefrisOfTheHiddenWaysTriggeredAbility copy() { + return new SefrisOfTheHiddenWaysTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE_GROUP; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Boolean applies = false; + /* + Sefris of the Hidden Ways must be on the battlefield for its first ability to trigger. + It does not trigger when Sefris goes to the graveyard from the battlefield, even if other + creature cards also went to the graveyard at the same time. + */ + Permanent sourceCard = game.getPermanent(sourceId); + if (((ZoneChangeGroupEvent) event).getToZone() != Zone.GRAVEYARD + || sourceCard == null) { + return false; + } + ZoneChangeGroupEvent zEvent = (ZoneChangeGroupEvent) event; + Set cards = zEvent.getCards(); + for (Card card : cards) { + if (card.isCreature(game) + && (Card) sourceCard != card // 603.6c, 603.10a, and 603.10. + && !card.isCopy() + && card.isOwnedBy(controllerId)) { + applies = true; + } + } + return applies; + } + + @Override + public String getTriggerPhrase() { + return "Whenever one or more creature cards are put into your graveyard from anywhere, venture into the dungeon."; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SeismicWave.java b/Mage.Sets/src/mage/cards/s/SeismicWave.java new file mode 100644 index 00000000000..ebc6f6a8e18 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SeismicWave.java @@ -0,0 +1,95 @@ +package mage.cards.s; + +import java.util.UUID; + +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.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.Target; +import mage.target.Targets; +import mage.target.common.TargetAnyTarget; +import mage.target.common.TargetOpponent; + +/** + * + * @author weirddan455 + */ +public final class SeismicWave extends CardImpl { + + public SeismicWave(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{R}"); + + // Seismic Wave deals 2 damage to any target and 1 damage to each nonartifact creature target opponent controls. + this.getSpellAbility().addTarget(new TargetAnyTarget().withChooseHint("2 damage")); + this.getSpellAbility().addTarget(new TargetOpponent().withChooseHint("1 damage to each nonartifact creature target opponent controls")); + this.getSpellAbility().addEffect(new SeismicWaveEffect()); + } + + private SeismicWave(final SeismicWave card) { + super(card); + } + + @Override + public SeismicWave copy() { + return new SeismicWave(this); + } +} + +class SeismicWaveEffect extends OneShotEffect { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); + + static { + filter.add(Predicates.not(CardType.ARTIFACT.getPredicate())); + } + + public SeismicWaveEffect() { + super(Outcome.Damage); + this.staticText = "{this} deals 2 damage to any target and 1 damage to each nonartifact creature target opponent controls"; + } + + private SeismicWaveEffect(final SeismicWaveEffect effect) { + super(effect); + } + + @Override + public SeismicWaveEffect copy() { + return new SeismicWaveEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Targets targets = source.getTargets(); + if (targets.size() < 2) { + return false; + } + UUID firstTarget = targets.get(0).getFirstTarget(); + if (firstTarget != null) { + Permanent targetPermanent = game.getPermanent(firstTarget); + if (targetPermanent != null) { + targetPermanent.damage(2, source, game); + } else { + Player targetPlayer = game.getPlayer(firstTarget); + if (targetPlayer != null) { + targetPlayer.damage(2, source, game); + } + } + } + Target targetOpponent = targets.get(1); + UUID opponentId = targetOpponent.getFirstTarget(); + if (opponentId != null && targetOpponent.isLegal(source, game)) { // Needs this check in case opponent gets hexproof at instant speed + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filter, opponentId, game)) { + permanent.damage(1, source, game); + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SeizeTheStorm.java b/Mage.Sets/src/mage/cards/s/SeizeTheStorm.java index 45f52e91d3f..eef63c29577 100644 --- a/Mage.Sets/src/mage/cards/s/SeizeTheStorm.java +++ b/Mage.Sets/src/mage/cards/s/SeizeTheStorm.java @@ -13,7 +13,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.filter.StaticFilters; import mage.game.Game; -import mage.game.permanent.token.SeizeTheStormToken; +import mage.game.permanent.token.SeizeTheStormElementalToken; import mage.players.Player; import java.util.UUID; @@ -32,7 +32,7 @@ public final class SeizeTheStorm extends CardImpl { // Create a red Elemental creature token with trample and "This creature's power and toughness are each equal to the number of instant and sorcery cards in your graveyard, plus the number of cards with flashback you own in exile." this.getSpellAbility().addEffect(new CreateTokenEffect( - new SeizeTheStormToken(SeizeTheStormValue.instance, hint) + new SeizeTheStormElementalToken(SeizeTheStormValue.instance, hint) )); this.getSpellAbility().addHint(hint); diff --git a/Mage.Sets/src/mage/cards/s/SelfAssembler.java b/Mage.Sets/src/mage/cards/s/SelfAssembler.java index f6b61704083..a2f0ab77514 100644 --- a/Mage.Sets/src/mage/cards/s/SelfAssembler.java +++ b/Mage.Sets/src/mage/cards/s/SelfAssembler.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.Effect; @@ -11,22 +9,24 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.filter.FilterCard; +import mage.filter.common.FilterCreatureCard; import mage.target.common.TargetCardInLibrary; +import java.util.UUID; + /** - * * @author fireshoes */ public final class SelfAssembler extends CardImpl { - private static final FilterCard filter = new FilterCard("an Assembly-Worker card"); + private static final FilterCard filter = new FilterCreatureCard("an Assembly-Worker creature card"); static { filter.add(SubType.ASSEMBLY_WORKER.getPredicate()); } public SelfAssembler(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{5}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{5}"); this.subtype.add(SubType.ASSEMBLY_WORKER); this.power = new MageInt(4); this.toughness = new MageInt(4); @@ -34,7 +34,7 @@ public final class SelfAssembler extends CardImpl { // When Self-Assembler enters the battlefield, you may search your library for an Assembly-Worker creature card, reveal it, put it into your hand, // then shuffle your library. Effect effect = new SearchLibraryPutInHandEffect(new TargetCardInLibrary(0, 1, filter), true, true); - effect.setText("you may search your library for an Assembly-Worker card, reveal it, put it into your hand, then shuffle"); + effect.setText("you may search your library for an Assembly-Worker creature card, reveal it, put it into your hand, then shuffle"); this.addAbility(new EntersBattlefieldTriggeredAbility(effect, true)); } diff --git a/Mage.Sets/src/mage/cards/s/SelflessSamurai.java b/Mage.Sets/src/mage/cards/s/SelflessSamurai.java new file mode 100644 index 00000000000..af1921c859a --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SelflessSamurai.java @@ -0,0 +1,67 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksAloneControlledTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.IndestructibleAbility; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SelflessSamurai extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("another target creature you control"); + + static { + filter.add(AnotherPredicate.instance); + } + + public SelflessSamurai(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.subtype.add(SubType.FOX); + this.subtype.add(SubType.SAMURAI); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Whenever a Samurai or Warrior you control attacks alone, it gains lifelink until end of turn. + this.addAbility(new AttacksAloneControlledTriggeredAbility( + new GainAbilityTargetEffect(LifelinkAbility.getInstance()) + .setText("it gains lifelink until end of turn"), + StaticFilters.FILTER_CONTROLLED_SAMURAI_OR_WARRIOR, true, false + )); + + // Sacrifice Selfless Samurai: Another target creature you control gains indestructible until end of turn. + Ability ability = new SimpleActivatedAbility(new GainAbilityTargetEffect( + IndestructibleAbility.getInstance(), Duration.EndOfTurn + ), new SacrificeSourceCost()); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + } + + private SelflessSamurai(final SelflessSamurai card) { + super(card); + } + + @Override + public SelflessSamurai copy() { + return new SelflessSamurai(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SelflessSpirit.java b/Mage.Sets/src/mage/cards/s/SelflessSpirit.java index 8d41f38ddf5..98a3f3d5ba7 100644 --- a/Mage.Sets/src/mage/cards/s/SelflessSpirit.java +++ b/Mage.Sets/src/mage/cards/s/SelflessSpirit.java @@ -14,7 +14,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Zone; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; /** * @@ -34,7 +34,7 @@ public final class SelflessSpirit extends CardImpl { // Sacrifice Selfless Spirit: Creatures you control gain indestructible until end of turn. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(IndestructibleAbility.getInstance(), Duration.EndOfTurn, - new FilterControlledCreaturePermanent("creatures you control")), new SacrificeSourceCost())); + StaticFilters.FILTER_CONTROLLED_CREATURES), new SacrificeSourceCost())); } private SelflessSpirit(final SelflessSpirit card) { diff --git a/Mage.Sets/src/mage/cards/s/SelhoffEntomber.java b/Mage.Sets/src/mage/cards/s/SelhoffEntomber.java new file mode 100644 index 00000000000..2f385a0c250 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SelhoffEntomber.java @@ -0,0 +1,43 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.costs.common.TapSourceCost; +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 SelhoffEntomber extends CardImpl { + + public SelhoffEntomber(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); + + this.subtype.add(SubType.ZOMBIE); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // {T}, Discard a creature card: Draw a card. + Ability ability = new SimpleActivatedAbility(new DrawCardSourceControllerEffect(1), new TapSourceCost()); + ability.addCost(new DiscardCardCost(StaticFilters.FILTER_CARD_CREATURE_A)); + this.addAbility(ability); + } + + private SelhoffEntomber(final SelhoffEntomber card) { + super(card); + } + + @Override + public SelhoffEntomber copy() { + return new SelhoffEntomber(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SenateGriffin.java b/Mage.Sets/src/mage/cards/s/SenateGriffin.java index d85c995f774..de0fa33c1f1 100644 --- a/Mage.Sets/src/mage/cards/s/SenateGriffin.java +++ b/Mage.Sets/src/mage/cards/s/SenateGriffin.java @@ -27,7 +27,7 @@ public final class SenateGriffin extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Senate Griffin enters the battlefield, scry 1. - this.addAbility(new EntersBattlefieldTriggeredAbility(new ScryEffect(1))); + this.addAbility(new EntersBattlefieldTriggeredAbility(new ScryEffect(1, false))); } private SenateGriffin(final SenateGriffin card) { diff --git a/Mage.Sets/src/mage/cards/s/SengirTheDarkBaron.java b/Mage.Sets/src/mage/cards/s/SengirTheDarkBaron.java index 45899e1314d..033e11fb41e 100644 --- a/Mage.Sets/src/mage/cards/s/SengirTheDarkBaron.java +++ b/Mage.Sets/src/mage/cards/s/SengirTheDarkBaron.java @@ -107,7 +107,7 @@ class SengirTheDarkBaronWatcher extends Watcher { @Override public void watch(GameEvent event, Game game) { - if (event.getType() != GameEvent.EventType.BEGINNING_PHASE_PRE) { + if (event.getType() == GameEvent.EventType.BEGINNING_PHASE_PRE) { game.getPlayers() .values() .stream() diff --git a/Mage.Sets/src/mage/cards/s/SentinelSliver.java b/Mage.Sets/src/mage/cards/s/SentinelSliver.java index e6dfac198ea..708dd9fddff 100644 --- a/Mage.Sets/src/mage/cards/s/SentinelSliver.java +++ b/Mage.Sets/src/mage/cards/s/SentinelSliver.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; @@ -11,24 +9,27 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * * @author Plopman */ public final class SentinelSliver extends CardImpl { public SentinelSliver(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); this.subtype.add(SubType.SLIVER); this.power = new MageInt(2); this.toughness = new MageInt(2); // Sliver creatures you control have vigilance. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledEffect(VigilanceAbility.getInstance(), Duration.WhileOnBattlefield, new FilterControlledCreaturePermanent(SubType.SLIVER, "Sliver creatures you control ")))); + this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( + VigilanceAbility.getInstance(), Duration.WhileOnBattlefield, + StaticFilters.FILTER_PERMANENT_SLIVERS + ))); } private SentinelSliver(final SentinelSliver card) { diff --git a/Mage.Sets/src/mage/cards/s/SereneMaster.java b/Mage.Sets/src/mage/cards/s/SereneMaster.java index dfec1752291..56629a3f2ec 100644 --- a/Mage.Sets/src/mage/cards/s/SereneMaster.java +++ b/Mage.Sets/src/mage/cards/s/SereneMaster.java @@ -1,4 +1,3 @@ - package mage.cards.s; import java.util.UUID; @@ -30,7 +29,7 @@ import mage.target.targetpointer.FixedTarget; public final class SereneMaster extends CardImpl { public SereneMaster(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.MONK); @@ -83,10 +82,10 @@ class SereneMasterEffect extends OneShotEffect { int newSourcePower = attackingCreature.getPower().getValue(); int newAttackerPower = sourceCreature.getPower().getValue(); ContinuousEffect effect = new SetPowerToughnessTargetEffect(newSourcePower, sourceCreature.getToughness().getValue(), Duration.EndOfCombat); - effect.setTargetPointer(new FixedTarget(source.getSourceId())); + effect.setTargetPointer(new FixedTarget(source.getSourceId(), game)); game.addEffect(effect, source); effect = new SetPowerToughnessTargetEffect(newAttackerPower, attackingCreature.getToughness().getValue(), Duration.EndOfCombat); - effect.setTargetPointer(new FixedTarget(attackingCreature.getId())); + effect.setTargetPointer(new FixedTarget(attackingCreature.getId(), game)); game.addEffect(effect, source); return true; } diff --git a/Mage.Sets/src/mage/cards/s/SereneSunset.java b/Mage.Sets/src/mage/cards/s/SereneSunset.java new file mode 100644 index 00000000000..157e5d9a9eb --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SereneSunset.java @@ -0,0 +1,47 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.effects.common.PreventDamageByTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.game.Game; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetadjustment.TargetAdjuster; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SereneSunset extends CardImpl { + + public SereneSunset(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{X}{G}"); + + // Prevent all combat damage X target creatures would deal this turn. + this.getSpellAbility().addEffect(new PreventDamageByTargetEffect(Duration.EndOfTurn, true) + .setText("prevent all combat damage X target creatures would deal this turn")); + this.getSpellAbility().setTargetAdjuster(SereneSunsetAdjuster.instance); + } + + private SereneSunset(final SereneSunset card) { + super(card); + } + + @Override + public SereneSunset copy() { + return new SereneSunset(this); + } +} + +enum SereneSunsetAdjuster implements TargetAdjuster { + instance; + + @Override + public void adjustTargets(Ability ability, Game game) { + ability.getTargets().clear(); + ability.addTarget(new TargetCreaturePermanent(ability.getManaCostsToPay().getX())); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SerpentAssassin.java b/Mage.Sets/src/mage/cards/s/SerpentAssassin.java index 740510ff80c..18d32d8794f 100644 --- a/Mage.Sets/src/mage/cards/s/SerpentAssassin.java +++ b/Mage.Sets/src/mage/cards/s/SerpentAssassin.java @@ -1,9 +1,7 @@ - package mage.cards.s; import java.util.UUID; import mage.MageInt; -import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.common.DestroyTargetEffect; @@ -11,9 +9,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -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; /** @@ -21,12 +17,6 @@ import mage.target.common.TargetCreaturePermanent; * @author fireshoes */ public final class SerpentAssassin extends CardImpl { - - static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } public SerpentAssassin(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}{B}"); @@ -37,7 +27,7 @@ public final class SerpentAssassin extends CardImpl { // When Serpent Assassin enters the battlefield, you may destroy target nonblack creature. Ability ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect(), true); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SerpentineAmbush.java b/Mage.Sets/src/mage/cards/s/SerpentineAmbush.java new file mode 100644 index 00000000000..77c4d9c2a8b --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SerpentineAmbush.java @@ -0,0 +1,95 @@ +package mage.cards.s; + +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SerpentineAmbush extends CardImpl { + + public SerpentineAmbush(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}"); + + // Until end of turn, target creature becomes a blue Serpent with base power and toughness 5/5. + this.getSpellAbility().addEffect(new SerpentineAmbushEffect()); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + private SerpentineAmbush(final SerpentineAmbush card) { + super(card); + } + + @Override + public SerpentineAmbush copy() { + return new SerpentineAmbush(this); + } +} + +class SerpentineAmbushEffect extends ContinuousEffectImpl { + + SerpentineAmbushEffect() { + super(Duration.EndOfTurn, Outcome.Benefit); + staticText = "until end of turn, target creature becomes " + + "a blue Serpent with base power and toughness 5/5"; + } + + private SerpentineAmbushEffect(final SerpentineAmbushEffect effect) { + super(effect); + } + + @Override + public SerpentineAmbushEffect copy() { + return new SerpentineAmbushEffect(this); + } + + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (permanent == null) { + discard(); + return false; + } + switch (layer) { + case TypeChangingEffects_4: + permanent.removeAllCreatureTypes(game); + permanent.addSubType(game, SubType.SERPENT); + return true; + case ColorChangingEffects_5: + permanent.getColor(game).setColor(ObjectColor.BLUE); + return true; + case PTChangingEffects_7: + if (sublayer == SubLayer.SetPT_7b) { + permanent.getPower().setValue(5); + permanent.getToughness().setValue(5); + return true; + } + } + return false; + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean hasLayer(Layer layer) { + switch (layer) { + case TypeChangingEffects_4: + case ColorChangingEffects_5: + case PTChangingEffects_7: + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SerpentsSoulJar.java b/Mage.Sets/src/mage/cards/s/SerpentsSoulJar.java index e5a3f7c327f..0210def2ebb 100644 --- a/Mage.Sets/src/mage/cards/s/SerpentsSoulJar.java +++ b/Mage.Sets/src/mage/cards/s/SerpentsSoulJar.java @@ -14,13 +14,17 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; +import mage.game.ExileZone; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; +import mage.util.CardUtil; import mage.watchers.Watcher; -import java.util.*; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; /** * @author TheElk801 @@ -34,8 +38,8 @@ public final class SerpentsSoulJar extends CardImpl { // Whenever an Elf you control dies, exile it. this.addAbility(new DiesCreatureTriggeredAbility( - new SerpentsSoulJarExileEffect(), false, filter, true) - ); + new SerpentsSoulJarExileEffect(), false, filter, true + )); // {T}, Pay 2 life: Until end of turn, you may cast a creature spell from among cards exiled with Serpent's Soul-Jar. Ability ability = new SimpleActivatedAbility(new SerpentsSoulJarCastFromExileEffect(), new TapSourceCost()); @@ -77,13 +81,11 @@ class SerpentsSoulJarExileEffect extends OneShotEffect { if (player == null || permanent == null || card == null) { return false; } - MageObjectReference mor = new MageObjectReference(permanent, game); - player.moveCards(card, Zone.EXILED, source, game); - String exileId = "serpentsSoulJar_" + mor.getSourceId() + mor.getZoneChangeCounter(); - if (game.getState().getValue(exileId) == null) { - game.getState().setValue(exileId, new HashSet()); - } - ((Set) game.getState().getValue(exileId)).add(new MageObjectReference(card, game)); + player.moveCardsToExile( + card, source, game, true, + CardUtil.getExileZoneId(game, source), + CardUtil.getSourceName(game, source) + ); return true; } } @@ -112,28 +114,16 @@ class SerpentsSoulJarCastFromExileEffect extends AsThoughEffectImpl { @Override public void init(Ability source, Game game) { super.init(source, game); - SerpentsSoulJarWatcher watcher = game.getState().getWatcher(SerpentsSoulJarWatcher.class); - if (watcher != null) { - watcher.addPlayable(source, game); - } + SerpentsSoulJarWatcher.addPlayable(source, game); } @Override public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { - SerpentsSoulJarWatcher watcher = game.getState().getWatcher(SerpentsSoulJarWatcher.class); - if (watcher == null || !watcher.checkPermission(affectedControllerId, source, game)) { + if (!SerpentsSoulJarWatcher.checkPermission(affectedControllerId, source, game)) { return false; } - Object value = game.getState().getValue( - "serpentsSoulJar_" + source.getSourceId() + source.getSourceObjectZoneChangeCounter() - ); - if (!(value instanceof Set)) { - discard(); - return false; - } - Set morSet = (Set) value; - if (game.getState().getZone(sourceId) != Zone.EXILED - || morSet.stream().noneMatch(mor -> mor.refersTo(sourceId, game))) { + ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source)); + if (exileZone == null || !exileZone.contains(sourceId)) { return false; } Card card = game.getCard(sourceId); @@ -167,24 +157,27 @@ class SerpentsSoulJarWatcher extends Watcher { super.reset(); } - boolean checkPermission(UUID playerId, Ability source, Game game) { + static boolean checkPermission(UUID playerId, Ability source, Game game) { if (!playerId.equals(source.getControllerId())) { return false; } - MageObjectReference mor = new MageObjectReference( - source.getSourceId(), source.getSourceObjectZoneChangeCounter(), game - ); - if (!morMap.containsKey(mor)) { - return false; - } - return morMap.get(mor).getOrDefault(playerId, 0) > 0; + MageObjectReference mor = new MageObjectReference(source); + SerpentsSoulJarWatcher watcher = game.getState().getWatcher(SerpentsSoulJarWatcher.class); + return watcher + .morMap + .containsKey(mor) + && watcher + .morMap + .get(mor) + .getOrDefault(playerId, 0) > 0; } - void addPlayable(Ability source, Game game) { - MageObjectReference mor = new MageObjectReference( - source.getSourceId(), source.getSourceObjectZoneChangeCounter(), game - ); - morMap.computeIfAbsent(mor, m -> new HashMap<>()) - .compute(source.getControllerId(), (u, i) -> i == null ? 1 : Integer.sum(i, 1)); + static void addPlayable(Ability source, Game game) { + MageObjectReference mor = new MageObjectReference(source); + game.getState() + .getWatcher(SerpentsSoulJarWatcher.class) + .morMap + .computeIfAbsent(mor, m -> new HashMap<>()) + .compute(source.getControllerId(), CardUtil::setOrIncrementValue); } } diff --git a/Mage.Sets/src/mage/cards/s/SerraTheBenevolent.java b/Mage.Sets/src/mage/cards/s/SerraTheBenevolent.java index bcffe715a31..38c30822e0e 100644 --- a/Mage.Sets/src/mage/cards/s/SerraTheBenevolent.java +++ b/Mage.Sets/src/mage/cards/s/SerraTheBenevolent.java @@ -1,7 +1,6 @@ package mage.cards.s; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.GetEmblemEffect; import mage.abilities.effects.common.continuous.BoostControlledEffect; @@ -36,7 +35,7 @@ public final class SerraTheBenevolent extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.SERRA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +2: Creatures you control with flying get +1/+1 until end of turn. this.addAbility(new LoyaltyAbility(new BoostControlledEffect(1, 1, Duration.EndOfTurn, filter) diff --git a/Mage.Sets/src/mage/cards/s/SeshirosLivingLegacy.java b/Mage.Sets/src/mage/cards/s/SeshirosLivingLegacy.java new file mode 100644 index 00000000000..61ed396df36 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SeshirosLivingLegacy.java @@ -0,0 +1,43 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SeshirosLivingLegacy extends CardImpl { + + public SeshirosLivingLegacy(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, ""); + + this.subtype.add(SubType.SNAKE); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + this.color.setGreen(true); + this.nightCard = true; + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // Haste + this.addAbility(HasteAbility.getInstance()); + } + + private SeshirosLivingLegacy(final SeshirosLivingLegacy card) { + super(card); + } + + @Override + public SeshirosLivingLegacy copy() { + return new SeshirosLivingLegacy(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SevenTailMentor.java b/Mage.Sets/src/mage/cards/s/SevenTailMentor.java new file mode 100644 index 00000000000..36bddfaa18e --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SevenTailMentor.java @@ -0,0 +1,57 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldOrDiesSourceTriggeredAbility; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SevenTailMentor extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent("creature or Vehicle you control"); + + static { + filter.add(Predicates.or( + CardType.CREATURE.getPredicate(), + SubType.VEHICLE.getPredicate() + )); + } + + public SevenTailMentor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); + + this.subtype.add(SubType.FOX); + this.subtype.add(SubType.SAMURAI); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // When Seven-Tail Mentor enters the battlefield or dies, put a +1/+1 counter on target creature or Vehicle you control. + Ability ability = new EntersBattlefieldOrDiesSourceTriggeredAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance()), false + ); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + } + + private SevenTailMentor(final SevenTailMentor card) { + super(card); + } + + @Override + public SevenTailMentor copy() { + return new SevenTailMentor(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SeverSoul.java b/Mage.Sets/src/mage/cards/s/SeverSoul.java index 5e7f49fa947..4958df11b40 100644 --- a/Mage.Sets/src/mage/cards/s/SeverSoul.java +++ b/Mage.Sets/src/mage/cards/s/SeverSoul.java @@ -1,8 +1,6 @@ - package mage.cards.s; import java.util.UUID; -import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DestroyTargetEffect; @@ -10,9 +8,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ColorPredicate; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -24,18 +20,11 @@ import mage.target.common.TargetCreaturePermanent; */ public final class SeverSoul extends CardImpl { - private static final FilterCreaturePermanent nonBlackCreature = new FilterCreaturePermanent("nonblack creature"); - - static { - nonBlackCreature.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public SeverSoul(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{B}{B}"); - // Destroy target nonblack creature. It can't be regenerated. - this.getSpellAbility().addTarget(new TargetCreaturePermanent(nonBlackCreature)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); this.getSpellAbility().addEffect(new DestroyTargetEffect(true)); // You gain life equal to its toughness. this.getSpellAbility().addEffect(new GainLifeEqualToToughnessEffect()); diff --git a/Mage.Sets/src/mage/cards/s/ShadowAlleyDenizen.java b/Mage.Sets/src/mage/cards/s/ShadowAlleyDenizen.java index bd6ac80cb2a..769ae1b0c86 100644 --- a/Mage.Sets/src/mage/cards/s/ShadowAlleyDenizen.java +++ b/Mage.Sets/src/mage/cards/s/ShadowAlleyDenizen.java @@ -1,4 +1,3 @@ - package mage.cards.s; import java.util.UUID; @@ -6,6 +5,7 @@ import mage.MageInt; import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; +import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.keyword.IntimidateAbility; import mage.cards.CardImpl; @@ -28,8 +28,8 @@ public final class ShadowAlleyDenizen extends CardImpl { private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("another black creature"); static { - filter.add(new ColorPredicate(ObjectColor.BLACK)); filter.add(AnotherPredicate.instance); + filter.add(new ColorPredicate(ObjectColor.BLACK)); } public ShadowAlleyDenizen(UUID ownerId, CardSetInfo setInfo) { @@ -40,12 +40,10 @@ public final class ShadowAlleyDenizen extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); - // Whenever another black creature enters the battlefield under your control, target creature gains intimidate until end of turn. - Ability ability = new EntersBattlefieldAllTriggeredAbility( - Zone.BATTLEFIELD, - new GainAbilityTargetEffect(IntimidateAbility.getInstance(), Duration.EndOfTurn), - filter, false, null, true); + Effect effect = new GainAbilityTargetEffect(IntimidateAbility.getInstance(), Duration.EndOfTurn); + effect.setText("target creature gains intimidate until end of turn. (It can't be blocked except by artifact creatures and/or creatures that share a color with it.)"); + Ability ability = new EntersBattlefieldAllTriggeredAbility(Zone.BATTLEFIELD, effect, filter, false, null, true); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/ShadowOfDoubt.java b/Mage.Sets/src/mage/cards/s/ShadowOfDoubt.java index 19c3cea0f09..75a2a3f8641 100644 --- a/Mage.Sets/src/mage/cards/s/ShadowOfDoubt.java +++ b/Mage.Sets/src/mage/cards/s/ShadowOfDoubt.java @@ -27,7 +27,7 @@ public final class ShadowOfDoubt extends CardImpl { // Players can't search libraries this turn. this.getSpellAbility().addEffect(new LibrariesCantBeSearchedEffect()); // Draw a card. - this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("
")); } private ShadowOfDoubt(final ShadowOfDoubt card) { diff --git a/Mage.Sets/src/mage/cards/s/ShadowgrangeArchfiend.java b/Mage.Sets/src/mage/cards/s/ShadowgrangeArchfiend.java new file mode 100644 index 00000000000..301c2283035 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/ShadowgrangeArchfiend.java @@ -0,0 +1,138 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.costs.common.PayLifeCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.keyword.MadnessAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.mageobject.PowerPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.Target; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + * @author Alex-Vasile + */ +public final class ShadowgrangeArchfiend extends CardImpl { + public ShadowgrangeArchfiend(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{6}{B}"); + this.subtype.add(SubType.DEMON); + + this.power = new MageInt(8); + this.toughness = new MageInt(4); + + // When Shadowgrange Archfiend enters the battlefield, + // each opponent sacrifices a creature with the greatest power among creatures they control. + // You gain life equal to the greatest power among creatures sacrificed this way. + this.addAbility(new EntersBattlefieldTriggeredAbility(new ShadowgrangeArchfiendEffect())); + + // Madness—{2}{B}, Pay 8 life. + MadnessAbility madnessAbility = new MadnessAbility(this, new ManaCostsImpl<>("{2}{B}"), 8); + this.addAbility(madnessAbility); + } + + private ShadowgrangeArchfiend(final ShadowgrangeArchfiend card) { super(card); } + + @Override + public ShadowgrangeArchfiend copy() { return new ShadowgrangeArchfiend(this); } +} + +class ShadowgrangeArchfiendEffect extends OneShotEffect { + + public ShadowgrangeArchfiendEffect() { + super(Outcome.Benefit); + this.staticText = "each opponent sacrifices a creature with the greatest power among creatures they control. " + + "You gain life equal to the greatest power among creatures sacrificed this way"; + } + + private ShadowgrangeArchfiendEffect(final ShadowgrangeArchfiendEffect effect) { super(effect); } + + @Override + public ShadowgrangeArchfiendEffect copy() { return new ShadowgrangeArchfiendEffect(this); } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) {return false; } + + List toSacrifice = new ArrayList<>(); + + // Iterate through each opponent + for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { + if (!controller.hasOpponent(playerId, game)) { continue; } + + Player opponent = game.getPlayer(playerId); + if (opponent == null) { continue; } + + int greatestPower = Integer.MIN_VALUE; + int numberOfCreatures = 0; + Permanent creatureToSacrifice = null; + + // Iterature through each creature + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, playerId, game)) { + if (permanent.getPower().getValue() > greatestPower) { + greatestPower = permanent.getPower().getValue(); + numberOfCreatures = 1; + creatureToSacrifice = permanent; + } else if (permanent.getPower().getValue() == greatestPower) { + numberOfCreatures++; + } + } + + // If multiple creatures are tied for having the greatest power + if (numberOfCreatures > 1) { + FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent( + "creature to sacrifice with power equal to " + greatestPower); + filter.add(new PowerPredicate(ComparisonType.EQUAL_TO, greatestPower)); + Target target = new TargetControlledCreaturePermanent(filter); + if (opponent.choose(outcome, target, playerId, game)) { + creatureToSacrifice = game.getPermanent(target.getFirstTarget()); + } + } + + if (creatureToSacrifice != null) { + toSacrifice.add(creatureToSacrifice); + } + } + + int greatestPowerAmongAllCreaturesSacked = Integer.MIN_VALUE; + int powerOfCurrentCreature; + + // Sack the creatures and save the greaterest power amoung those which were sacked + for (Permanent permanent : toSacrifice) { + powerOfCurrentCreature = permanent.getPower().getValue(); + + // Try to sack it + if (permanent.sacrifice(source, game)) { + if (powerOfCurrentCreature > greatestPowerAmongAllCreaturesSacked) { + greatestPowerAmongAllCreaturesSacked = powerOfCurrentCreature; + } + } + } + + // Gain life equal to the power of greatest creature sacked, if it is positive + if (greatestPowerAmongAllCreaturesSacked > 0) { + new GainLifeEffect(greatestPowerAmongAllCreaturesSacked).apply(game, source); + } + + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/s/ShadyTraveler.java b/Mage.Sets/src/mage/cards/s/ShadyTraveler.java index f2424f30024..a7c56fa4541 100644 --- a/Mage.Sets/src/mage/cards/s/ShadyTraveler.java +++ b/Mage.Sets/src/mage/cards/s/ShadyTraveler.java @@ -3,7 +3,6 @@ package mage.cards.s; import mage.MageInt; import mage.abilities.keyword.DayboundAbility; import mage.abilities.keyword.MenaceAbility; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -23,14 +22,12 @@ public final class ShadyTraveler extends CardImpl { this.subtype.add(SubType.WEREWOLF); this.power = new MageInt(2); this.toughness = new MageInt(3); - this.transformable = true; this.secondSideCardClazz = mage.cards.s.StalkingPredator.class; // Menace this.addAbility(new MenaceAbility()); // Daybound - this.addAbility(new TransformAbility()); this.addAbility(new DayboundAbility()); } diff --git a/Mage.Sets/src/mage/cards/s/ShaileDeanOfRadiance.java b/Mage.Sets/src/mage/cards/s/ShaileDeanOfRadiance.java index 31bc50fd8d4..445191fd3ba 100644 --- a/Mage.Sets/src/mage/cards/s/ShaileDeanOfRadiance.java +++ b/Mage.Sets/src/mage/cards/s/ShaileDeanOfRadiance.java @@ -17,6 +17,7 @@ import mage.constants.SubType; import mage.constants.SuperType; import mage.counters.CounterType; import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicate; @@ -35,13 +36,11 @@ public final class ShaileDeanOfRadiance extends ModalDoubleFacesCard { private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("another target creature"); private static final FilterPermanent shaileFilter = new FilterControlledCreaturePermanent("creature that entered the battlefield under your control this turn"); - private static final FilterPermanent embroseFilter = new FilterControlledCreaturePermanent("a creature you control with a +1/+1 counter on it"); static { filter.add(AnotherPredicate.instance); shaileFilter.add(EnteredThisTurnPredicate.instance); shaileFilter.add((Predicate) (input, game) -> !input.checkControlChanged(game)); - embroseFilter.add(CounterType.P1P1.getPredicate()); } public ShaileDeanOfRadiance(UUID ownerId, CardSetInfo setInfo) { @@ -77,7 +76,7 @@ public final class ShaileDeanOfRadiance extends ModalDoubleFacesCard { this.getRightHalfCard().addAbility(ability); // Whenever a creature you control with a +1/+1 counter on it dies, draw a card. - this.getRightHalfCard().addAbility(new DiesCreatureTriggeredAbility(new DrawCardSourceControllerEffect(1), false, embroseFilter)); + this.getRightHalfCard().addAbility(new DiesCreatureTriggeredAbility(new DrawCardSourceControllerEffect(1), false, StaticFilters.FILTER_A_CONTROLLED_CREATURE_P1P1)); } private ShaileDeanOfRadiance(final ShaileDeanOfRadiance card) { diff --git a/Mage.Sets/src/mage/cards/s/ShaleskinBruiser.java b/Mage.Sets/src/mage/cards/s/ShaleskinBruiser.java index 14cce8d24e6..78390e22c5c 100644 --- a/Mage.Sets/src/mage/cards/s/ShaleskinBruiser.java +++ b/Mage.Sets/src/mage/cards/s/ShaleskinBruiser.java @@ -4,6 +4,7 @@ package mage.cards.s; 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; @@ -29,6 +30,7 @@ public final class ShaleskinBruiser extends CardImpl { filter.add(AnotherPredicate.instance); } + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter, 3); public ShaleskinBruiser(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{6}{R}"); @@ -39,8 +41,7 @@ public final class ShaleskinBruiser extends CardImpl { // Trample this.addAbility(TrampleAbility.getInstance()); // Whenever Shaleskin Bruiser attacks, it gets +3/+0 until end of turn for each other attacking Beast. - PermanentsOnBattlefieldCount value = new PermanentsOnBattlefieldCount(filter, 3); - this.addAbility(new AttacksTriggeredAbility(new BoostSourceEffect(value, StaticValue.get(0), Duration.EndOfTurn, true), false)); + this.addAbility(new AttacksTriggeredAbility(new BoostSourceEffect(xValue, StaticValue.get(0), Duration.EndOfTurn, true, "it"), false)); } private ShaleskinBruiser(final ShaleskinBruiser card) { diff --git a/Mage.Sets/src/mage/cards/s/ShamanOfTheGreatHunt.java b/Mage.Sets/src/mage/cards/s/ShamanOfTheGreatHunt.java index 4a2e91a93d3..93e7ea1deef 100644 --- a/Mage.Sets/src/mage/cards/s/ShamanOfTheGreatHunt.java +++ b/Mage.Sets/src/mage/cards/s/ShamanOfTheGreatHunt.java @@ -22,6 +22,7 @@ import mage.constants.ComparisonType; import mage.constants.SetTargetPointer; import mage.constants.Zone; import mage.counters.CounterType; +import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.PowerPredicate; @@ -52,7 +53,7 @@ public final class ShamanOfTheGreatHunt extends CardImpl { effect.setText("put a +1/+1 counter on that creature"); this.addAbility(new DealsDamageToAPlayerAllTriggeredAbility( effect, - new FilterControlledCreaturePermanent("a creature you control"), false, SetTargetPointer.PERMANENT, true + StaticFilters.FILTER_CONTROLLED_A_CREATURE, false, SetTargetPointer.PERMANENT, true )); // Ferocious — {2}{G/U}{G/U}: Draw a card for each creature you control with power 4 or greater. diff --git a/Mage.Sets/src/mage/cards/s/ShamblingGoblin.java b/Mage.Sets/src/mage/cards/s/ShamblingGoblin.java index a87a6df18cd..4b6137c2fc8 100644 --- a/Mage.Sets/src/mage/cards/s/ShamblingGoblin.java +++ b/Mage.Sets/src/mage/cards/s/ShamblingGoblin.java @@ -11,8 +11,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; /** @@ -21,12 +20,6 @@ import mage.target.common.TargetCreaturePermanent; */ public final class ShamblingGoblin extends CardImpl { - private static final FilterCreaturePermanent filterOpponentCreature = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filterOpponentCreature.add(TargetController.OPPONENT.getControllerPredicate()); - } - public ShamblingGoblin(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{B}"); this.subtype.add(SubType.ZOMBIE); @@ -36,7 +29,7 @@ public final class ShamblingGoblin extends CardImpl { // When Shambling Goblin dies, target creature an opponent controls gets -1/-1 until end of turn. Ability ability = new DiesSourceTriggeredAbility(new BoostTargetEffect(-1,-1, Duration.EndOfTurn)); - ability.addTarget(new TargetCreaturePermanent(filterOpponentCreature)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/ShannaSisaysLegacy.java b/Mage.Sets/src/mage/cards/s/ShannaSisaysLegacy.java index be9f729b723..8adccfdb727 100644 --- a/Mage.Sets/src/mage/cards/s/ShannaSisaysLegacy.java +++ b/Mage.Sets/src/mage/cards/s/ShannaSisaysLegacy.java @@ -18,7 +18,7 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.Zone; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -30,8 +30,6 @@ import mage.game.stack.StackObject; */ public final class ShannaSisaysLegacy extends CardImpl { - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("creatures you control"); - public ShannaSisaysLegacy(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}{W}"); @@ -45,7 +43,7 @@ public final class ShannaSisaysLegacy extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ShannaSisaysLegacyEffect())); // Shanna gets +1/+1 for each creature you control. - DynamicValue value = new PermanentsOnBattlefieldCount(filter); + DynamicValue value = new PermanentsOnBattlefieldCount(StaticFilters.FILTER_CONTROLLED_CREATURES); this.addAbility(new SimpleStaticAbility( Zone.BATTLEFIELD, new BoostSourceEffect(value, value, Duration.WhileOnBattlefield) diff --git a/Mage.Sets/src/mage/cards/s/ShatteredDreams.java b/Mage.Sets/src/mage/cards/s/ShatteredDreams.java index 9ca10425d53..e799faae24d 100644 --- a/Mage.Sets/src/mage/cards/s/ShatteredDreams.java +++ b/Mage.Sets/src/mage/cards/s/ShatteredDreams.java @@ -6,7 +6,7 @@ import mage.abilities.effects.common.discard.DiscardCardYouChooseTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.target.common.TargetOpponent; /** @@ -15,18 +15,13 @@ import mage.target.common.TargetOpponent; */ public final class ShatteredDreams extends CardImpl { - private static final FilterCard filter = new FilterCard("an artifact card from it"); - static { - filter.add(CardType.ARTIFACT.getPredicate()); - } - public ShatteredDreams(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{B}"); // Target opponent reveals their hand. You choose an artifact card from it. That player discards that card. this.getSpellAbility().addTarget(new TargetOpponent()); - this.getSpellAbility().addEffect(new DiscardCardYouChooseTargetEffect(filter)); + this.getSpellAbility().addEffect(new DiscardCardYouChooseTargetEffect(StaticFilters.FILTER_CARD_ARTIFACT_AN)); } private ShatteredDreams(final ShatteredDreams card) { diff --git a/Mage.Sets/src/mage/cards/s/ShatteredSanctum.java b/Mage.Sets/src/mage/cards/s/ShatteredSanctum.java new file mode 100644 index 00000000000..6cf6dc1d8e6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/ShatteredSanctum.java @@ -0,0 +1,49 @@ +package mage.cards.s; + +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.TapSourceEffect; +import mage.abilities.mana.BlackManaAbility; +import mage.abilities.mana.WhiteManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ShatteredSanctum extends CardImpl { + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition( + StaticFilters.FILTER_LANDS, ComparisonType.FEWER_THAN, 2 + ); + + public ShatteredSanctum(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // Shattered Sanctum enters the battlefield tapped unless you control two or more other lands. + this.addAbility(new EntersBattlefieldAbility( + new ConditionalOneShotEffect(new TapSourceEffect(), condition), + "tapped unless you control two or more other lands" + )); + + // {T}: Add {W} or {B}. + this.addAbility(new WhiteManaAbility()); + this.addAbility(new BlackManaAbility()); + } + + private ShatteredSanctum(final ShatteredSanctum card) { + super(card); + } + + @Override + public ShatteredSanctum copy() { + return new ShatteredSanctum(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/ShedWeakness.java b/Mage.Sets/src/mage/cards/s/ShedWeakness.java index c2e924c0ed9..1ad5728a2a7 100644 --- a/Mage.Sets/src/mage/cards/s/ShedWeakness.java +++ b/Mage.Sets/src/mage/cards/s/ShedWeakness.java @@ -1,4 +1,3 @@ - package mage.cards.s; import java.util.UUID; @@ -71,7 +70,7 @@ class MayRemoveM1M1CouterTargetEffect extends OneShotEffect { if (target.getCounters(game).getCount(CounterType.M1M1) > 0) { if (controller.chooseUse(outcome, "Remove a -1/-1 counter from " + target.getIdName() + "?", source, game)) { Effect effect = new RemoveCounterTargetEffect(CounterType.M1M1.createInstance()); - effect.setTargetPointer(new FixedTarget(target.getId())); + effect.setTargetPointer(new FixedTarget(target.getId(), game)); effect.apply(game, source); } } @@ -84,4 +83,3 @@ class MayRemoveM1M1CouterTargetEffect extends OneShotEffect { return new MayRemoveM1M1CouterTargetEffect(this); } } - diff --git a/Mage.Sets/src/mage/cards/s/ShelteringBoughs.java b/Mage.Sets/src/mage/cards/s/ShelteringBoughs.java new file mode 100644 index 00000000000..29fd43d589f --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/ShelteringBoughs.java @@ -0,0 +1,52 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +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.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ShelteringBoughs extends CardImpl { + + public ShelteringBoughs(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // When Sheltering Boughs enters the battlefield, draw a card. + this.addAbility(new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(1))); + + // Enchanted creature gets +1/+3. + this.addAbility(new SimpleStaticAbility(new BoostEnchantedEffect(1, 3))); + } + + private ShelteringBoughs(final ShelteringBoughs card) { + super(card); + } + + @Override + public ShelteringBoughs copy() { + return new ShelteringBoughs(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/ShelteringLight.java b/Mage.Sets/src/mage/cards/s/ShelteringLight.java index 6dc54b37b9e..bdcaf9b6ae8 100644 --- a/Mage.Sets/src/mage/cards/s/ShelteringLight.java +++ b/Mage.Sets/src/mage/cards/s/ShelteringLight.java @@ -22,7 +22,7 @@ public final class ShelteringLight extends CardImpl { // Target creature gains indestructible until end of turn. Scry 1. this.getSpellAbility().addEffect(new GainAbilityTargetEffect(IndestructibleAbility.getInstance(), Duration.EndOfTurn)); - this.getSpellAbility().addEffect(new ScryEffect(1)); + this.getSpellAbility().addEffect(new ScryEffect(1, false)); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } diff --git a/Mage.Sets/src/mage/cards/s/ShieldMare.java b/Mage.Sets/src/mage/cards/s/ShieldMare.java index 465258b4e27..41e13c22a23 100644 --- a/Mage.Sets/src/mage/cards/s/ShieldMare.java +++ b/Mage.Sets/src/mage/cards/s/ShieldMare.java @@ -1,29 +1,25 @@ package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.ObjectColor; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.combat.CantBeBlockedByCreaturesSourceEffect; -import mage.constants.SubType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.TargetController; +import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.FilterStackObject; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.game.stack.StackObject; + +import java.util.UUID; /** - * * @author TheElk801 */ public final class ShieldMare extends CardImpl { @@ -43,10 +39,7 @@ public final class ShieldMare extends CardImpl { // Shield Mare can't be blocked by red creatures. this.addAbility(new SimpleStaticAbility( - Zone.BATTLEFIELD, - new CantBeBlockedByCreaturesSourceEffect( - filter, Duration.WhileOnBattlefield - ) + new CantBeBlockedByCreaturesSourceEffect(filter, Duration.WhileOnBattlefield) )); // When Shield Mare enters the battlefield or becomes the target of a spell or ability and opponent controls, you gain 3 life. @@ -65,12 +58,6 @@ public final class ShieldMare extends CardImpl { class ShieldMareTriggeredAbility extends TriggeredAbilityImpl { - private static final FilterStackObject filter = new FilterStackObject(); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public ShieldMareTriggeredAbility() { super(Zone.ALL, new GainLifeEffect(3)); } @@ -92,24 +79,21 @@ class ShieldMareTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD) { - return event.getTargetId().equals(getSourceId()); - } - if (event.getType() == GameEvent.EventType.TARGETED) { - Permanent permanent = game.getPermanent(getSourceId()); - if (permanent == null) { + switch (event.getType()) { + case ENTERS_THE_BATTLEFIELD: + return event.getTargetId().equals(getSourceId()); + case TARGETED: + break; + default: return false; - } - StackObject object = game.getStack().getStackObject(event.getSourceId()); - return event.getTargetId().equals(getSourceId()) - && filter.match(object, getSourceId(), getControllerId(), game); } - return false; + return event.getTargetId().equals(this.getSourceId()) + && game.getOpponents(this.getControllerId()).contains(game.getControllerId(event.getSourceId())); } @Override public String getRule() { - return "When {this} enters the battlefield or becomes the target" - + " of a spell or ability an opponent controls, you gain 3 life"; + return "When {this} enters the battlefield or becomes the target " + + "of a spell or ability an opponent controls, you gain 3 life"; } } diff --git a/Mage.Sets/src/mage/cards/s/ShieldedAetherThief.java b/Mage.Sets/src/mage/cards/s/ShieldedAetherThief.java index abebd970341..43546ff001d 100644 --- a/Mage.Sets/src/mage/cards/s/ShieldedAetherThief.java +++ b/Mage.Sets/src/mage/cards/s/ShieldedAetherThief.java @@ -35,7 +35,7 @@ public final class ShieldedAetherThief extends CardImpl { this.addAbility(FlashAbility.getInstance()); // Whenever Shield Aether Thief blocks, you get {E}. - this.addAbility(new BlocksSourceTriggeredAbility(new GetEnergyCountersControllerEffect(1), false, true)); + this.addAbility(new BlocksSourceTriggeredAbility(new GetEnergyCountersControllerEffect(1), false)); // {T}, Pay {E}{E}{E}: Draw a card. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), new TapSourceCost()); diff --git a/Mage.Sets/src/mage/cards/s/ShieldedByFaith.java b/Mage.Sets/src/mage/cards/s/ShieldedByFaith.java index 2e0754f721d..0d3514f4f6d 100644 --- a/Mage.Sets/src/mage/cards/s/ShieldedByFaith.java +++ b/Mage.Sets/src/mage/cards/s/ShieldedByFaith.java @@ -18,7 +18,7 @@ import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.SetTargetPointer; import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; @@ -48,7 +48,7 @@ public final class ShieldedByFaith extends CardImpl { // Whenever a creature enters the battlefield, you may attach Shielded by Faith to that creature. this.addAbility(new EntersBattlefieldAllTriggeredAbility( Zone.BATTLEFIELD, new AttachEffect(Outcome.Benefit, "attach {this} to that creature"), - new FilterCreaturePermanent("a creature"), true, SetTargetPointer.PERMANENT, null, false)); + StaticFilters.FILTER_PERMANENT_A_CREATURE, true, SetTargetPointer.PERMANENT, null, false)); } private ShieldedByFaith(final ShieldedByFaith card) { diff --git a/Mage.Sets/src/mage/cards/s/ShiftingShadow.java b/Mage.Sets/src/mage/cards/s/ShiftingShadow.java index a10eba3cba0..348066d4230 100644 --- a/Mage.Sets/src/mage/cards/s/ShiftingShadow.java +++ b/Mage.Sets/src/mage/cards/s/ShiftingShadow.java @@ -1,40 +1,27 @@ - package mage.cards.s; -import java.util.UUID; +import mage.MageObjectReference; import mage.abilities.Ability; +import mage.abilities.Mode; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.Effect; +import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.AttachEffect; -import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; import mage.abilities.keyword.EnchantAbility; import mage.abilities.keyword.HasteAbility; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.cards.Cards; -import mage.cards.CardsImpl; -import mage.constants.AttachmentType; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.SubType; -import mage.constants.TargetController; -import mage.constants.Zone; -import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.permanent.PermanentIdPredicate; +import mage.cards.*; +import mage.constants.*; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; -import mage.target.Target; import mage.target.TargetPermanent; -import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * - * @author Saga + * @author TheElk801 */ public final class ShiftingShadow extends CardImpl { @@ -50,13 +37,7 @@ public final class ShiftingShadow extends CardImpl { // Enchanted creature has haste and “At the beginning of your upkeep, destroy this creature. Reveal cards from the top of your library until you reveal a creature card. // Put that card onto the battlefield and attach Shifting Shadow to it, then put all other cards revealed this way on the bottom of your library in a random order.” - Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(HasteAbility.getInstance(), AttachmentType.AURA)); - Effect effect = new GainAbilityAttachedEffect(new BeginningOfUpkeepTriggeredAbility( - new ShiftingShadowEffect(this.getId()), TargetController.YOU, false), AttachmentType.AURA); - effect.setText("and \"At the beginning of your upkeep, destroy this creature. Reveal cards from the top of your library until you reveal a creature card. " - + "Put that card onto the battlefield and attach {this} to it, then put all other cards revealed this way on the bottom of your library in a random order.\""); - ability.addEffect(effect); - this.addAbility(ability); + this.addAbility(new SimpleStaticAbility(new ShiftingShadowGainEffect())); } private ShiftingShadow(final ShiftingShadow card) { @@ -69,75 +50,108 @@ public final class ShiftingShadow extends CardImpl { } } -class ShiftingShadowEffect extends OneShotEffect { +class ShiftingShadowGainEffect extends ContinuousEffectImpl { - private final UUID auraId; - - public ShiftingShadowEffect(UUID auraId) { - super(Outcome.PutCreatureInPlay); - this.staticText = "destroy this creature. Reveal cards from the top of your library until you reveal a creature card. " - + "Put that card onto the battlefield and attach {this} to it, then put all other cards revealed this way on the bottom of your library in a random order"; - this.auraId = auraId; + ShiftingShadowGainEffect() { + super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); + staticText = "enchanted creature has haste and \"At the beginning of your upkeep, " + + "destroy this creature. Reveal cards from the top of your library until you reveal a creature card. " + + "Put that card onto the battlefield and attach {this} to it, then put all other cards " + + "revealed this way on the bottom of your library in a random order.\""; } - public ShiftingShadowEffect(final ShiftingShadowEffect effect, UUID auraId) { + private ShiftingShadowGainEffect(final ShiftingShadowGainEffect effect) { super(effect); - this.auraId = auraId; } @Override - public ShiftingShadowEffect copy() { - return new ShiftingShadowEffect(this, auraId); + public ShiftingShadowGainEffect copy() { + return new ShiftingShadowGainEffect(this); } @Override public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - Permanent enchanted = game.getPermanentOrLKIBattlefield(source.getSourceId()); - if (controller != null && enchanted != null) { - Permanent aura = null; - int index = 0; - while (aura == null && index < enchanted.getAttachments().size()) { - UUID attached = enchanted.getAttachments().get(index); - if (attached.equals(auraId)) { - aura = game.getPermanentOrLKIBattlefield(attached); - } else { - index += 1; - } - } - if (aura != null) { - enchanted.destroy(source, game, false); - // Because this effect has two steps, we have to call the processAction method here, so that triggered effects of the target going to graveyard go to the stack - // If we don't do it here, gained triggered effects to the target will be removed from the following moveCards method and the applyEffcts done there. - // Example: {@link org.mage.test.commander.duel.MairsilThePretenderTest#MairsilThePretenderTest Test} - game.getState().processAction(game); - - Cards revealed = new CardsImpl(); - Cards otherCards = new CardsImpl(); - for (Card card : controller.getLibrary().getCards(game)) { - revealed.add(card); - if (card != null && card.isCreature(game)) { - controller.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent newEnchanted = game.getPermanent(card.getId()); - FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent(); - filter.add(new PermanentIdPredicate(card.getId())); - Target target = new TargetControlledCreaturePermanent(filter); - if (newEnchanted != null) { - target.addTarget(newEnchanted.getId(), source, game); - aura.getSpellAbility().getTargets().clear(); - aura.getSpellAbility().getTargets().add(target); - newEnchanted.addAttachment(aura.getId(), source, game); - } - break; - } else { - otherCards.add(card); - } - } - controller.revealCards(enchanted.getIdName(), revealed, game); - controller.putCardsOnBottomOfLibrary(otherCards, game, source, false); - return true; - } + Permanent aura = source.getSourcePermanentIfItStillExists(game); + if (aura == null) { + return false; } - return false; + Permanent permanent = game.getPermanent(aura.getAttachedTo()); + if (permanent == null) { + return false; + } + permanent.addAbility(HasteAbility.getInstance(), source.getSourceId(), game); + permanent.addAbility(new BeginningOfUpkeepTriggeredAbility( + new ShiftingShadowEffect(aura, game), TargetController.YOU, false + ), source.getSourceId(), game); + return true; + } +} + +class ShiftingShadowEffect extends OneShotEffect { + + private final MageObjectReference mor; + private final String name; + + ShiftingShadowEffect(Permanent permanent, Game game) { + super(Outcome.Benefit); + this.mor = new MageObjectReference(permanent, game); + this.name = permanent.getName(); + } + + private ShiftingShadowEffect(final ShiftingShadowEffect effect) { + super(effect); + this.mor = effect.mor; + this.name = effect.name; + } + + @Override + public ShiftingShadowEffect copy() { + return new ShiftingShadowEffect(this); + } + + private static Card getCard(Player player, Cards cards, Game game) { + for (Card card : player.getLibrary().getCards(game)) { + cards.add(card); + if (card.isCreature(game)) { + return card; + } + } + return null; + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (permanent != null) { + permanent.destroy(source, game); + game.getState().processAction(game); + } + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return true; + } + Cards cards = new CardsImpl(); + Card card = getCard(player, cards, game); + player.revealCards(source, cards, game); + Permanent creature; + if (card != null) { + player.moveCards(card, Zone.BATTLEFIELD, source, game); + creature = game.getPermanent(card.getId()); + } else { + creature = null; + } + if (creature != null && mor.zoneCounterIsCurrent(game)) { + creature.addAttachment(mor.getSourceId(), source, game); + } + cards.retainZone(Zone.LIBRARY, game); + player.putCardsOnBottomOfLibrary(cards, game, source, false); + return true; + } + + @Override + public String getText(Mode mode) { + return "destroy this creature. Reveal cards from the top of your library until you reveal a creature card. " + + "Put that card onto the battlefield and attach " + name + " to it, then put all other cards " + + "revealed this way on the bottom of your library in a random order."; } } diff --git a/Mage.Sets/src/mage/cards/s/ShigekiJukaiVisionary.java b/Mage.Sets/src/mage/cards/s/ShigekiJukaiVisionary.java new file mode 100644 index 00000000000..78a3a3d5870 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/ShigekiJukaiVisionary.java @@ -0,0 +1,113 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.ReturnToHandFromBattlefieldSourceCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.abilities.keyword.ChannelAbility; +import mage.cards.*; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInLibrary; +import mage.target.common.TargetCardInYourGraveyard; +import mage.target.targetadjustment.TargetAdjuster; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ShigekiJukaiVisionary extends CardImpl { + + public ShigekiJukaiVisionary(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{1}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.SNAKE); + this.subtype.add(SubType.DRUID); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // {1}{G}, {T}, Return Shigeki, Jukai Visionary to its owner's hand: Reveal the top four cards of your library. You may put a land card from among them onto the battlefield tapped. Put the rest into your graveyard. + Ability ability = new SimpleActivatedAbility(new ShigekiJukaiVisionaryEffect(), new ManaCostsImpl<>("{1}{G}")); + ability.addCost(new TapSourceCost()); + ability.addCost(new ReturnToHandFromBattlefieldSourceCost()); + this.addAbility(ability); + + // Channel — {X}{X}{G}{G}, Discard Shigeki: Return X target nonlegendary cards from your graveyard to your hand. + this.addAbility(new ChannelAbility( + "{X}{X}{G}{G}", new ReturnFromGraveyardToHandTargetEffect() + .setText("return X target nonlegendary cards from your graveyard to your hand") + ).setTargetAdjuster(ShigekiJukaiVisionaryAdjuster.instance)); + } + + private ShigekiJukaiVisionary(final ShigekiJukaiVisionary card) { + super(card); + } + + @Override + public ShigekiJukaiVisionary copy() { + return new ShigekiJukaiVisionary(this); + } +} + +enum ShigekiJukaiVisionaryAdjuster implements TargetAdjuster { + instance; + private static final FilterCard filter = new FilterCard("nonlegendary cards from your graveyard"); + + static { + filter.add(Predicates.not(SuperType.LEGENDARY.getPredicate())); + } + + @Override + public void adjustTargets(Ability ability, Game game) { + ability.getTargets().clear(); + ability.addTarget(new TargetCardInYourGraveyard(ability.getManaCostsToPay().getX(), filter)); + } +} + +class ShigekiJukaiVisionaryEffect extends OneShotEffect { + + ShigekiJukaiVisionaryEffect() { + super(Outcome.Benefit); + staticText = "reveal the top four cards of your library. You may put a land card " + + "from among them onto the battlefield tapped. Put the rest into your graveyard"; + } + + private ShigekiJukaiVisionaryEffect(final ShigekiJukaiVisionaryEffect effect) { + super(effect); + } + + @Override + public ShigekiJukaiVisionaryEffect copy() { + return new ShigekiJukaiVisionaryEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Cards cards = new CardsImpl(player.getLibrary().getTopCards(game, 4)); + player.revealCards(source, cards, game); + TargetCard target = new TargetCardInLibrary(0, 1, StaticFilters.FILTER_CARD_LAND); + player.choose(outcome, cards, target, game); + Card card = game.getCard(target.getFirstTarget()); + if (card != null) { + player.moveCards(card, Zone.BATTLEFIELD, source, game, true, false, true, null); + cards.remove(card); + } + player.moveCards(cards, Zone.GRAVEYARD, source, game); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/s/ShiningShoal.java b/Mage.Sets/src/mage/cards/s/ShiningShoal.java index 7685e0eae52..ee5887fe1a0 100644 --- a/Mage.Sets/src/mage/cards/s/ShiningShoal.java +++ b/Mage.Sets/src/mage/cards/s/ShiningShoal.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageObject; import mage.ObjectColor; import mage.abilities.Ability; @@ -16,8 +14,6 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; import mage.filter.common.FilterOwnedCard; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardIdPredicate; import mage.filter.predicate.mageobject.ColorPredicate; import mage.game.Game; import mage.game.events.GameEvent; @@ -27,24 +23,33 @@ import mage.target.TargetSource; import mage.target.common.TargetAnyTarget; import mage.target.common.TargetCardInHand; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class ShiningShoal extends CardImpl { + private static final FilterOwnedCard filter + = new FilterOwnedCard("a white card with mana value X from your hand"); + + static { + filter.add(new ColorPredicate(ObjectColor.WHITE)); + } + public ShiningShoal(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{X}{W}{W}"); this.subtype.add(SubType.ARCANE); // You may exile a white card with converted mana cost X from your hand rather than pay Shining Shoal's mana cost - FilterOwnedCard filter = new FilterOwnedCard("a white card with mana value X from your hand"); - filter.add(new ColorPredicate(ObjectColor.WHITE)); - filter.add(Predicates.not(new CardIdPredicate(this.getId()))); // the exile cost can never be paid with the card itself - this.addAbility(new AlternativeCostSourceAbility(new ExileFromHandCost(new TargetCardInHand(filter), true))); + this.addAbility(new AlternativeCostSourceAbility(new ExileFromHandCost( + new TargetCardInHand(filter), true + ))); // The next X damage that a source of your choice would deal to you and/or creatures you control this turn is dealt to any target instead. - this.getSpellAbility().addEffect(new ShiningShoalRedirectDamageTargetEffect(Duration.EndOfTurn, ExileFromHandCostCardConvertedMana.instance)); + this.getSpellAbility().addEffect(new ShiningShoalRedirectDamageTargetEffect( + Duration.EndOfTurn, ExileFromHandCostCardConvertedMana.instance + )); this.getSpellAbility().addTarget(new TargetSource()); this.getSpellAbility().addTarget(new TargetAnyTarget()); } @@ -129,5 +134,4 @@ class ShiningShoalRedirectDamageTargetEffect extends RedirectDamageFromSourceToT } return false; } - } diff --git a/Mage.Sets/src/mage/cards/s/ShinkaGatekeeper.java b/Mage.Sets/src/mage/cards/s/ShinkaGatekeeper.java index 22775f3e7fd..20f608fa9f3 100644 --- a/Mage.Sets/src/mage/cards/s/ShinkaGatekeeper.java +++ b/Mage.Sets/src/mage/cards/s/ShinkaGatekeeper.java @@ -28,7 +28,7 @@ public final class ShinkaGatekeeper extends CardImpl { this.toughness = new MageInt(2); // Whenever Shinka Gatekeeper is dealt damage, it deals that much damage to you. - this.addAbility(new DealtDamageToSourceTriggeredAbility(new ShinkaGatekeeperDealDamageEffect(), false, false, true)); + this.addAbility(new DealtDamageToSourceTriggeredAbility(new ShinkaGatekeeperDealDamageEffect(), false, false)); } private ShinkaGatekeeper(final ShinkaGatekeeper card) { diff --git a/Mage.Sets/src/mage/cards/s/ShinyImpetus.java b/Mage.Sets/src/mage/cards/s/ShinyImpetus.java index f02de1301c2..2f0eef20584 100644 --- a/Mage.Sets/src/mage/cards/s/ShinyImpetus.java +++ b/Mage.Sets/src/mage/cards/s/ShinyImpetus.java @@ -2,9 +2,10 @@ package mage.cards.s; import mage.abilities.Ability; import mage.abilities.common.AttacksAttachedTriggeredAbility; -import mage.abilities.common.GoadAttachedAbility; +import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.combat.GoadAttachedEffect; import mage.abilities.effects.common.continuous.BoostEnchantedEffect; import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; @@ -37,7 +38,9 @@ public final class ShinyImpetus extends CardImpl { this.addAbility(ability); // Enchanted creature gets +2/+2 and is goaded. - this.addAbility(new GoadAttachedAbility(new BoostEnchantedEffect(2, 2))); + ability = new SimpleStaticAbility(new BoostEnchantedEffect(2, 2)); + ability.addEffect(new GoadAttachedEffect()); + this.addAbility(ability); // Whenever enchanted creature attacks, you create a Treasure token. this.addAbility(new AttacksAttachedTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/s/ShipbreakerKraken.java b/Mage.Sets/src/mage/cards/s/ShipbreakerKraken.java index 3156591d5c8..cb022bbd3df 100644 --- a/Mage.Sets/src/mage/cards/s/ShipbreakerKraken.java +++ b/Mage.Sets/src/mage/cards/s/ShipbreakerKraken.java @@ -1,37 +1,28 @@ package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.BecomesMonstrousSourceTriggeredAbility; -import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; +import mage.abilities.effects.common.DontUntapInControllersUntapStepTargetEffect; import mage.abilities.effects.common.TapTargetEffect; import mage.abilities.keyword.MonstrosityAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.PhaseStep; -import mage.constants.WatcherScope; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.ZoneChangeEvent; -import mage.game.permanent.Permanent; +import mage.constants.SubType; import mage.target.common.TargetCreaturePermanent; -import mage.watchers.Watcher; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class ShipbreakerKraken extends CardImpl { public ShipbreakerKraken(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}{U}"); this.subtype.add(SubType.KRAKEN); this.power = new MageInt(6); @@ -39,11 +30,13 @@ public final class ShipbreakerKraken extends CardImpl { // {6}{U}{U}: Monstrosity 4. this.addAbility(new MonstrosityAbility("{6}{U}{U}", 4)); + // When Shipbreaker Kraken becomes monstrous, tap up to four target creatures. Those creatures don't untap during their controllers' untap steps for as long as you control Shipbreaker Kraken. Ability ability = new BecomesMonstrousSourceTriggeredAbility(new TapTargetEffect()); - ability.addTarget(new TargetCreaturePermanent(0,4)); - ability.addEffect(new ShipbreakerKrakenReplacementEffect()); - this.addAbility(ability, new ShipbreakerKrakenWatcher()); + ability.addTarget(new TargetCreaturePermanent(0, 4)); + ability.addEffect(new DontUntapInControllersUntapStepTargetEffect(Duration.WhileControlled) + .setText("Those creatures don't untap during their controllers' untap steps for as long as you control {this}")); + this.addAbility(ability); } private ShipbreakerKraken(final ShipbreakerKraken card) { @@ -55,95 +48,3 @@ public final class ShipbreakerKraken extends CardImpl { return new ShipbreakerKraken(this); } } - -class ShipbreakerKrakenReplacementEffect extends ContinuousRuleModifyingEffectImpl { - - public ShipbreakerKrakenReplacementEffect() { - super(Duration.Custom, Outcome.Detriment); - this.staticText = "Those creatures don't untap during their controllers' untap steps for as long as you control {this}"; - } - - public ShipbreakerKrakenReplacementEffect(final ShipbreakerKrakenReplacementEffect effect) { - super(effect); - } - - @Override - public ShipbreakerKrakenReplacementEffect copy() { - return new ShipbreakerKrakenReplacementEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - return false; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.LOST_CONTROL || - event.getType() == GameEvent.EventType.ZONE_CHANGE || - event.getType() == GameEvent.EventType.UNTAP; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - // Source must be on the battlefield (it's neccessary to check here because if as response to the enter - // the battlefield triggered ability the source dies (or will be exiled), then the ZONE_CHANGE or LOST_CONTROL - // event will happen before this effect is applied ever) - Permanent sourcePermanent = game.getPermanent(source.getSourceId()); - if (sourcePermanent == null || !sourcePermanent.isControlledBy(source.getControllerId())) { - discard(); - return false; - } - if (event.getType() == GameEvent.EventType.LOST_CONTROL) { - if (event.getTargetId().equals(source.getSourceId())) { - discard(); - return false; - } - } - if (event.getType() == GameEvent.EventType.ZONE_CHANGE && event.getTargetId().equals(source.getSourceId())) { - ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - if (zEvent.getFromZone() == Zone.BATTLEFIELD) { - discard(); - return false; - } - } - - if (game.getTurn().getStepType() == PhaseStep.UNTAP && event.getType() == GameEvent.EventType.UNTAP) { - if (targetPointer.getTargets(game, source).contains(event.getTargetId())) { - return true; - } - } - - return false; - } -} - -class ShipbreakerKrakenWatcher extends Watcher { - - ShipbreakerKrakenWatcher () { - super(WatcherScope.CARD); - } - - @Override - public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.LOST_CONTROL - && event.getPlayerId().equals(controllerId) - && event.getTargetId().equals(sourceId)) { - condition = true; - game.replaceEvent(event); - return; - } - if (event.getType() == GameEvent.EventType.ZONE_CHANGE && event.getTargetId().equals(sourceId)) { - ZoneChangeEvent zEvent = (ZoneChangeEvent)event; - if (zEvent.getFromZone() == Zone.BATTLEFIELD) { - condition = true; - game.replaceEvent(event); - } - } - } - - @Override - public void reset() { - //don't reset condition each turn - only when this leaves the battlefield - } -} diff --git a/Mage.Sets/src/mage/cards/s/ShipwreckLooter.java b/Mage.Sets/src/mage/cards/s/ShipwreckLooter.java index 7976888aa4e..74b45ec082a 100644 --- a/Mage.Sets/src/mage/cards/s/ShipwreckLooter.java +++ b/Mage.Sets/src/mage/cards/s/ShipwreckLooter.java @@ -32,8 +32,7 @@ public final class ShipwreckLooter extends CardImpl { // Raid - When Shipwreck Looter enters the battlefield,if you attacked this turn, you may draw a card. If you do, discard a card. Ability ability = new ConditionalInterveningIfTriggeredAbility( new EntersBattlefieldTriggeredAbility(new DrawDiscardControllerEffect(1, 1, true)), - RaidCondition.instance, - "Raid — When {this} enters the battlefield, if you attacked this turn, you may draw a card. If you do, discard a card."); + RaidCondition.instance, "When {this} enters the battlefield, if you attacked this turn, you may draw a card. If you do, discard a card."); ability.setAbilityWord(AbilityWord.RAID); ability.addHint(RaidHint.instance); this.addAbility(ability, new PlayerAttackedWatcher()); diff --git a/Mage.Sets/src/mage/cards/s/ShipwreckSinger.java b/Mage.Sets/src/mage/cards/s/ShipwreckSinger.java index 22893c2539b..01dd3e53836 100644 --- a/Mage.Sets/src/mage/cards/s/ShipwreckSinger.java +++ b/Mage.Sets/src/mage/cards/s/ShipwreckSinger.java @@ -1,4 +1,3 @@ - package mage.cards.s; import java.util.UUID; @@ -15,11 +14,8 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.TargetController; import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AttackingPredicate; -import mage.target.Target; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; /** @@ -28,13 +24,6 @@ import mage.target.common.TargetCreaturePermanent; */ public final class ShipwreckSinger extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); - private static final FilterCreaturePermanent filterAttacking = new FilterCreaturePermanent("Attacking creatures"); - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - filterAttacking.add(AttackingPredicate.instance); - } - public ShipwreckSinger(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{U}{B}"); this.subtype.add(SubType.SIREN); @@ -45,15 +34,13 @@ public final class ShipwreckSinger extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); // {1}{U}: Target creature an opponent controls attacks this turn if able. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AttacksIfAbleTargetEffect(Duration.EndOfTurn), new ManaCostsImpl("{1}{U}")); - Target target = new TargetCreaturePermanent(filter); - ability.addTarget(target); + Ability ability = new SimpleActivatedAbility(new AttacksIfAbleTargetEffect(Duration.EndOfTurn), new ManaCostsImpl("{1}{U}")); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); this.addAbility(ability); // {1}{B}, {T}: Attacking creatures get -1/-1 until end of turn. - ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostAllEffect(-1,-1, Duration.EndOfTurn, filterAttacking, false), new ManaCostsImpl("{1}{B}")); + ability = new SimpleActivatedAbility(new BoostAllEffect(-1,-1, Duration.EndOfTurn, StaticFilters.FILTER_ATTACKING_CREATURES, false), new ManaCostsImpl("{1}{B}")); ability.addCost(new TapSourceCost()); this.addAbility(ability); - } private ShipwreckSinger(final ShipwreckSinger card) { diff --git a/Mage.Sets/src/mage/cards/s/ShireiShizosCaretaker.java b/Mage.Sets/src/mage/cards/s/ShireiShizosCaretaker.java index d030d0a960f..a9c17b62b5e 100644 --- a/Mage.Sets/src/mage/cards/s/ShireiShizosCaretaker.java +++ b/Mage.Sets/src/mage/cards/s/ShireiShizosCaretaker.java @@ -1,30 +1,38 @@ package mage.cards.s; import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.DiesCreatureTriggeredAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.ReturnToBattlefieldUnderYourControlTargetEffect; -import mage.cards.Card; +import mage.abilities.condition.common.SourceOnBattlefieldCondition; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.ZoneChangeEvent; -import mage.game.permanent.Permanent; -import mage.target.targetpointer.FixedTarget; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.PowerPredicate; import java.util.UUID; /** - * @author emerald000 + * @author TheElk801 */ public final class ShireiShizosCaretaker extends CardImpl { + private static final FilterPermanent filter = new FilterCreaturePermanent(); + + static { + filter.add(TargetController.YOU.getOwnerPredicate()); + filter.add(new PowerPredicate(ComparisonType.FEWER_THAN, 2)); + } + + private static final String rule1 = "you may return that card to the battlefield " + + "t the beginning of the next end step if {this} is still on the battlefield"; + private static final String rule2 = "Whenever a creature with power 1 or less " + + "is put into your graveyard from the battlefield, "; + public ShireiShizosCaretaker(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}"); addSuperType(SuperType.LEGENDARY); @@ -34,7 +42,12 @@ public final class ShireiShizosCaretaker extends CardImpl { this.toughness = new MageInt(2); // Whenever a creature with power 1 or less is put into your graveyard from the battlefield, you may return that card to the battlefield at the beginning of the next end step if Shirei, Shizo's Caretaker is still on the battlefield. - this.addAbility(new ShireiShizosCaretakerTriggeredAbility(this.getId())); + this.addAbility(new DiesCreatureTriggeredAbility(new CreateDelayedTriggeredAbilityEffect( + new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new ConditionalOneShotEffect( + new ReturnFromGraveyardToBattlefieldTargetEffect(), SourceOnBattlefieldCondition.instance, + "you may return that card to the battlefield if {this} is still on the battlefield" + ), TargetController.ANY, null, true) + ).setText(rule1), false, filter, true).setTriggerPhrase(rule2)); } private ShireiShizosCaretaker(final ShireiShizosCaretaker card) { @@ -46,110 +59,3 @@ public final class ShireiShizosCaretaker extends CardImpl { return new ShireiShizosCaretaker(this); } } - -class ShireiShizosCaretakerTriggeredAbility extends TriggeredAbilityImpl { - - ShireiShizosCaretakerTriggeredAbility(UUID shireiId) { - super(Zone.BATTLEFIELD, new ShireiShizosCaretakerEffect(shireiId), false); - } - - ShireiShizosCaretakerTriggeredAbility(final ShireiShizosCaretakerTriggeredAbility ability) { - super(ability); - } - - @Override - public ShireiShizosCaretakerTriggeredAbility copy() { - return new ShireiShizosCaretakerTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ZONE_CHANGE; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - Permanent LKIpermanent = game.getPermanentOrLKIBattlefield(zEvent.getTargetId()); - Card card = game.getCard(zEvent.getTargetId()); - if (card != null - && LKIpermanent != null - && card.isOwnedBy(this.controllerId) - && zEvent.isDiesEvent() - && card.isCreature(game) - && LKIpermanent.getPower().getValue() <= 1) { - for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(zEvent.getTargetId())); - } - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever a creature with power 1 or less is put into your graveyard from the battlefield, you may return that card to the battlefield at the beginning of the next end step if Shirei, Shizo's Caretaker is still on the battlefield."; - } -} - -class ShireiShizosCaretakerEffect extends OneShotEffect { - - protected final UUID shireiId; - - ShireiShizosCaretakerEffect(UUID shireiId) { - super(Outcome.PutCreatureInPlay); - this.staticText = "you may return that card to the battlefield at the beginning of the next end step if {this} is still on the battlefield."; - this.shireiId = shireiId; - } - - ShireiShizosCaretakerEffect(final ShireiShizosCaretakerEffect effect) { - super(effect); - this.shireiId = effect.shireiId; - } - - @Override - public ShireiShizosCaretakerEffect copy() { - return new ShireiShizosCaretakerEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Card card = game.getCard(this.getTargetPointer().getFirst(game, source)); - if (card != null) { - Effect effect = new ShireiShizosCaretakerReturnEffect(shireiId); - effect.setText("return that card to the battlefield if {this} is still on the battlefield"); - DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect); - delayedAbility.getEffects().get(0).setTargetPointer(new FixedTarget(card, game)); - game.addDelayedTriggeredAbility(delayedAbility, source); - return true; - } - return false; - } -} - -class ShireiShizosCaretakerReturnEffect extends ReturnToBattlefieldUnderYourControlTargetEffect { - - protected final UUID shireiId; - - ShireiShizosCaretakerReturnEffect(UUID shireiId) { - this.shireiId = shireiId; - } - - ShireiShizosCaretakerReturnEffect(final ShireiShizosCaretakerReturnEffect effect) { - super(effect); - this.shireiId = effect.shireiId; - } - - @Override - public ShireiShizosCaretakerReturnEffect copy() { - return new ShireiShizosCaretakerReturnEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - if (game.getBattlefield().containsPermanent(shireiId)) { - return super.apply(game, source); - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/s/ShivanEmissary.java b/Mage.Sets/src/mage/cards/s/ShivanEmissary.java index 719a3e22de7..9c03db22543 100644 --- a/Mage.Sets/src/mage/cards/s/ShivanEmissary.java +++ b/Mage.Sets/src/mage/cards/s/ShivanEmissary.java @@ -1,9 +1,7 @@ - package mage.cards.s; import java.util.UUID; import mage.MageInt; -import mage.ObjectColor; import mage.abilities.TriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.KickedCondition; @@ -14,9 +12,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -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; /** @@ -26,12 +22,6 @@ import mage.target.common.TargetCreaturePermanent; */ public final class ShivanEmissary extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("black creature"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public ShivanEmissary(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{R}"); this.subtype.add(SubType.HUMAN); @@ -43,7 +33,7 @@ public final class ShivanEmissary extends CardImpl { this.addAbility(new KickerAbility("{1}{B}")); // When Shivan Emissary enters the battlefield, if it was kicked, destroy target nonblack creature. It can't be regenerated. TriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect(true)); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, KickedCondition.instance, "When {this} enters the battlefield, if it was kicked, destroy target nonblack creature. It can't be regenerated.")); } diff --git a/Mage.Sets/src/mage/cards/s/ShorecrasherMimic.java b/Mage.Sets/src/mage/cards/s/ShorecrasherMimic.java index 7c286d1f834..59c2e846234 100644 --- a/Mage.Sets/src/mage/cards/s/ShorecrasherMimic.java +++ b/Mage.Sets/src/mage/cards/s/ShorecrasherMimic.java @@ -32,7 +32,7 @@ public final class ShorecrasherMimic extends CardImpl { filter.add(new ColorPredicate(ObjectColor.BLUE)); } - private String rule = "Whenever you cast a spell that's both green and blue, {this} has base power and toughness 5/3 until end of turn and gains trample until end of turn."; + private static final String rule = "Whenever you cast a spell that's both green and blue, {this} has base power and toughness 5/3 until end of turn and gains trample until end of turn."; public ShorecrasherMimic(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G/U}"); diff --git a/Mage.Sets/src/mage/cards/s/ShorikaiGenesisEngine.java b/Mage.Sets/src/mage/cards/s/ShorikaiGenesisEngine.java new file mode 100644 index 00000000000..ecbaf2acea7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/ShorikaiGenesisEngine.java @@ -0,0 +1,57 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.CanBeYourCommanderAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DrawDiscardControllerEffect; +import mage.abilities.keyword.CrewAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.game.permanent.token.PilotToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ShorikaiGenesisEngine extends CardImpl { + + public ShorikaiGenesisEngine(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{W}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.VEHICLE); + this.power = new MageInt(8); + this.toughness = new MageInt(8); + + // {1}, {T}: Draw two cards, then discard a card. Create a 1/1 colorless Pilot creature token with "This creature crews Vehicles as though its power were 2 greater." + Ability ability = new SimpleActivatedAbility( + new DrawDiscardControllerEffect(2, 1), new GenericManaCost(1) + ); + ability.addEffect(new CreateTokenEffect(new PilotToken())); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + + // Crew 8 + this.addAbility(new CrewAbility(8)); + + // Shorikai, Genesis Engine can be your commander. + this.addAbility(CanBeYourCommanderAbility.getInstance()); + } + + private ShorikaiGenesisEngine(final ShorikaiGenesisEngine card) { + super(card); + } + + @Override + public ShorikaiGenesisEngine copy() { + return new ShorikaiGenesisEngine(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/ShortCircuit.java b/Mage.Sets/src/mage/cards/s/ShortCircuit.java new file mode 100644 index 00000000000..85563112904 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/ShortCircuit.java @@ -0,0 +1,67 @@ +package mage.cards.s; + +import mage.abilities.Ability; +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.effects.common.continuous.LoseAbilityAttachedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.abilities.keyword.FlashAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +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 ShortCircuit extends CardImpl { + + private static final Condition condition = new AttachedToMatchesFilterCondition(StaticFilters.FILTER_PERMANENT_CREATURE); + + public ShortCircuit(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}"); + + this.subtype.add(SubType.AURA); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // 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 -3/-0 and loses flying. + Ability ability = new SimpleStaticAbility(new ConditionalContinuousEffect( + new BoostEnchantedEffect(-3, 0), condition, + "as long as enchanted permanent is a creature, it gets -3/-0" + )); + ability.addEffect(new ConditionalContinuousEffect( + new LoseAbilityAttachedEffect( + FlyingAbility.getInstance(), AttachmentType.AURA + ), condition, "and loses flying" + )); + this.addAbility(ability); + } + + private ShortCircuit(final ShortCircuit card) { + super(card); + } + + @Override + public ShortCircuit copy() { + return new ShortCircuit(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/ShowOfDominance.java b/Mage.Sets/src/mage/cards/s/ShowOfDominance.java index 1a362d45c63..cfbf79b586e 100644 --- a/Mage.Sets/src/mage/cards/s/ShowOfDominance.java +++ b/Mage.Sets/src/mage/cards/s/ShowOfDominance.java @@ -1,4 +1,3 @@ - package mage.cards.s; import java.util.UUID; @@ -91,7 +90,7 @@ class ShowOfDominanceEffect extends OneShotEffect { } } if (selectedCreature != null) { - FixedTarget target = new FixedTarget(selectedCreature.getId()); + FixedTarget target = new FixedTarget(selectedCreature.getId(), game); Effect effect = new AddCountersTargetEffect(CounterType.P1P1.createInstance(4)); effect.setTargetPointer(target); diff --git a/Mage.Sets/src/mage/cards/s/ShowdownOfTheSkalds.java b/Mage.Sets/src/mage/cards/s/ShowdownOfTheSkalds.java index 99e469ae5ec..be68929b9d7 100644 --- a/Mage.Sets/src/mage/cards/s/ShowdownOfTheSkalds.java +++ b/Mage.Sets/src/mage/cards/s/ShowdownOfTheSkalds.java @@ -34,7 +34,7 @@ public final class ShowdownOfTheSkalds extends CardImpl { this.subtype.add(SubType.SAGA); // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) - SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); + SagaAbility sagaAbility = new SagaAbility(this); // I — Exile the top four cards of your library. Until the end of your next turn, you may play those cards. sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, new ShowdownOfTheSkaldsEffect()); diff --git a/Mage.Sets/src/mage/cards/s/Showstopper.java b/Mage.Sets/src/mage/cards/s/Showstopper.java index 8fbc3a3f4ba..97c47e1036c 100644 --- a/Mage.Sets/src/mage/cards/s/Showstopper.java +++ b/Mage.Sets/src/mage/cards/s/Showstopper.java @@ -10,9 +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.FilterControlledCreaturePermanent; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.Target; import mage.target.common.TargetCreaturePermanent; @@ -24,23 +22,16 @@ import mage.target.common.TargetCreaturePermanent; public final class Showstopper extends CardImpl { - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("creatures you control"); - private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent("creature an opponent controls"); - static { - filter2.add(TargetController.OPPONENT.getControllerPredicate()); - } - public Showstopper (UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{B}{R}"); // Until end of turn, creatures you control gain "When this creature dies, it deals 2 damage to target creature an opponent controls." TriggeredAbility ability = new DiesSourceTriggeredAbility(new DamageTargetEffect(2, "it"), false); - Target target = new TargetCreaturePermanent(filter2); + Target target = new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE); ability.addTarget(target); - Effect effect = new GainAbilityControlledEffect(ability, Duration.EndOfTurn, filter); + Effect effect = new GainAbilityControlledEffect(ability, Duration.EndOfTurn, StaticFilters.FILTER_CONTROLLED_CREATURES); effect.setText("Until end of turn, creatures you control gain \"When this creature dies, it deals 2 damage to target creature an opponent controls.\""); this.getSpellAbility().addEffect(effect); - } public Showstopper (final Showstopper card) { diff --git a/Mage.Sets/src/mage/cards/s/ShrewdHatchling.java b/Mage.Sets/src/mage/cards/s/ShrewdHatchling.java index 6145a57fc7e..db9ce2c6952 100644 --- a/Mage.Sets/src/mage/cards/s/ShrewdHatchling.java +++ b/Mage.Sets/src/mage/cards/s/ShrewdHatchling.java @@ -5,14 +5,16 @@ import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.common.SpellCastAllTriggeredAbility; +import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.RestrictionEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.effects.common.counter.RemoveCounterSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; import mage.counters.CounterType; import mage.filter.FilterSpell; import mage.filter.predicate.mageobject.ColorPredicate; @@ -27,19 +29,14 @@ import java.util.UUID; */ public final class ShrewdHatchling extends CardImpl { - private static final FilterSpell filter = new FilterSpell("blue spell"); - private static final FilterSpell filter2 = new FilterSpell("red spell"); + private static final FilterSpell filter = new FilterSpell("a blue spell"); + private static final FilterSpell filter2 = new FilterSpell("a red spell"); static { - filter.add(TargetController.YOU.getControllerPredicate()); filter.add(new ColorPredicate(ObjectColor.BLUE)); - filter2.add(TargetController.YOU.getControllerPredicate()); filter2.add(new ColorPredicate(ObjectColor.RED)); } - private String rule = "Whenever you cast a blue spell, remove a -1/-1 counter from Shrewd Hatchling."; - private String rule2 = "Whenever you cast a red spell, remove a -1/-1 counter from Shrewd Hatchling."; - public ShrewdHatchling(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U/R}"); this.subtype.add(SubType.ELEMENTAL); @@ -48,18 +45,24 @@ public final class ShrewdHatchling extends CardImpl { this.toughness = new MageInt(6); // Shrewd Hatchling enters the battlefield with four -1/-1 counters on it. - this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.M1M1.createInstance(4)))); + this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect( + CounterType.M1M1.createInstance(4) + ), "with four -1/-1 counters on it")); // {UR}: Target creature can't block Shrewd Hatchling this turn. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ShrewdHatchlingEffect(), new ManaCostsImpl("{U/R}")); + Ability ability = new SimpleActivatedAbility(new ShrewdHatchlingEffect(), new ManaCostsImpl<>("{U/R}")); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); // Whenever you cast a blue spell, remove a -1/-1 counter from Shrewd Hatchling. - this.addAbility(new SpellCastAllTriggeredAbility(new RemoveCounterSourceEffect(CounterType.M1M1.createInstance()), filter, false, rule)); + this.addAbility(new SpellCastControllerTriggeredAbility( + new RemoveCounterSourceEffect(CounterType.M1M1.createInstance()), filter, false + )); // Whenever you cast a red spell, remove a -1/-1 counter from Shrewd Hatchling. - this.addAbility(new SpellCastAllTriggeredAbility(new RemoveCounterSourceEffect(CounterType.M1M1.createInstance()), filter2, false, rule2)); + this.addAbility(new SpellCastControllerTriggeredAbility( + new RemoveCounterSourceEffect(CounterType.M1M1.createInstance()), filter2, false + )); } diff --git a/Mage.Sets/src/mage/cards/s/ShrikeHarpy.java b/Mage.Sets/src/mage/cards/s/ShrikeHarpy.java index 663a594d09e..00033d1a63c 100644 --- a/Mage.Sets/src/mage/cards/s/ShrikeHarpy.java +++ b/Mage.Sets/src/mage/cards/s/ShrikeHarpy.java @@ -14,7 +14,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetOpponent; /** @@ -32,10 +32,12 @@ public final class ShrikeHarpy extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); + // Tribute 2
this.addAbility(new TributeAbility(2)); + // When Shrike Harpy enters the battlefield, if tribute wasn't paid, target opponent sacrifices a creature. - TriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new SacrificeEffect(new FilterCreaturePermanent("a creature"), 1, "target opponent"), false); + TriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new SacrificeEffect(StaticFilters.FILTER_PERMANENT_A_CREATURE, 1, "target opponent"), false); ability.addTarget(new TargetOpponent()); this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, TributeNotPaidCondition.instance, "When {this} enters the battlefield, if its tribute wasn't paid, target opponent sacrifices a creature.")); diff --git a/Mage.Sets/src/mage/cards/s/ShrillHowler.java b/Mage.Sets/src/mage/cards/s/ShrillHowler.java index bfd0b23bd22..b08fa88df95 100644 --- a/Mage.Sets/src/mage/cards/s/ShrillHowler.java +++ b/Mage.Sets/src/mage/cards/s/ShrillHowler.java @@ -28,7 +28,6 @@ public final class ShrillHowler extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(1); - this.transformable = true; this.secondSideCardClazz = mage.cards.h.HowlingChorus.class; // Creatures with power less than Shrill Howler's power can't block it. @@ -36,7 +35,7 @@ public final class ShrillHowler extends CardImpl { // {5}{G}: Transform Shrill Howler. this.addAbility(new TransformAbility()); - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new TransformSourceEffect(true), new ManaCostsImpl<>("{5}{G}"))); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new TransformSourceEffect(), new ManaCostsImpl<>("{5}{G}"))); } diff --git a/Mage.Sets/src/mage/cards/s/ShrineSteward.java b/Mage.Sets/src/mage/cards/s/ShrineSteward.java new file mode 100644 index 00000000000..4a8f451da0a --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/ShrineSteward.java @@ -0,0 +1,51 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.predicate.Predicates; +import mage.target.common.TargetCardInLibrary; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ShrineSteward extends CardImpl { + + private static final FilterCard filter = new FilterCard("an Aura or Shrine card"); + + static { + filter.add(Predicates.or( + SubType.AURA.getPredicate(), + SubType.SHRINE.getPredicate() + )); + } + + public ShrineSteward(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{5}"); + + this.subtype.add(SubType.CONSTRUCT); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // When Shrine Steward enters the battlefield, you may search your library for an Aura or Shrine card, reveal it, put it into your hand, then shuffle. + this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryPutInHandEffect( + new TargetCardInLibrary(filter), true, true + ), true)); + } + + private ShrineSteward(final ShrineSteward card) { + super(card); + } + + @Override + public ShrineSteward copy() { + return new ShrineSteward(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/Shrivel.java b/Mage.Sets/src/mage/cards/s/Shrivel.java index d602ae4faa1..ed5af12c77d 100644 --- a/Mage.Sets/src/mage/cards/s/Shrivel.java +++ b/Mage.Sets/src/mage/cards/s/Shrivel.java @@ -1,4 +1,3 @@ - package mage.cards.s; import java.util.UUID; @@ -7,7 +6,6 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.common.FilterCreaturePermanent; /** * @@ -19,7 +17,7 @@ public final class Shrivel extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{B}"); - this.getSpellAbility().addEffect(new BoostAllEffect(-1, -1, Duration.EndOfTurn, new FilterCreaturePermanent("All creatures"), false)); + this.getSpellAbility().addEffect(new BoostAllEffect(-1, -1, Duration.EndOfTurn)); } private Shrivel(final Shrivel card) { diff --git a/Mage.Sets/src/mage/cards/s/SickeningShoal.java b/Mage.Sets/src/mage/cards/s/SickeningShoal.java index 20a19d76676..fd781fba8c5 100644 --- a/Mage.Sets/src/mage/cards/s/SickeningShoal.java +++ b/Mage.Sets/src/mage/cards/s/SickeningShoal.java @@ -1,4 +1,3 @@ - package mage.cards.s; import mage.ObjectColor; @@ -14,8 +13,6 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; import mage.filter.common.FilterOwnedCard; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardIdPredicate; import mage.filter.predicate.mageobject.ColorPredicate; import mage.target.common.TargetCardInHand; import mage.target.common.TargetCreaturePermanent; @@ -23,25 +20,29 @@ import mage.target.common.TargetCreaturePermanent; import java.util.UUID; /** - * * @author LevelX2 */ public final class SickeningShoal extends CardImpl { + private static final FilterOwnedCard filter + = new FilterOwnedCard("a black card with mana value X from your hand"); + private static final DynamicValue xValue = new SignInversionDynamicValue(ExileFromHandCostCardConvertedMana.instance); + + static { + filter.add(new ColorPredicate(ObjectColor.BLACK)); + } + public SickeningShoal(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{X}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{X}{B}{B}"); this.subtype.add(SubType.ARCANE); - // You may exile a black card with converted mana cost X from your hand rather than pay Sickening Shoal's mana cost. - FilterOwnedCard filter = new FilterOwnedCard("a black card with mana value X from your hand"); - filter.add(new ColorPredicate(ObjectColor.BLACK)); - filter.add(Predicates.not(new CardIdPredicate(this.getId()))); // the exile cost can never be paid with the card itself - this.addAbility(new AlternativeCostSourceAbility(new ExileFromHandCost(new TargetCardInHand(filter), true))); + this.addAbility(new AlternativeCostSourceAbility(new ExileFromHandCost( + new TargetCardInHand(filter), true + ))); // Target creature gets -X/-X until end of turn. - DynamicValue x = new SignInversionDynamicValue(ExileFromHandCostCardConvertedMana.instance); - this.getSpellAbility().addEffect(new BoostTargetEffect(x, x, Duration.EndOfTurn, true)); + this.getSpellAbility().addEffect(new BoostTargetEffect(xValue, xValue, Duration.EndOfTurn, true)); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } diff --git a/Mage.Sets/src/mage/cards/s/SiegeModification.java b/Mage.Sets/src/mage/cards/s/SiegeModification.java index 14cf7531d19..89add34aa5e 100644 --- a/Mage.Sets/src/mage/cards/s/SiegeModification.java +++ b/Mage.Sets/src/mage/cards/s/SiegeModification.java @@ -23,7 +23,7 @@ import mage.target.TargetPermanent; */ public final class SiegeModification 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(), diff --git a/Mage.Sets/src/mage/cards/s/SigardaHeronsGrace.java b/Mage.Sets/src/mage/cards/s/SigardaHeronsGrace.java index 5f7673ef045..656252d0343 100644 --- a/Mage.Sets/src/mage/cards/s/SigardaHeronsGrace.java +++ b/Mage.Sets/src/mage/cards/s/SigardaHeronsGrace.java @@ -1,4 +1,3 @@ - package mage.cards.s; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/s/SigardasImprisonment.java b/Mage.Sets/src/mage/cards/s/SigardasImprisonment.java new file mode 100644 index 00000000000..0c640fae3fa --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SigardasImprisonment.java @@ -0,0 +1,57 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.ExileAttachedEffect; +import mage.abilities.effects.common.combat.CantAttackBlockAttachedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.game.permanent.token.BloodToken; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SigardasImprisonment extends CardImpl { + + public SigardasImprisonment(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + this.addAbility(new EnchantAbility(auraTarget.getTargetName())); + + // Enchanted creature can't attack or block. + this.addAbility(new SimpleStaticAbility(new CantAttackBlockAttachedEffect(AttachmentType.AURA))); + + // {4}{W}: Exile enchanted creature. Create a Blood token. + Ability ability = new SimpleActivatedAbility(new ExileAttachedEffect(), new ManaCostsImpl<>("{4}{W}")); + ability.addEffect(new CreateTokenEffect(new BloodToken())); + this.addAbility(ability); + } + + private SigardasImprisonment(final SigardasImprisonment card) { + super(card); + } + + @Override + public SigardasImprisonment copy() { + return new SigardasImprisonment(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SigardasSummons.java b/Mage.Sets/src/mage/cards/s/SigardasSummons.java new file mode 100644 index 00000000000..3b54c8be211 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SigardasSummons.java @@ -0,0 +1,93 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SigardasSummons extends CardImpl { + + public SigardasSummons(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{W}{W}"); + + // Creatures you control with +1/+1 counters on them have base power and toughness 4/4, have flying, and are Angels in addition to their other types. + this.addAbility(new SimpleStaticAbility(new SigardasSummonsEffect())); + } + + private SigardasSummons(final SigardasSummons card) { + super(card); + } + + @Override + public SigardasSummons copy() { + return new SigardasSummons(this); + } +} + +class SigardasSummonsEffect extends ContinuousEffectImpl { + + SigardasSummonsEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "creatures you control with +1/+1 counters on them have base power and toughness 4/4, " + + "have flying, and are Angels in addition to their other types"; + } + + private SigardasSummonsEffect(final SigardasSummonsEffect effect) { + super(effect); + } + + @Override + public SigardasSummonsEffect copy() { + return new SigardasSummonsEffect(this); + } + + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + for (Permanent permanent : game.getBattlefield().getActivePermanents( + StaticFilters.FILTER_CONTROLLED_CREATURE_P1P1, source.getControllerId(), source.getSourceId(), game + )) { + switch (layer) { + case TypeChangingEffects_4: + permanent.addSubType(game, SubType.ANGEL); + return true; + case AbilityAddingRemovingEffects_6: + permanent.addAbility(FlyingAbility.getInstance(), source.getSourceId(), game); + return true; + case PTChangingEffects_7: + if (sublayer == SubLayer.SetPT_7b) { + permanent.getPower().setValue(4); + permanent.getToughness().setValue(4); + return true; + } + } + } + return false; + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean hasLayer(Layer layer) { + switch (layer) { + case TypeChangingEffects_4: + case AbilityAddingRemovingEffects_6: + case PTChangingEffects_7: + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SigardianPaladin.java b/Mage.Sets/src/mage/cards/s/SigardianPaladin.java new file mode 100644 index 00000000000..88ecbb9bebd --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SigardianPaladin.java @@ -0,0 +1,132 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.abilities.keyword.LifelinkAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.WatcherScope; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; +import mage.watchers.Watcher; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SigardianPaladin extends CardImpl { + + public SigardianPaladin(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.KNIGHT); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // As long as you've put one or more +1/+1 counters on a creature this turn, Sigardian Paladin has trample and lifelink. + Ability ability = new SimpleStaticAbility(new ConditionalContinuousEffect( + new GainAbilitySourceEffect(TrampleAbility.getInstance(), Duration.WhileOnBattlefield), + SigardianPaladinCondition.instance, "as long as you've put " + + "one or more +1/+1 counters on a creature this turn, {this} has trample" + )); + ability.addEffect(new ConditionalContinuousEffect( + new GainAbilitySourceEffect( + LifelinkAbility.getInstance(), Duration.WhileOnBattlefield + ), SigardianPaladinCondition.instance, "and lifelink" + )); + this.addAbility(ability.addHint(SigardianPaladinCondition.getHint()), new SigardianPaladinWatcher()); + + // {1}{G}{W}: Target creature you control with a +1/+1 counter on it gains trample and lifelink until end of turn. + ability = new SimpleActivatedAbility( + new GainAbilityTargetEffect(TrampleAbility.getInstance()) + .setText("target creature you control with a +1/+1 counter on it gains trample"), + new ManaCostsImpl<>("{1}{G}{W}") + ); + ability.addEffect(new GainAbilityTargetEffect( + LifelinkAbility.getInstance() + ).setText("and lifelink until end of turn")); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CONTROLLED_CREATURE_P1P1)); + this.addAbility(ability); + } + + private SigardianPaladin(final SigardianPaladin card) { + super(card); + } + + @Override + public SigardianPaladin copy() { + return new SigardianPaladin(this); + } +} + +enum SigardianPaladinCondition implements Condition { + instance; + private static final Hint hint = new ConditionHint( + instance, "You've put one or more +1/+1 counters on a creature this turn" + ); + + @Override + public boolean apply(Game game, Ability source) { + return SigardianPaladinWatcher.checkPlayer(source.getControllerId(), game); + } + + public static Hint getHint() { + return hint; + } +} + +class SigardianPaladinWatcher extends Watcher { + + private final Set playerSet = new HashSet<>(); + + SigardianPaladinWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() != GameEvent.EventType.COUNTER_ADDED + || playerSet.contains(event.getPlayerId()) + || !event.getData().equals(CounterType.P1P1.getName())) { + return; + } + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent != null && permanent.isCreature(game)) { + playerSet.add(event.getPlayerId()); + } + } + + @Override + public void reset() { + super.reset(); + playerSet.clear(); + } + + static boolean checkPlayer(UUID playerId, Game game) { + return game.getState() + .getWatcher(SigardianPaladinWatcher.class) + .playerSet + .contains(playerId); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SigilCaptain.java b/Mage.Sets/src/mage/cards/s/SigilCaptain.java index d3de9ccc49b..01922dd7443 100644 --- a/Mage.Sets/src/mage/cards/s/SigilCaptain.java +++ b/Mage.Sets/src/mage/cards/s/SigilCaptain.java @@ -1,4 +1,3 @@ - package mage.cards.s; import java.util.UUID; @@ -14,7 +13,6 @@ import mage.constants.Zone; import mage.counters.CounterType; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; @@ -25,12 +23,10 @@ import mage.target.targetpointer.FixedTarget; public final class SigilCaptain extends CardImpl { public SigilCaptain(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{G}{W}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}{W}{W}"); this.subtype.add(SubType.RHINO); this.subtype.add(SubType.SOLDIER); - - this.power = new MageInt(3); this.toughness = new MageInt(3); @@ -73,7 +69,7 @@ class SigilCaptainTriggeredAbility extends TriggeredAbilityImpl { && permanent.getPower().getValue() == 1 && permanent.getToughness().getValue() == 1) { for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getTargetId())); + effect.setTargetPointer(new FixedTarget(event.getTargetId(), game)); } return true; } diff --git a/Mage.Sets/src/mage/cards/s/SigilOfValor.java b/Mage.Sets/src/mage/cards/s/SigilOfValor.java index e6102aab637..c166c545d9a 100644 --- a/Mage.Sets/src/mage/cards/s/SigilOfValor.java +++ b/Mage.Sets/src/mage/cards/s/SigilOfValor.java @@ -1,4 +1,3 @@ - package mage.cards.s; import java.util.UUID; @@ -33,7 +32,7 @@ import mage.target.targetpointer.FixedTarget; public final class SigilOfValor extends CardImpl { public SigilOfValor(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{2}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); this.subtype.add(SubType.EQUIPMENT); // Whenever equipped creature attacks alone, it gets +1/+1 until end of turn for each other creature you control. @@ -81,7 +80,7 @@ class SigilOfValorTriggeredAbility extends TriggeredAbilityImpl { UUID attackerId = game.getCombat().getAttackers().get(0); if (equipment != null && equipment.isAttachedTo(attackerId)) { - this.getEffects().get(0).setTargetPointer(new FixedTarget(attackerId)); + this.getEffects().get(0).setTargetPointer(new FixedTarget(attackerId, game)); return true; } } diff --git a/Mage.Sets/src/mage/cards/s/SilentBladeOni.java b/Mage.Sets/src/mage/cards/s/SilentBladeOni.java index f6441df1d54..a9a95eec106 100644 --- a/Mage.Sets/src/mage/cards/s/SilentBladeOni.java +++ b/Mage.Sets/src/mage/cards/s/SilentBladeOni.java @@ -33,7 +33,7 @@ public final class SilentBladeOni extends CardImpl { this.toughness = new MageInt(5); // Ninjutsu {4}{U}{B} - this.addAbility(new NinjutsuAbility(new ManaCostsImpl("{4}{U}{B}"))); + this.addAbility(new NinjutsuAbility("{4}{U}{B}")); // Whenever Silent-Blade Oni deals combat damage to a player, look at that player's hand. // You may cast a nonland card in it without paying that card's mana cost. diff --git a/Mage.Sets/src/mage/cards/s/SilhanaLedgewalker.java b/Mage.Sets/src/mage/cards/s/SilhanaLedgewalker.java index 4735f88c013..b496bc56262 100644 --- a/Mage.Sets/src/mage/cards/s/SilhanaLedgewalker.java +++ b/Mage.Sets/src/mage/cards/s/SilhanaLedgewalker.java @@ -1,9 +1,8 @@ package mage.cards.s; import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.RestrictionEffect; +import mage.abilities.common.SimpleEvasionAbility; +import mage.abilities.effects.common.combat.CantBeBlockedByCreaturesSourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.HexproofAbility; import mage.cards.CardImpl; @@ -11,9 +10,9 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.AbilityPredicate; import java.util.UUID; @@ -22,6 +21,12 @@ import java.util.UUID; */ public final class SilhanaLedgewalker extends CardImpl { + private static final FilterCreaturePermanent onlyFlyingCreatures = new FilterCreaturePermanent("except by creatures with flying"); + + static { + onlyFlyingCreatures.add(Predicates.not(new AbilityPredicate(FlyingAbility.class))); + } + public SilhanaLedgewalker(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); this.subtype.add(SubType.ELF); @@ -34,7 +39,7 @@ public final class SilhanaLedgewalker extends CardImpl { this.addAbility(HexproofAbility.getInstance()); // Silhana Ledgewalker can't be blocked except by creatures with flying. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SilhanaLedgewalkerEffect())); + this.addAbility(new SimpleEvasionAbility(new CantBeBlockedByCreaturesSourceEffect(onlyFlyingCreatures, Duration.WhileOnBattlefield))); } private SilhanaLedgewalker(final SilhanaLedgewalker card) { @@ -45,32 +50,4 @@ public final class SilhanaLedgewalker extends CardImpl { public SilhanaLedgewalker copy() { return new SilhanaLedgewalker(this); } - } - -class SilhanaLedgewalkerEffect extends RestrictionEffect { - - public SilhanaLedgewalkerEffect() { - super(Duration.WhileOnBattlefield); - staticText = "{this} can't be blocked except by creatures with flying"; - } - - public SilhanaLedgewalkerEffect(final SilhanaLedgewalkerEffect effect) { - super(effect); - } - - @Override - public boolean applies(Permanent permanent, Ability source, Game game) { - return source.getSourceId().equals(permanent.getId()); - } - - @Override - public boolean canBeBlocked(Permanent attacker, Permanent blocker, Ability source, Game game, boolean canUseChooseDialogs) { - return blocker.getAbilities().contains(FlyingAbility.getInstance()); - } - - @Override - public SilhanaLedgewalkerEffect copy() { - return new SilhanaLedgewalkerEffect(this); - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/s/Silkguard.java b/Mage.Sets/src/mage/cards/s/Silkguard.java new file mode 100644 index 00000000000..c22d3a855b7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/Silkguard.java @@ -0,0 +1,74 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.HexproofAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.ModifiedPredicate; +import mage.game.Game; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.targetadjustment.TargetAdjuster; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Silkguard extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledPermanent("Auras, Equipment, and modified creatures"); + + static { + filter.add(Predicates.or( + SubType.AURA.getPredicate(), + SubType.EQUIPMENT.getPredicate(), + Predicates.and( + ModifiedPredicate.instance, + CardType.CREATURE.getPredicate() + ) + )); + } + + public Silkguard(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{X}{G}"); + + // Put a +1/+1 counter on each of up to X target creatures you control. + this.getSpellAbility().addEffect(new AddCountersTargetEffect(CounterType.P1P1.createInstance()) + .setText("put a +1/+1 counter on each of up to X target creatures you control")); + this.getSpellAbility().setTargetAdjuster(SilkguardAdjuster.instance); + + // Auras, Equipment, and modified creatures you control gain hexproof until end of turn. + this.getSpellAbility().addEffect(new GainAbilityControlledEffect( + HexproofAbility.getInstance(), Duration.EndOfTurn, filter + ).concatBy("
")); + } + + private Silkguard(final Silkguard card) { + super(card); + } + + @Override + public Silkguard copy() { + return new Silkguard(this); + } +} + +enum SilkguardAdjuster implements TargetAdjuster { + instance; + + @Override + public void adjustTargets(Ability ability, Game game) { + ability.getTargets().clear(); + ability.addTarget(new TargetControlledCreaturePermanent(0, ability.getManaCostsToPay().getX())); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SilkweaverElite.java b/Mage.Sets/src/mage/cards/s/SilkweaverElite.java index 6995058b501..df1a5fa6aad 100644 --- a/Mage.Sets/src/mage/cards/s/SilkweaverElite.java +++ b/Mage.Sets/src/mage/cards/s/SilkweaverElite.java @@ -1,9 +1,6 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.RevoltCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; @@ -16,6 +13,8 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.watchers.common.RevoltWatcher; +import java.util.UUID; + /** * @author JRHerlehy */ @@ -33,14 +32,11 @@ public final class SilkweaverElite extends CardImpl { this.addAbility(ReachAbility.getInstance()); // Revolt — When Silkweaver Elite enters the battlefield, if a permanent you controlled left the battlefield this turn, draw a card. - Ability ability = new ConditionalInterveningIfTriggeredAbility(new EntersBattlefieldTriggeredAbility( - new DrawCardSourceControllerEffect(1), false), RevoltCondition.instance, - "Revolt — When {this} enters the battlefield, if a permanent you controlled left" - + " the battlefield this turn, draw a card."); - ability.setAbilityWord(AbilityWord.REVOLT); - ability.addWatcher(new RevoltWatcher()); - this.addAbility(ability); - + this.addAbility(new ConditionalInterveningIfTriggeredAbility( + new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(1), false), + RevoltCondition.instance, "When {this} enters the battlefield, " + + "if a permanent you controlled left the battlefield this turn, draw a card." + ).setAbilityWord(AbilityWord.REVOLT), new RevoltWatcher()); } private SilkweaverElite(final SilkweaverElite card) { diff --git a/Mage.Sets/src/mage/cards/s/SilumgarsCommand.java b/Mage.Sets/src/mage/cards/s/SilumgarsCommand.java index 82a4c341047..02f04a0d2b4 100644 --- a/Mage.Sets/src/mage/cards/s/SilumgarsCommand.java +++ b/Mage.Sets/src/mage/cards/s/SilumgarsCommand.java @@ -12,8 +12,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.filter.FilterPermanent; -import mage.filter.FilterSpell; -import mage.filter.predicate.Predicates; +import mage.filter.StaticFilters; import mage.target.TargetPermanent; import mage.target.TargetSpell; import mage.target.common.TargetCreaturePermanent; @@ -23,12 +22,9 @@ import mage.target.common.TargetCreaturePermanent; * @author fireshoes */ public final class SilumgarsCommand extends CardImpl { - - private static final FilterSpell filter = new FilterSpell("noncreature spell"); private static final FilterPermanent filter2 = new FilterPermanent("planeswalker"); static { - filter.add(Predicates.not(CardType.CREATURE.getPredicate())); filter2.add(CardType.PLANESWALKER.getPredicate()); } @@ -41,7 +37,7 @@ public final class SilumgarsCommand extends CardImpl { // Counter target noncreature spell; this.getSpellAbility().getEffects().add(new CounterTargetEffect()); - this.getSpellAbility().getTargets().add(new TargetSpell(filter)); + this.getSpellAbility().getTargets().add(new TargetSpell(StaticFilters.FILTER_SPELL_NON_CREATURE)); // or Return target permanent to its owner's hand; Mode mode = new Mode(); diff --git a/Mage.Sets/src/mage/cards/s/SilvarDevourerOfTheFree.java b/Mage.Sets/src/mage/cards/s/SilvarDevourerOfTheFree.java index 6cd68a89ba3..da9a18788a3 100644 --- a/Mage.Sets/src/mage/cards/s/SilvarDevourerOfTheFree.java +++ b/Mage.Sets/src/mage/cards/s/SilvarDevourerOfTheFree.java @@ -42,7 +42,7 @@ public final class SilvarDevourerOfTheFree extends CardImpl { this.addAbility(new PartnerWithAbility("Trynn, Champion of Freedom")); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // Sacrifice a Human: Put a +1/+1 counter on Silvar, Devourer of the Free. It gains indestructible until end of turn. Ability ability = new SimpleActivatedAbility( diff --git a/Mage.Sets/src/mage/cards/s/SilverFurMaster.java b/Mage.Sets/src/mage/cards/s/SilverFurMaster.java new file mode 100644 index 00000000000..b21dc4ce87e --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SilverFurMaster.java @@ -0,0 +1,62 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.abilities.effects.common.cost.AbilitiesCostReductionControllerEffect; +import mage.abilities.keyword.NinjutsuAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; + +import java.util.UUID; + +/** + * @author Addictiveme + */ +public final class SilverFurMaster extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Ninja and Rogue creatures"); + + static { + filter.add(Predicates.or( + SubType.NINJA.getPredicate(), + SubType.ROGUE.getPredicate() + )); + } + + public SilverFurMaster(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{U}{B}"); + + this.subtype.add(SubType.RAT); + this.subtype.add(SubType.NINJA); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Ninjutsu {U}{B} + this.addAbility(new NinjutsuAbility("{U}{B}")); + + // Ninjutsu abilities you activate cost {1} less to activate. + this.addAbility(new SimpleStaticAbility(new AbilitiesCostReductionControllerEffect( + NinjutsuAbility.class, "Ninjutsu" + ).setText("ninjutsu abilities you activate cost {1} less to activate"))); + + // Other Ninja and Rogue creatures you control get +1/+1 + this.addAbility(new SimpleStaticAbility(new BoostControlledEffect( + 1, 1, Duration.WhileOnBattlefield, filter, true + ))); + } + + private SilverFurMaster(final SilverFurMaster card) { + super(card); + } + + @Override + public SilverFurMaster copy() { + return new SilverFurMaster(this); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/s/SilverfurPartisan.java b/Mage.Sets/src/mage/cards/s/SilverfurPartisan.java index 17745fbfe4f..97859ef9b38 100644 --- a/Mage.Sets/src/mage/cards/s/SilverfurPartisan.java +++ b/Mage.Sets/src/mage/cards/s/SilverfurPartisan.java @@ -83,7 +83,7 @@ class CreaturesYouControlBecomesTargetTriggeredAbility extends TriggeredAbilityI || object.isSorcery(game)) { if (getTargets().isEmpty()) { for (Effect effect : getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getTargetId())); + effect.setTargetPointer(new FixedTarget(event.getTargetId(), game)); } } return true; diff --git a/Mage.Sets/src/mage/cards/s/SilverpeltWerewolf.java b/Mage.Sets/src/mage/cards/s/SilverpeltWerewolf.java index e98bee109b9..8bf1157c998 100644 --- a/Mage.Sets/src/mage/cards/s/SilverpeltWerewolf.java +++ b/Mage.Sets/src/mage/cards/s/SilverpeltWerewolf.java @@ -24,7 +24,6 @@ public final class SilverpeltWerewolf extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(5); - this.transformable = true; this.nightCard = true; // Whenever Silverpelt Werewolf deals combat damage to a player, draw a card. diff --git a/Mage.Sets/src/mage/cards/s/SilverquillCampus.java b/Mage.Sets/src/mage/cards/s/SilverquillCampus.java index 39c59903a02..714838c866a 100644 --- a/Mage.Sets/src/mage/cards/s/SilverquillCampus.java +++ b/Mage.Sets/src/mage/cards/s/SilverquillCampus.java @@ -30,7 +30,7 @@ public final class SilverquillCampus extends CardImpl { this.addAbility(new BlackManaAbility()); // {4}, {T}: Scry 1. - Ability ability = new SimpleActivatedAbility(new ScryEffect(1), new GenericManaCost(4)); + Ability ability = new SimpleActivatedAbility(new ScryEffect(1, false), new GenericManaCost(4)); ability.addCost(new TapSourceCost()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SimianSling.java b/Mage.Sets/src/mage/cards/s/SimianSling.java new file mode 100644 index 00000000000..a3bcb8ac777 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SimianSling.java @@ -0,0 +1,119 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostEquippedEffect; +import mage.abilities.keyword.ReconfigureAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SimianSling extends CardImpl { + + public SimianSling(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{R}"); + + this.subtype.add(SubType.EQUIPMENT); + this.subtype.add(SubType.MONKEY); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Equipped creature gets +1/+1. + this.addAbility(new SimpleStaticAbility(new BoostEquippedEffect(1, 1))); + + // Whenever Simian Sling or equipped creature becomes blocked, it deals 1 damage to defending player. + this.addAbility(new SimianSlingTriggeredAbility()); + + // Reconfigure {2} + this.addAbility(new ReconfigureAbility("{2}")); + } + + private SimianSling(final SimianSling card) { + super(card); + } + + @Override + public SimianSling copy() { + return new SimianSling(this); + } +} + +class SimianSlingTriggeredAbility extends TriggeredAbilityImpl { + + SimianSlingTriggeredAbility() { + super(Zone.BATTLEFIELD, new SimianSlingEffect()); + } + + private SimianSlingTriggeredAbility(final SimianSlingTriggeredAbility ability) { + super(ability); + } + + @Override + public SimianSlingTriggeredAbility copy() { + return new SimianSlingTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.CREATURE_BLOCKED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (!event.getTargetId().equals(getSourceId())) { + Permanent permanent = getSourcePermanentOrLKI(game); + if (permanent == null || !event.getTargetId().equals(permanent.getAttachedTo())) { + return false; + } + } + getEffects().setValue("attacker", event.getTargetId()); + getEffects().setTargetPointer(new FixedTarget(game.getCombat().getDefendingPlayerId(event.getTargetId(), game))); + return true; + } + + @Override + public String getRule() { + return "Whenever Simian Sling or equipped creature becomes blocked, it deals 1 damage to defending player."; + } +} + +class SimianSlingEffect extends OneShotEffect { + + SimianSlingEffect() { + super(Outcome.Benefit); + } + + private SimianSlingEffect(final SimianSlingEffect effect) { + super(effect); + } + + @Override + public SimianSlingEffect copy() { + return new SimianSlingEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + UUID attacker = (UUID) getValue("attacker"); + Player player = game.getPlayer(getTargetPointer().getFirst(game, source)); + return attacker != null + && player != null + && player.damage(1, attacker, source, game) > 0; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SimicBasilisk.java b/Mage.Sets/src/mage/cards/s/SimicBasilisk.java index 28dd393e02c..89fb8bc9549 100644 --- a/Mage.Sets/src/mage/cards/s/SimicBasilisk.java +++ b/Mage.Sets/src/mage/cards/s/SimicBasilisk.java @@ -19,8 +19,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Zone; -import mage.counters.CounterType; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; /** @@ -28,11 +27,6 @@ import mage.target.common.TargetCreaturePermanent; * @author JotaPeRL */ public final class SimicBasilisk extends CardImpl { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature with a +1/+1 counter on it"); - static { - filter.add(CounterType.P1P1.getPredicate()); - } public SimicBasilisk(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{G}{G}"); @@ -49,7 +43,7 @@ public final class SimicBasilisk extends CardImpl { new AtTheEndOfCombatDelayedTriggeredAbility(new DestroyTargetEffect()), true); effect.setText("destroy that creature at end of combat"); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainAbilityTargetEffect(new DealsDamageToACreatureTriggeredAbility(effect, true, false, true), Duration.EndOfTurn), new ManaCostsImpl("{1}{G}")); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_A_CREATURE_P1P1)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SinProdder.java b/Mage.Sets/src/mage/cards/s/SinProdder.java index 965e931652d..f62db39ba0f 100644 --- a/Mage.Sets/src/mage/cards/s/SinProdder.java +++ b/Mage.Sets/src/mage/cards/s/SinProdder.java @@ -27,7 +27,7 @@ public final class SinProdder extends CardImpl { this.toughness = new MageInt(2); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // At the beginning of your upkeep, reveal the top card of your library. Any opponent may have you put that card into your graveyard. If a player does, // Sin Prodder deals damage to that player equal to that card's converted mana cost. Otherwise, put that card into your hand. diff --git a/Mage.Sets/src/mage/cards/s/SinisterSabotage.java b/Mage.Sets/src/mage/cards/s/SinisterSabotage.java index 03835c6bc63..76548eefe43 100644 --- a/Mage.Sets/src/mage/cards/s/SinisterSabotage.java +++ b/Mage.Sets/src/mage/cards/s/SinisterSabotage.java @@ -22,7 +22,7 @@ public final class SinisterSabotage extends CardImpl { this.getSpellAbility().addTarget(new TargetSpell()); // Surveil 1. - this.getSpellAbility().addEffect(new SurveilEffect(1)); + this.getSpellAbility().addEffect(new SurveilEffect(1).concatBy("
")); } private SinisterSabotage(final SinisterSabotage card) { diff --git a/Mage.Sets/src/mage/cards/s/SinisterWaltz.java b/Mage.Sets/src/mage/cards/s/SinisterWaltz.java new file mode 100644 index 00000000000..d06e4e09b8e --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SinisterWaltz.java @@ -0,0 +1,78 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.*; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SinisterWaltz extends CardImpl { + + public SinisterWaltz(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{B}{R}"); + + // Choose three target creature cards in your graveyard. Return two of them at random to the battlefield and put the other on the bottom of your library. + this.getSpellAbility().addEffect(new SinisterWaltzEffect()); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard( + 0, 3, StaticFilters.FILTER_CARD_CREATURES + )); + } + + private SinisterWaltz(final SinisterWaltz card) { + super(card); + } + + @Override + public SinisterWaltz copy() { + return new SinisterWaltz(this); + } +} + +class SinisterWaltzEffect extends OneShotEffect { + + SinisterWaltzEffect() { + super(Outcome.Benefit); + staticText = "choose three target creature cards in your graveyard. Return two of them " + + "at random to the battlefield and put the other on the bottom of your library"; + } + + private SinisterWaltzEffect(final SinisterWaltzEffect effect) { + super(effect); + } + + @Override + public SinisterWaltzEffect copy() { + return new SinisterWaltzEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Cards cards = new CardsImpl(getTargetPointer().getTargets(game, source)); + Card card; + if (cards.size() > 2) { + card = cards.getRandom(game); + cards.remove(card); + } else { + card = null; + } + player.moveCards(cards, Zone.BATTLEFIELD, source, game); + if (card != null) { + player.putCardsOnBottomOfLibrary(card, game, source, false); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SinnersJudgment.java b/Mage.Sets/src/mage/cards/s/SinnersJudgment.java new file mode 100644 index 00000000000..25d59e150e9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SinnersJudgment.java @@ -0,0 +1,96 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.PutIntoGraveFromAnywhereSourceAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.ExileSourceEffect; +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.constants.TargetController; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPlayer; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SinnersJudgment extends CardImpl { + + public SinnersJudgment(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, ""); + + this.subtype.add(SubType.AURA); + this.subtype.add(SubType.CURSE); + this.color.setWhite(true); + this.nightCard = true; + + // Enchant player + TargetPlayer auraTarget = new TargetPlayer(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // At the beginning of your upkeep, put a judgment counter on Sinner's Judgment. Then if there are three or more judgment counters on it, enchanted player loses the game. + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new SinnersJudgmentEffect(), TargetController.YOU, false + )); + + // If Sinner's Judgment would be put into a graveyard from anywhere, exile it instead. + this.addAbility(new PutIntoGraveFromAnywhereSourceAbility(new ExileSourceEffect().setText("exile it instead"))); + } + + private SinnersJudgment(final SinnersJudgment card) { + super(card); + } + + @Override + public SinnersJudgment copy() { + return new SinnersJudgment(this); + } +} + +class SinnersJudgmentEffect extends OneShotEffect { + + SinnersJudgmentEffect() { + super(Outcome.Benefit); + staticText = "put a judgment counter on {this}. Then if there are three " + + "or more judgment counters on it, enchanted player loses the game"; + } + + private SinnersJudgmentEffect(final SinnersJudgmentEffect effect) { + super(effect); + } + + @Override + public SinnersJudgmentEffect copy() { + return new SinnersJudgmentEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (permanent == null) { + return false; + } + permanent.addCounters(CounterType.JUDGMENT.createInstance(), source, game); + if (permanent.getCounters(game).getCount(CounterType.JUDGMENT) < 3) { + return true; + } + Player player = game.getPlayer(permanent.getAttachedTo()); + if (player != null) { + player.lost(game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SinuousVermin.java b/Mage.Sets/src/mage/cards/s/SinuousVermin.java index 9c54114fa7e..72b64382bd2 100644 --- a/Mage.Sets/src/mage/cards/s/SinuousVermin.java +++ b/Mage.Sets/src/mage/cards/s/SinuousVermin.java @@ -38,7 +38,8 @@ public final class SinuousVermin extends CardImpl { Zone.BATTLEFIELD, new ConditionalContinuousEffect(new GainAbilitySourceEffect(new MenaceAbility(), Duration.WhileOnBattlefield), MonstrousCondition.instance, - "As long as {this} is monstrous, it has menace")); + "As long as {this} is monstrous, it has menace. " + + "(It can't be blocked except by two or more creatures.)")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SionaCaptainOfThePyleas.java b/Mage.Sets/src/mage/cards/s/SionaCaptainOfThePyleas.java index f9b6a388f14..aa0f10339fa 100644 --- a/Mage.Sets/src/mage/cards/s/SionaCaptainOfThePyleas.java +++ b/Mage.Sets/src/mage/cards/s/SionaCaptainOfThePyleas.java @@ -95,7 +95,7 @@ class SionaCaptainOfThePyleasAbility extends TriggeredAbilityImpl { @Override public String getRule() { return "Whenever an Aura you control becomes attached to a creature you control, " + - "create a 1/1 white Human Soldier creature token"; + "create a 1/1 white Human Soldier creature token."; } @Override diff --git a/Mage.Sets/src/mage/cards/s/SireOfTheStorm.java b/Mage.Sets/src/mage/cards/s/SireOfTheStorm.java index 4da090f073a..5f0d1e4df7d 100644 --- a/Mage.Sets/src/mage/cards/s/SireOfTheStorm.java +++ b/Mage.Sets/src/mage/cards/s/SireOfTheStorm.java @@ -26,7 +26,7 @@ public final class SireOfTheStorm extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(3); this.addAbility(FlyingAbility.getInstance()); - this.addAbility(new SpellCastControllerTriggeredAbility(new DrawCardSourceControllerEffect(1), StaticFilters.SPIRIT_OR_ARCANE_CARD, true)); + this.addAbility(new SpellCastControllerTriggeredAbility(new DrawCardSourceControllerEffect(1), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true)); } private SireOfTheStorm(final SireOfTheStorm card) { diff --git a/Mage.Sets/src/mage/cards/s/SistersOfStoneDeath.java b/Mage.Sets/src/mage/cards/s/SistersOfStoneDeath.java index 286a1218b15..88f3775b82a 100644 --- a/Mage.Sets/src/mage/cards/s/SistersOfStoneDeath.java +++ b/Mage.Sets/src/mage/cards/s/SistersOfStoneDeath.java @@ -1,13 +1,11 @@ package mage.cards.s; -import java.util.LinkedList; -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.OneShotEffect; -import mage.abilities.effects.common.ExileTargetEffect; +import mage.abilities.effects.common.ExileTargetForSourceEffect; import mage.abilities.effects.common.combat.MustBeBlockedByTargetSourceEffect; import mage.cards.Card; import mage.cards.CardImpl; @@ -24,14 +22,16 @@ import mage.game.Game; import mage.players.Player; import mage.target.TargetCard; import mage.target.common.TargetCreaturePermanent; +import mage.util.CardUtil; + +import java.util.LinkedList; +import java.util.UUID; /** * @author jeffwadsworth */ public final class SistersOfStoneDeath extends CardImpl { - private UUID exileId = UUID.randomUUID(); - public SistersOfStoneDeath(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}{B}{G}{G}"); this.addSuperType(SuperType.LEGENDARY); @@ -46,7 +46,7 @@ public final class SistersOfStoneDeath extends CardImpl { this.addAbility(ability); // {B}{G}: Exile target creature blocking or blocked by Sisters of Stone Death. - Ability ability2 = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileTargetEffect(exileId, this.getIdName()), new ManaCostsImpl("{B}{G}")); + Ability ability2 = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileTargetForSourceEffect(), new ManaCostsImpl("{B}{G}")); FilterCreaturePermanent filter = new FilterCreaturePermanent("creature blocking or blocked by Sisters of Stone Death"); filter.add(Predicates.or(new BlockedByIdPredicate(this.getId()), new BlockingAttackerIdPredicate(this.getId()))); @@ -54,7 +54,7 @@ public final class SistersOfStoneDeath extends CardImpl { this.addAbility(ability2); // {2}{B}: Put a creature card exiled with Sisters of Stone Death onto the battlefield under your control. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new SistersOfStoneDeathEffect(exileId), new ManaCostsImpl("{2}{B}"))); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new SistersOfStoneDeathEffect(), new ManaCostsImpl("{2}{B}"))); } @@ -70,17 +70,13 @@ public final class SistersOfStoneDeath extends CardImpl { class SistersOfStoneDeathEffect extends OneShotEffect { - private final UUID exileId; - - public SistersOfStoneDeathEffect(UUID exileId) { + public SistersOfStoneDeathEffect() { super(Outcome.PutCreatureInPlay); - this.exileId = exileId; staticText = "Put a creature card exiled with {this} onto the battlefield under your control"; } public SistersOfStoneDeathEffect(final SistersOfStoneDeathEffect effect) { super(effect); - this.exileId = effect.exileId; } @Override @@ -89,7 +85,7 @@ class SistersOfStoneDeathEffect extends OneShotEffect { TargetCard target = new TargetCard(Zone.EXILED, StaticFilters.FILTER_CARD_CREATURE); Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - ExileZone exile = game.getExile().getExileZone(exileId); + ExileZone exile = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source)); if (exile != null) { LinkedList cards = new LinkedList<>(exile); for (UUID cardId : cards) { diff --git a/Mage.Sets/src/mage/cards/s/SithAssassin.java b/Mage.Sets/src/mage/cards/s/SithAssassin.java index b87dd578ba9..fbd65985e49 100644 --- a/Mage.Sets/src/mage/cards/s/SithAssassin.java +++ b/Mage.Sets/src/mage/cards/s/SithAssassin.java @@ -1,9 +1,7 @@ - package mage.cards.s; import java.util.UUID; import mage.MageInt; -import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.HateCondition; @@ -13,9 +11,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -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 mage.watchers.common.LifeLossOtherFromCombatWatcher; @@ -25,12 +21,6 @@ import mage.watchers.common.LifeLossOtherFromCombatWatcher; */ public final class SithAssassin extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public SithAssassin(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); this.subtype.add(SubType.PUREBLOOD); @@ -43,7 +33,7 @@ public final class SithAssassin extends CardImpl { new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect(), true), HateCondition.instance, "Hate — When {this} enters the battlefield, if an opponent lost life from a source other than combat damage this turn, you may destroy target nonblack creature."); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); this.addAbility(ability, new LifeLossOtherFromCombatWatcher()); } diff --git a/Mage.Sets/src/mage/cards/s/SkatewingSpy.java b/Mage.Sets/src/mage/cards/s/SkatewingSpy.java index bcd52cbc78b..e4605b1908c 100644 --- a/Mage.Sets/src/mage/cards/s/SkatewingSpy.java +++ b/Mage.Sets/src/mage/cards/s/SkatewingSpy.java @@ -11,9 +11,7 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; import mage.constants.Zone; -import mage.counters.CounterType; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; import java.util.UUID; @@ -22,12 +20,6 @@ import java.util.UUID; */ public final class SkatewingSpy extends CardImpl { - private static final FilterPermanent filter = new FilterControlledCreaturePermanent(); - - static { - filter.add(CounterType.P1P1.getPredicate()); - } - public SkatewingSpy(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); @@ -46,10 +38,9 @@ public final class SkatewingSpy extends CardImpl { new GainAbilityAllEffect( FlyingAbility.getInstance(), Duration.WhileOnBattlefield, - filter, "Each creature you control " + - "with a +1/+1 counter on it has flying" + StaticFilters.FILTER_EACH_CONTROLLED_CREATURE_P1P1) ) - )); + ); } private SkatewingSpy(final SkatewingSpy card) { diff --git a/Mage.Sets/src/mage/cards/s/SkeletalScrying.java b/Mage.Sets/src/mage/cards/s/SkeletalScrying.java index fe3181c71b5..9996b0e404e 100644 --- a/Mage.Sets/src/mage/cards/s/SkeletalScrying.java +++ b/Mage.Sets/src/mage/cards/s/SkeletalScrying.java @@ -12,7 +12,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Zone; -import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.game.Game; import mage.target.common.TargetCardInYourGraveyard; @@ -58,13 +58,12 @@ public final class SkeletalScrying extends CardImpl { enum SkeletalScryingAdjuster implements CostAdjuster { instance; - private static final FilterCard filter = new FilterCard("cards from your graveyard"); @Override public void adjustCosts(Ability ability, Game game) { int xValue = ability.getManaCostsToPay().getX(); if (xValue > 0) { - ability.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard(xValue, xValue, filter))); + ability.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard(xValue, xValue, StaticFilters.FILTER_CARDS_FROM_YOUR_GRAVEYARD))); } } } diff --git a/Mage.Sets/src/mage/cards/s/SkinInvasion.java b/Mage.Sets/src/mage/cards/s/SkinInvasion.java index 570fc75c936..d41fd9d2657 100644 --- a/Mage.Sets/src/mage/cards/s/SkinInvasion.java +++ b/Mage.Sets/src/mage/cards/s/SkinInvasion.java @@ -34,7 +34,6 @@ public final class SkinInvasion extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{R}"); this.subtype.add(SubType.AURA); - this.transformable = true; this.secondSideCardClazz = SkinShedder.class; // Enchant creature diff --git a/Mage.Sets/src/mage/cards/s/Skinthinner.java b/Mage.Sets/src/mage/cards/s/Skinthinner.java index 3e1ffbbb549..2f442022d5e 100644 --- a/Mage.Sets/src/mage/cards/s/Skinthinner.java +++ b/Mage.Sets/src/mage/cards/s/Skinthinner.java @@ -1,9 +1,7 @@ - package mage.cards.s; import java.util.UUID; import mage.MageInt; -import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.TurnedFaceUpSourceTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; @@ -13,9 +11,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -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; /** @@ -24,12 +20,6 @@ import mage.target.common.TargetCreaturePermanent; */ public final class Skinthinner extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public Skinthinner(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}"); this.subtype.add(SubType.ZOMBIE); @@ -40,7 +30,7 @@ public final class Skinthinner extends CardImpl { this.addAbility(new MorphAbility(this, new ManaCostsImpl("{3}{B}{B}"))); // When Skinthinner is turned face up, destroy target nonblack creature. It can't be regenerated. Ability ability = new TurnedFaceUpSourceTriggeredAbility(new DestroyTargetEffect(true)); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SkirkVolcanist.java b/Mage.Sets/src/mage/cards/s/SkirkVolcanist.java index 5517d561147..d130eeb8114 100644 --- a/Mage.Sets/src/mage/cards/s/SkirkVolcanist.java +++ b/Mage.Sets/src/mage/cards/s/SkirkVolcanist.java @@ -1,4 +1,3 @@ - package mage.cards.s; import java.util.UUID; @@ -6,7 +5,6 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.TurnedFaceUpSourceTriggeredAbility; import mage.abilities.costs.common.SacrificeTargetCost; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.DamageMultiEffect; import mage.abilities.keyword.MorphAbility; import mage.cards.CardImpl; @@ -23,10 +21,10 @@ import mage.target.common.TargetCreaturePermanentAmount; */ public final class SkirkVolcanist extends CardImpl { - private static final FilterControlledLandPermanent filterSacrifice = new FilterControlledLandPermanent("two Mountains"); + private static final FilterControlledLandPermanent filter = new FilterControlledLandPermanent("Mountains"); static { - filterSacrifice.add(SubType.MOUNTAIN.getPredicate()); + filter.add(SubType.MOUNTAIN.getPredicate()); } public SkirkVolcanist(UUID ownerId, CardSetInfo setInfo) { @@ -36,12 +34,10 @@ public final class SkirkVolcanist extends CardImpl { this.toughness = new MageInt(1); // Morph-Sacrifice two Mountains. - this.addAbility(new MorphAbility(this, new SacrificeTargetCost(new TargetControlledPermanent(2,2, filterSacrifice, false)))); + this.addAbility(new MorphAbility(this, new SacrificeTargetCost(new TargetControlledPermanent(2, filter)))); // When Skirk Volcanist is turned face up, it deals 3 damage divided as you choose among one, two, or three target creatures. - Effect effect = new DamageMultiEffect(3); - effect.setText("it deals 3 damage divided as you choose among one, two, or three target creatures"); - Ability ability = new TurnedFaceUpSourceTriggeredAbility(effect); + Ability ability = new TurnedFaceUpSourceTriggeredAbility(new DamageMultiEffect(3, "it")); ability.addTarget(new TargetCreaturePermanentAmount(3)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SkittishKavu.java b/Mage.Sets/src/mage/cards/s/SkittishKavu.java index 769ac48fb37..86d2e590e2e 100644 --- a/Mage.Sets/src/mage/cards/s/SkittishKavu.java +++ b/Mage.Sets/src/mage/cards/s/SkittishKavu.java @@ -43,7 +43,7 @@ public final class SkittishKavu extends CardImpl { // Skittish Kavu gets +1/+1 as long as no opponent controls a white or blue creature. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(new BoostSourceEffect(1, 1, Duration.WhileOnBattlefield), - new InvertCondition(new PermanentsOnTheBattlefieldCondition(filter, ComparisonType.MORE_THAN, 0, false)), + new InvertCondition(new PermanentsOnTheBattlefieldCondition(filter, false)), "{this} gets +1/+1 as long as no opponent controls a white or blue creature"))); } diff --git a/Mage.Sets/src/mage/cards/s/SkophosMazeWarden.java b/Mage.Sets/src/mage/cards/s/SkophosMazeWarden.java index 3362476b6bf..e76024a5546 100644 --- a/Mage.Sets/src/mage/cards/s/SkophosMazeWarden.java +++ b/Mage.Sets/src/mage/cards/s/SkophosMazeWarden.java @@ -99,6 +99,7 @@ class SkophosMazeWardenTriggeredAbility extends TriggeredAbilityImpl { @Override public String getRule() { return "Whenever another creature becomes the target of an ability of a land you control " + - "named Labyrinth of Skophos, you may have {this} fight that creature."; + "named Labyrinth of Skophos, you may have {this} fight that creature. " + + "(Each deals damage equal to its power to the other.)"; } } diff --git a/Mage.Sets/src/mage/cards/s/SkophosWarleader.java b/Mage.Sets/src/mage/cards/s/SkophosWarleader.java index f943dc1a5e0..0325d9ef2ad 100644 --- a/Mage.Sets/src/mage/cards/s/SkophosWarleader.java +++ b/Mage.Sets/src/mage/cards/s/SkophosWarleader.java @@ -52,7 +52,8 @@ public final class SkophosWarleader extends CardImpl { new ColoredManaCost(ColoredManaSymbol.R)); ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(filter))); ability.addEffect(new GainAbilitySourceEffect(new MenaceAbility(), Duration.EndOfTurn) - .setText("and gains menace until end of turn")); + .setText("and gains menace until end of turn. " + + "(It can't be blocked except by two or more creatures.)")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/Skulduggery.java b/Mage.Sets/src/mage/cards/s/Skulduggery.java index 276b9d88fc8..b36ca510552 100644 --- a/Mage.Sets/src/mage/cards/s/Skulduggery.java +++ b/Mage.Sets/src/mage/cards/s/Skulduggery.java @@ -11,8 +11,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetControlledCreaturePermanent; @@ -25,19 +24,13 @@ import mage.target.targetpointer.FixedTarget; */ public final class Skulduggery extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public Skulduggery(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{B}"); // Until end of turn, target creature you control gets +1/+1 and target creature an opponent controls gets -1/-1. this.getSpellAbility().addEffect(new SkulduggeryEffect()); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); } private Skulduggery(final Skulduggery card) { diff --git a/Mage.Sets/src/mage/cards/s/SkulkingKiller.java b/Mage.Sets/src/mage/cards/s/SkulkingKiller.java new file mode 100644 index 00000000000..846b8c88f97 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SkulkingKiller.java @@ -0,0 +1,78 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +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.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetOpponentsCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SkulkingKiller extends CardImpl { + + public SkulkingKiller(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); + + this.subtype.add(SubType.VAMPIRE); + this.subtype.add(SubType.ASSASSIN); + this.power = new MageInt(4); + this.toughness = new MageInt(2); + + // When Skulking Killer enters the battlefield, target creature an opponent controls gets -2/-2 until end of turn if that opponent controls no other creatures. + Ability ability = new EntersBattlefieldTriggeredAbility(new SkulkingKillerEffect()); + ability.addTarget(new TargetOpponentsCreaturePermanent()); + this.addAbility(ability); + } + + private SkulkingKiller(final SkulkingKiller card) { + super(card); + } + + @Override + public SkulkingKiller copy() { + return new SkulkingKiller(this); + } +} + +class SkulkingKillerEffect extends OneShotEffect { + + SkulkingKillerEffect() { + super(Outcome.Benefit); + staticText = "target creature an opponent controls gets -2/-2 " + + "until end of turn if that opponent controls no other creatures"; + } + + private SkulkingKillerEffect(final SkulkingKillerEffect effect) { + super(effect); + } + + @Override + public SkulkingKillerEffect copy() { + return new SkulkingKillerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent == null || game.getBattlefield().count( + StaticFilters.FILTER_CONTROLLED_CREATURE, + source.getSourceId(), permanent.getControllerId(), game + ) > 1) { + return false; + } + game.addEffect(new BoostTargetEffect(-2, -2), source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SkullSkaab.java b/Mage.Sets/src/mage/cards/s/SkullSkaab.java new file mode 100644 index 00000000000..2c90bc7a0a7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SkullSkaab.java @@ -0,0 +1,85 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.ExploitAbility; +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.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.permanent.PermanentToken; +import mage.game.permanent.token.ZombieToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SkullSkaab extends CardImpl { + + public SkullSkaab(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{U}{B}"); + + this.subtype.add(SubType.ZOMBIE); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Exploit + this.addAbility(new ExploitAbility()); + + // Whenever a creature you control exploits a nontoken creature, create a 2/2 black Zombie creature token. + this.addAbility(new SkullSkaabTriggeredAbility()); + } + + private SkullSkaab(final SkullSkaab card) { + super(card); + } + + @Override + public SkullSkaab copy() { + return new SkullSkaab(this); + } +} + +class SkullSkaabTriggeredAbility extends TriggeredAbilityImpl { + + SkullSkaabTriggeredAbility() { + super(Zone.BATTLEFIELD, new CreateTokenEffect(new ZombieToken())); + } + + private SkullSkaabTriggeredAbility(final SkullSkaabTriggeredAbility ability) { + super(ability); + } + + @Override + public SkullSkaabTriggeredAbility copy() { + return new SkullSkaabTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.EXPLOITED_CREATURE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent exploiter = game.getPermanentOrLKIBattlefield(event.getSourceId()); + Permanent exploited = game.getPermanentOrLKIBattlefield(event.getTargetId()); + return exploiter != null && exploited != null + && exploiter.isCreature(game) + && exploited.isCreature(game) + && exploiter.isControlledBy(getControllerId()) + && !(exploited instanceof PermanentToken); + } + + @Override + public String getRule() { + return "Whenever a creature you control exploits a nontoken creature, " + + "create a 2/2 black Zombie creature token."; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SkullmaneBaku.java b/Mage.Sets/src/mage/cards/s/SkullmaneBaku.java index 734be483048..738cc6fbb39 100644 --- a/Mage.Sets/src/mage/cards/s/SkullmaneBaku.java +++ b/Mage.Sets/src/mage/cards/s/SkullmaneBaku.java @@ -1,4 +1,3 @@ - package mage.cards.s; import java.util.UUID; @@ -6,33 +5,30 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility; -import mage.abilities.costs.Cost; import mage.abilities.costs.common.RemoveVariableCountersSourceCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.RemovedCountersForCostValue; +import mage.abilities.dynamicvalue.common.SignInversionDynamicValue; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; 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.counters.CounterType; import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; -import mage.target.targetpointer.FixedTarget; /** * @author LevelX2 */ public final class SkullmaneBaku extends CardImpl { + private static final DynamicValue xValue = new SignInversionDynamicValue(RemovedCountersForCostValue.instance); + public SkullmaneBaku(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{B}"); this.subtype.add(SubType.SPIRIT); @@ -41,12 +37,12 @@ public final class SkullmaneBaku extends CardImpl { this.toughness = new MageInt(1); // Whenever you cast a Spirit or Arcane spell, you may put a ki counter on Skullmane Baku. - this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.KI.createInstance()), StaticFilters.SPIRIT_OR_ARCANE_CARD, true)); + this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.KI.createInstance()), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true)); // {1}, {T}, Remove X ki counters from Skullmane Baku: Target creature gets -X/-X until end of turn. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new SkullmaneBakuUnboostEffect(), new GenericManaCost(1)); + Ability ability = new SimpleActivatedAbility(new BoostTargetEffect(xValue, xValue, Duration.EndOfTurn), new GenericManaCost(1)); ability.addCost(new TapSourceCost()); - ability.addCost(new RemoveVariableCountersSourceCost(CounterType.KI.createInstance(1))); + ability.addCost(new RemoveVariableCountersSourceCost(CounterType.KI.createInstance())); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } @@ -59,39 +55,4 @@ public final class SkullmaneBaku extends CardImpl { public SkullmaneBaku copy() { return new SkullmaneBaku(this); } - - static class SkullmaneBakuUnboostEffect extends OneShotEffect { - - public SkullmaneBakuUnboostEffect() { - super(Outcome.UnboostCreature); - staticText = "Target creature gets -X/-X until end of turn"; - } - - public SkullmaneBakuUnboostEffect(SkullmaneBakuUnboostEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - int numberToUnboost = 0; - for (Cost cost : source.getCosts()) { - if (cost instanceof RemoveVariableCountersSourceCost) { - numberToUnboost = ((RemoveVariableCountersSourceCost) cost).getAmount() * -1; - } - } - Permanent creature = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (creature != null && numberToUnboost != 0) { - ContinuousEffect effect = new BoostTargetEffect(numberToUnboost, numberToUnboost, Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(creature, game)); - game.addEffect(effect, source); - } - return true; - } - - @Override - public SkullmaneBakuUnboostEffect copy() { - return new SkullmaneBakuUnboostEffect(this); - } - - } } diff --git a/Mage.Sets/src/mage/cards/s/Skullsnatcher.java b/Mage.Sets/src/mage/cards/s/Skullsnatcher.java index bf0711ed8f8..002158a3d7f 100644 --- a/Mage.Sets/src/mage/cards/s/Skullsnatcher.java +++ b/Mage.Sets/src/mage/cards/s/Skullsnatcher.java @@ -43,7 +43,7 @@ public final class Skullsnatcher extends CardImpl { this.toughness = new MageInt(1); // Ninjutsu {B} ({B}, Return an unblocked attacker you control to hand: Put this card onto the battlefield from your hand tapped and attacking.) - this.addAbility(new NinjutsuAbility(new ManaCostsImpl("{B"))); + this.addAbility(new NinjutsuAbility("{B}")); // Whenever Skullsnatcher deals combat damage to a player, exile up to two target cards from that player's graveyard. Effect effect = new ExileTargetEffect(null, "", Zone.GRAVEYARD); diff --git a/Mage.Sets/src/mage/cards/s/SkyBlessedSamurai.java b/Mage.Sets/src/mage/cards/s/SkyBlessedSamurai.java new file mode 100644 index 00000000000..370051a9daa --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SkyBlessedSamurai.java @@ -0,0 +1,55 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +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 java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SkyBlessedSamurai extends CardImpl { + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount( + StaticFilters.FILTER_CONTROLLED_PERMANENT_ENCHANTMENT + ); + private static final Hint hint = new ValueHint("Enchantments you control", xValue); + + public SkyBlessedSamurai(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{6}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SAMURAI); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // This spell costs {1} less to cast for each enchantment you control. + this.addAbility(new SimpleStaticAbility(Zone.ALL, + new SpellCostReductionForEachSourceEffect(1, xValue) + ).addHint(hint)); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + } + + private SkyBlessedSamurai(final SkyBlessedSamurai card) { + super(card); + } + + @Override + public SkyBlessedSamurai copy() { + return new SkyBlessedSamurai(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SkyTerror.java b/Mage.Sets/src/mage/cards/s/SkyTerror.java index 90084dc5d3b..0eac13fce69 100644 --- a/Mage.Sets/src/mage/cards/s/SkyTerror.java +++ b/Mage.Sets/src/mage/cards/s/SkyTerror.java @@ -27,7 +27,7 @@ public final class SkyTerror extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); } diff --git a/Mage.Sets/src/mage/cards/s/SkyclaveShadowcat.java b/Mage.Sets/src/mage/cards/s/SkyclaveShadowcat.java index 3ea706e3722..a0063e15fad 100644 --- a/Mage.Sets/src/mage/cards/s/SkyclaveShadowcat.java +++ b/Mage.Sets/src/mage/cards/s/SkyclaveShadowcat.java @@ -13,9 +13,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.counters.CounterType; -import mage.filter.FilterPermanent; import mage.filter.StaticFilters; -import mage.filter.common.FilterControlledCreaturePermanent; import mage.target.common.TargetControlledPermanent; import java.util.UUID; @@ -25,13 +23,6 @@ import java.util.UUID; */ public final class SkyclaveShadowcat extends CardImpl { - private static final FilterPermanent filter - = new FilterControlledCreaturePermanent("a creature you control with a +1/+1 counter on it"); - - static { - filter.add(CounterType.P1P1.getPredicate()); - } - public SkyclaveShadowcat(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); @@ -50,7 +41,10 @@ public final class SkyclaveShadowcat extends CardImpl { this.addAbility(ability); // Whenever a creature you control with a +1/+1 counter on it dies, draw a card. - this.addAbility(new DiesCreatureTriggeredAbility(new DrawCardSourceControllerEffect(1), false, filter)); + this.addAbility(new DiesCreatureTriggeredAbility( + new DrawCardSourceControllerEffect(1), + false, + StaticFilters.FILTER_A_CONTROLLED_CREATURE_P1P1)); } private SkyclaveShadowcat(final SkyclaveShadowcat card) { diff --git a/Mage.Sets/src/mage/cards/s/SkyfireKirin.java b/Mage.Sets/src/mage/cards/s/SkyfireKirin.java index aee7bfba358..cb909c0c905 100644 --- a/Mage.Sets/src/mage/cards/s/SkyfireKirin.java +++ b/Mage.Sets/src/mage/cards/s/SkyfireKirin.java @@ -43,7 +43,7 @@ public final class SkyfireKirin extends CardImpl { // Whenever you cast a Spirit or Arcane spell, you may gain control of target creature with that spell's converted mana cost until end of turn. Ability ability = new SpellCastControllerTriggeredAbility( Zone.BATTLEFIELD, new SkyfireKirinEffect(), - StaticFilters.SPIRIT_OR_ARCANE_CARD, true, true + StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true, true ); ability.addTarget(new TargetCreaturePermanent()); ability.setTargetAdjuster(SkyfireKirinAdjuster.instance); @@ -102,7 +102,7 @@ class SkyfireKirinEffect extends OneShotEffect { } if (targetCreature != null) { ContinuousEffect effect = new GainControlTargetEffect(Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(targetCreature.getId())); + effect.setTargetPointer(new FixedTarget(targetCreature.getId(), game)); game.addEffect(effect, source); return true; } diff --git a/Mage.Sets/src/mage/cards/s/SkylineCascade.java b/Mage.Sets/src/mage/cards/s/SkylineCascade.java index 75cc118306d..65f3c2c43bb 100644 --- a/Mage.Sets/src/mage/cards/s/SkylineCascade.java +++ b/Mage.Sets/src/mage/cards/s/SkylineCascade.java @@ -10,8 +10,7 @@ import mage.abilities.mana.BlueManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; /** @@ -20,12 +19,6 @@ import mage.target.common.TargetCreaturePermanent; */ public final class SkylineCascade extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public SkylineCascade(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.LAND},""); @@ -34,7 +27,7 @@ public final class SkylineCascade extends CardImpl { // When Skyline Cascade enters the battlefield, target creature an opponent controls doesn't untap during its controller's next untap step. Ability ability = new EntersBattlefieldTriggeredAbility(new DontUntapInControllersNextUntapStepTargetEffect(), false); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); this.addAbility(ability); // {T}: Add {U}. diff --git a/Mage.Sets/src/mage/cards/s/SkylineScout.java b/Mage.Sets/src/mage/cards/s/SkylineScout.java index 3bb5b70e86e..21a1eeb448e 100644 --- a/Mage.Sets/src/mage/cards/s/SkylineScout.java +++ b/Mage.Sets/src/mage/cards/s/SkylineScout.java @@ -1,20 +1,20 @@ package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.DoIfCostPaid; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; import mage.abilities.keyword.FlyingAbility; -import mage.constants.SubType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; +import mage.constants.SubType; + +import java.util.UUID; /** - * * @author TheElk801 */ public final class SkylineScout extends CardImpl { @@ -29,10 +29,9 @@ public final class SkylineScout extends CardImpl { // Whenever Skyline Scout attacks, you may pay {1}{W}. If you do, it gains flying until end of turn. this.addAbility(new AttacksTriggeredAbility(new DoIfCostPaid( - new GainAbilitySourceEffect( - FlyingAbility.getInstance(), - Duration.EndOfTurn - ), new ManaCostsImpl("{1}{W}") + new GainAbilitySourceEffect(FlyingAbility.getInstance(), Duration.EndOfTurn) + .setText("it gains flying until end of turn"), + new ManaCostsImpl("{1}{W}") ), false)); } diff --git a/Mage.Sets/src/mage/cards/s/SkyswimmerKoi.java b/Mage.Sets/src/mage/cards/s/SkyswimmerKoi.java new file mode 100644 index 00000000000..c2b17b16299 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SkyswimmerKoi.java @@ -0,0 +1,45 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.effects.common.DrawDiscardControllerEffect; +import mage.abilities.keyword.FlyingAbility; +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 SkyswimmerKoi extends CardImpl { + + public SkyswimmerKoi(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); + + this.subtype.add(SubType.FISH); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever an artifact enters the battlefield under your control, you may draw a card. If you do, discard a card. + this.addAbility(new EntersBattlefieldControlledTriggeredAbility( + new DrawDiscardControllerEffect(1, 1, true), + StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT_AN + )); + } + + private SkyswimmerKoi(final SkyswimmerKoi card) { + super(card); + } + + @Override + public SkyswimmerKoi copy() { + return new SkyswimmerKoi(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SkywarpSkaab.java b/Mage.Sets/src/mage/cards/s/SkywarpSkaab.java new file mode 100644 index 00000000000..55a20e2ff17 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SkywarpSkaab.java @@ -0,0 +1,53 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.costs.common.ExileFromGraveCost; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SkywarpSkaab extends CardImpl { + + public SkywarpSkaab(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{U}"); + + this.subtype.add(SubType.ZOMBIE); + this.subtype.add(SubType.DRAKE); + this.power = new MageInt(2); + this.toughness = new MageInt(5); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When Skywarp Skaab enters the battlefield, you may exile two creature cards from your graveyard. If you do, draw a card. + this.addAbility(new EntersBattlefieldTriggeredAbility( + new DoIfCostPaid( + new DrawCardSourceControllerEffect(1), + new ExileFromGraveCost(new TargetCardInYourGraveyard( + 2, StaticFilters.FILTER_CARD_CREATURES_YOUR_GRAVEYARD + )) + ) + )); + } + + private SkywarpSkaab(final SkywarpSkaab card) { + super(card); + } + + @Override + public SkywarpSkaab copy() { + return new SkywarpSkaab(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SlateStreetRuffian.java b/Mage.Sets/src/mage/cards/s/SlateStreetRuffian.java index 8cb102b5a3f..d6ab1b92431 100644 --- a/Mage.Sets/src/mage/cards/s/SlateStreetRuffian.java +++ b/Mage.Sets/src/mage/cards/s/SlateStreetRuffian.java @@ -1,19 +1,13 @@ - package mage.cards.s; import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.common.BecomesBlockedByCreatureTriggeredAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; +import mage.abilities.effects.common.discard.DiscardTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Outcome; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; /** * @@ -29,9 +23,10 @@ public final class SlateStreetRuffian extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); - // Whenever Slate Street Ruffian becomes blocked, defending player discards a card. - this.addAbility(new BecomesBlockedByCreatureTriggeredAbility(new SlateStreetRuffianDiscardEffect(), false)); + this.addAbility(new BecomesBlockedSourceTriggeredAbility( + new DiscardTargetEffect(1).setText("defending player discards a card"), + false, true)); } private SlateStreetRuffian(final SlateStreetRuffian card) { @@ -43,33 +38,3 @@ public final class SlateStreetRuffian extends CardImpl { return new SlateStreetRuffian(this); } } - -class SlateStreetRuffianDiscardEffect extends OneShotEffect { - - public SlateStreetRuffianDiscardEffect() { - super(Outcome.Discard); - this.staticText = "defending player discards a card"; - } - - public SlateStreetRuffianDiscardEffect(final SlateStreetRuffianDiscardEffect effect) { - super(effect); - } - - @Override - public SlateStreetRuffianDiscardEffect copy() { - return new SlateStreetRuffianDiscardEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent blockingCreature = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (blockingCreature != null) { - Player opponent = game.getPlayer(blockingCreature.getControllerId()); - if (opponent != null) { - opponent.discard(1, false, false, source, game); - return true; - } - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/s/Slaughter.java b/Mage.Sets/src/mage/cards/s/Slaughter.java index 9d3e54559ed..e7bfe3deaee 100644 --- a/Mage.Sets/src/mage/cards/s/Slaughter.java +++ b/Mage.Sets/src/mage/cards/s/Slaughter.java @@ -1,17 +1,13 @@ - package mage.cards.s; import java.util.UUID; -import mage.ObjectColor; import mage.abilities.costs.common.PayLifeCost; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.keyword.BuybackAbility; 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; /** @@ -20,12 +16,6 @@ import mage.target.common.TargetCreaturePermanent; */ public final class Slaughter extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public Slaughter(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{B}{B}"); @@ -34,7 +24,7 @@ public final class Slaughter extends CardImpl { // Destroy target nonblack creature. It can't be regenerated. this.getSpellAbility().addEffect(new DestroyTargetEffect(true)); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); } private Slaughter(final Slaughter card) { diff --git a/Mage.Sets/src/mage/cards/s/SlaughterPact.java b/Mage.Sets/src/mage/cards/s/SlaughterPact.java index 99740ebf4ce..62ad870c5bd 100644 --- a/Mage.Sets/src/mage/cards/s/SlaughterPact.java +++ b/Mage.Sets/src/mage/cards/s/SlaughterPact.java @@ -1,8 +1,6 @@ - package mage.cards.s; import java.util.UUID; -import mage.ObjectColor; import mage.abilities.common.delayed.PactDelayedTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; @@ -10,9 +8,7 @@ import mage.abilities.effects.common.DestroyTargetEffect; 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; /** @@ -20,20 +16,14 @@ import mage.target.common.TargetCreaturePermanent; * @author Plopman */ public final class SlaughterPact extends CardImpl { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public SlaughterPact(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{0}"); this.color.setBlack(true); - + // Destroy target nonblack creature. - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); this.getSpellAbility().addEffect(new DestroyTargetEffect()); // At the beginning of your next upkeep, pay {2}{B}. If you don't, you lose the game. this.getSpellAbility().addEffect(new CreateDelayedTriggeredAbilityEffect(new PactDelayedTriggeredAbility(new ManaCostsImpl("{2}{B}")),false)); diff --git a/Mage.Sets/src/mage/cards/s/SleepOfTheDead.java b/Mage.Sets/src/mage/cards/s/SleepOfTheDead.java index 0d443a2e442..0a736726d62 100644 --- a/Mage.Sets/src/mage/cards/s/SleepOfTheDead.java +++ b/Mage.Sets/src/mage/cards/s/SleepOfTheDead.java @@ -20,7 +20,7 @@ public final class SleepOfTheDead extends CardImpl { // Tap target creature. It doesn't untap during its controller's next untap step. this.getSpellAbility().addEffect(new TapTargetEffect()); - this.getSpellAbility().addEffect(new DontUntapInControllersNextUntapStepTargetEffect()); + this.getSpellAbility().addEffect(new DontUntapInControllersNextUntapStepTargetEffect("it")); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); // Escape—{2}{U}, Exile three other cards from your graveyard. diff --git a/Mage.Sets/src/mage/cards/s/SleepingPotion.java b/Mage.Sets/src/mage/cards/s/SleepingPotion.java index 2585988f833..a25b7212b92 100644 --- a/Mage.Sets/src/mage/cards/s/SleepingPotion.java +++ b/Mage.Sets/src/mage/cards/s/SleepingPotion.java @@ -1,8 +1,6 @@ - package mage.cards.s; import java.util.UUID; -import mage.abilities.Ability; import mage.abilities.common.BecomesTargetAttachedTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; @@ -34,12 +32,14 @@ public final class SleepingPotion extends CardImpl { TargetPermanent auraTarget = new TargetCreaturePermanent(); this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.Detriment)); - Ability ability = new EnchantAbility(auraTarget.getTargetName()); - this.addAbility(ability); + this.addAbility(new EnchantAbility(auraTarget.getTargetName())); + // When Sleeping Potion enters the battlefield, tap enchanted creature. this.addAbility(new EntersBattlefieldTriggeredAbility(new TapEnchantedEffect())); + // Enchanted creature doesn't untap during its controller's untap step. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DontUntapInControllersUntapStepEnchantedEffect())); + // When enchanted creature becomes the target of a spell or ability, sacrifice Sleeping Potion. this.addAbility(new BecomesTargetAttachedTriggeredAbility(new SacrificeSourceEffect())); } diff --git a/Mage.Sets/src/mage/cards/s/SliverHivelord.java b/Mage.Sets/src/mage/cards/s/SliverHivelord.java index 93106c9dcb3..b2acb06e413 100644 --- a/Mage.Sets/src/mage/cards/s/SliverHivelord.java +++ b/Mage.Sets/src/mage/cards/s/SliverHivelord.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; @@ -9,14 +7,14 @@ import mage.abilities.keyword.IndestructibleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; +import mage.constants.SubType; import mage.constants.SuperType; -import mage.constants.Zone; import mage.filter.StaticFilters; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class SliverHivelord extends CardImpl { @@ -30,10 +28,10 @@ public final class SliverHivelord extends CardImpl { this.toughness = new MageInt(5); // Sliver creatures you control have indestructible. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, - new GainAbilityControlledEffect(IndestructibleAbility.getInstance(), - Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS))); - + this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( + IndestructibleAbility.getInstance(), Duration.WhileOnBattlefield, + StaticFilters.FILTER_PERMANENT_SLIVERS + ))); } private SliverHivelord(final SliverHivelord card) { diff --git a/Mage.Sets/src/mage/cards/s/SliverOverlord.java b/Mage.Sets/src/mage/cards/s/SliverOverlord.java index bbbb935432b..5fedf523a92 100644 --- a/Mage.Sets/src/mage/cards/s/SliverOverlord.java +++ b/Mage.Sets/src/mage/cards/s/SliverOverlord.java @@ -42,7 +42,7 @@ public final class SliverOverlord extends CardImpl { this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filter), true, true), new ManaCostsImpl("{3}"))); // {3}: Gain control of target Sliver. - Ability ability = (new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainControlTargetEffect(Duration.EndOfGame), new ManaCostsImpl("{3}"))); + Ability ability = (new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainControlTargetEffect(Duration.Custom), new ManaCostsImpl("{3}"))); Target target = new TargetPermanent(new FilterCreaturePermanent(SubType.SLIVER,"Sliver")); ability.addTarget(target); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/s/SludgeMonster.java b/Mage.Sets/src/mage/cards/s/SludgeMonster.java index 29775592cd1..42d3a095551 100644 --- a/Mage.Sets/src/mage/cards/s/SludgeMonster.java +++ b/Mage.Sets/src/mage/cards/s/SludgeMonster.java @@ -92,16 +92,15 @@ class SludgeMonsterEffect extends ContinuousEffectImpl { switch (layer) { case AbilityAddingRemovingEffects_6: permanent.removeAllAbilities(source.getSourceId(), game); - return true; + break; case PTChangingEffects_7: if (sublayer == SubLayer.SetPT_7b) { permanent.getPower().setValue(2); permanent.getToughness().setValue(2); - return true; } } } - return false; + return true; } @Override @@ -111,11 +110,6 @@ class SludgeMonsterEffect extends ContinuousEffectImpl { @Override public boolean hasLayer(Layer layer) { - switch (layer) { - case AbilityAddingRemovingEffects_6: - case PTChangingEffects_7: - return true; - } - return false; + return layer == Layer.AbilityAddingRemovingEffects_6 || layer == Layer.PTChangingEffects_7; } } diff --git a/Mage.Sets/src/mage/cards/s/SmellFear.java b/Mage.Sets/src/mage/cards/s/SmellFear.java index 601caa4aeed..267642731e4 100644 --- a/Mage.Sets/src/mage/cards/s/SmellFear.java +++ b/Mage.Sets/src/mage/cards/s/SmellFear.java @@ -22,7 +22,7 @@ public final class SmellFear extends CardImpl { this.getSpellAbility().addEffect(new ProliferateEffect()); // Target creature you control fights up to one target creature you don't control. - this.getSpellAbility().addEffect(new FightTargetsEffect( + this.getSpellAbility().addEffect(new FightTargetsEffect().setText( "
Target creature you control fights up to one target creature you don't control" )); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); diff --git a/Mage.Sets/src/mage/cards/s/SmeltWardGatekeepers.java b/Mage.Sets/src/mage/cards/s/SmeltWardGatekeepers.java index 29673ee5250..eaa2738d83d 100644 --- a/Mage.Sets/src/mage/cards/s/SmeltWardGatekeepers.java +++ b/Mage.Sets/src/mage/cards/s/SmeltWardGatekeepers.java @@ -14,8 +14,8 @@ import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; +import mage.filter.StaticFilters; import mage.filter.common.FilterControlledPermanent; -import mage.filter.common.FilterCreaturePermanent; import mage.target.Target; import mage.target.common.TargetCreaturePermanent; @@ -29,11 +29,9 @@ import java.util.UUID; public final class SmeltWardGatekeepers extends CardImpl { private static final FilterControlledPermanent filter = new FilterControlledPermanent(); - private static final FilterCreaturePermanent targetFilter = new FilterCreaturePermanent("creature an opponent controls"); static { filter.add(SubType.GATE.getPredicate()); - targetFilter.add(TargetController.OPPONENT.getControllerPredicate()); } private static final Condition gatesCondition = new PermanentsOnTheBattlefieldCondition(filter, ComparisonType.MORE_THAN, 1); @@ -53,7 +51,7 @@ public final class SmeltWardGatekeepers extends CardImpl { "When {this} enters the battlefield, if you control two or more Gates, gain control of target creature an opponent controls until end of turn. Untap that creature. That creature gains haste until end of turn."); ability.addEffect(new UntapTargetEffect()); ability.addEffect(new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn)); - Target target = new TargetCreaturePermanent(targetFilter); + Target target = new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE); ability.addTarget(target); ability.addHint(new ConditionHint(gatesCondition, "You control two or more Gates")); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/s/SmeltWardMinotaur.java b/Mage.Sets/src/mage/cards/s/SmeltWardMinotaur.java index e11e738fd9b..a755d3a546c 100644 --- a/Mage.Sets/src/mage/cards/s/SmeltWardMinotaur.java +++ b/Mage.Sets/src/mage/cards/s/SmeltWardMinotaur.java @@ -30,7 +30,7 @@ public final class SmeltWardMinotaur extends CardImpl { // Whenever you cast an instant or sorcery spell, target creature an opponent controls can't block this turn. Ability ability = new SpellCastControllerTriggeredAbility( new CantBlockTargetEffect(Duration.EndOfTurn), - StaticFilters.FILTER_SPELLS_INSTANT_OR_SORCERY, false + StaticFilters.FILTER_SPELL_AN_INSTANT_OR_SORCERY, false ); ability.addTarget(new TargetOpponentsCreaturePermanent()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/s/SmogsteedRider.java b/Mage.Sets/src/mage/cards/s/SmogsteedRider.java index 703db1c6f40..afe6f12c749 100644 --- a/Mage.Sets/src/mage/cards/s/SmogsteedRider.java +++ b/Mage.Sets/src/mage/cards/s/SmogsteedRider.java @@ -1,16 +1,16 @@ - package mage.cards.s; import java.util.UUID; import mage.MageInt; import mage.abilities.common.AttacksTriggeredAbility; -import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.effects.common.continuous.GainAbilityAllEffect; import mage.abilities.keyword.FearAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterPermanent; import mage.filter.common.FilterAttackingCreature; /** @@ -19,6 +19,8 @@ import mage.filter.common.FilterAttackingCreature; */ public final class SmogsteedRider extends CardImpl { + private static final FilterPermanent filter = new FilterAttackingCreature("each other attacking creature"); + public SmogsteedRider(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{B}{B}"); this.subtype.add(SubType.HUMAN); @@ -26,7 +28,7 @@ public final class SmogsteedRider extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); - this.addAbility(new AttacksTriggeredAbility(new GainAbilityControlledEffect(FearAbility.getInstance(), Duration.EndOfTurn, new FilterAttackingCreature(), true), false)); + this.addAbility(new AttacksTriggeredAbility(new GainAbilityAllEffect(FearAbility.getInstance(), Duration.EndOfTurn, filter, true), false)); } private SmogsteedRider(final SmogsteedRider card) { diff --git a/Mage.Sets/src/mage/cards/s/SmokeSpiritsAid.java b/Mage.Sets/src/mage/cards/s/SmokeSpiritsAid.java new file mode 100644 index 00000000000..ab4accb2f6f --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SmokeSpiritsAid.java @@ -0,0 +1,91 @@ +package mage.cards.s; + +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.Permanent; +import mage.game.permanent.token.SmokeBlessingToken; +import mage.game.permanent.token.Token; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetadjustment.TargetAdjuster; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SmokeSpiritsAid extends CardImpl { + + public SmokeSpiritsAid(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{R}"); + + // For each of up to X target creatures, create a red Aura enchantment token named Smoke Blessing attached to that creature. Those tokens have enchant creature and "When enchanted creature dies, it deals 1 damage to its controller and you create a Treasure token." + this.getSpellAbility().addEffect(new SmokeSpiritsAidEffect()); + this.getSpellAbility().setTargetAdjuster(SmokeSpiritsAidAdjuster.instance); + } + + private SmokeSpiritsAid(final SmokeSpiritsAid card) { + super(card); + } + + @Override + public SmokeSpiritsAid copy() { + return new SmokeSpiritsAid(this); + } +} + +enum SmokeSpiritsAidAdjuster implements TargetAdjuster { + instance; + + @Override + public void adjustTargets(Ability ability, Game game) { + if (ability.getManaCostsToPay().getX() > 0) { + ability.getTargets().clear(); + ability.addTarget(new TargetCreaturePermanent(0, ability.getManaCostsToPay().getX())); + } + } +} + +class SmokeSpiritsAidEffect extends OneShotEffect { + + SmokeSpiritsAidEffect() { + super(Outcome.Benefit); + staticText = "for each of up to X target creatures, create a red Aura enchantment token " + + "named Smoke Blessing attached to that creature. Those tokens have enchant creature and " + + "\"When enchanted creature dies, it deals 1 damage to its controller and you create a Treasure token.\""; + } + + private SmokeSpiritsAidEffect(final SmokeSpiritsAidEffect effect) { + super(effect); + } + + @Override + public SmokeSpiritsAidEffect copy() { + return new SmokeSpiritsAidEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Token token = new SmokeBlessingToken(); + for (UUID targetId : getTargetPointer().getTargets(game, source)) { + Permanent permanent = game.getPermanent(targetId); + if (permanent == null) { + continue; + } + token.putOntoBattlefield(1, game, source); + for (UUID tokenId : token.getLastAddedTokenIds()) { + Permanent aura = game.getPermanent(tokenId); + if (aura == null) { + continue; + } + aura.getAbilities().get(0).getTargets().get(0).add(source.getFirstTarget(), game); + aura.getAbilities().get(0).getEffects().get(0).apply(game, aura.getAbilities().get(0)); + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SmolderingEgg.java b/Mage.Sets/src/mage/cards/s/SmolderingEgg.java index b64c045305d..cf07316e41b 100644 --- a/Mage.Sets/src/mage/cards/s/SmolderingEgg.java +++ b/Mage.Sets/src/mage/cards/s/SmolderingEgg.java @@ -33,7 +33,6 @@ public final class SmolderingEgg extends CardImpl { this.subtype.add(SubType.EGG); this.power = new MageInt(0); this.toughness = new MageInt(4); - this.transformable = true; this.secondSideCardClazz = mage.cards.a.AshmouthDragon.class; // Defender @@ -92,7 +91,7 @@ class SmolderingEggEffect extends OneShotEffect { return true; } permanent.removeCounters(CounterType.EMBER.createInstance(counters), source, game); - new TransformSourceEffect(true).apply(game, source); + new TransformSourceEffect().apply(game, source); return true; } } diff --git a/Mage.Sets/src/mage/cards/s/SmolderingWerewolf.java b/Mage.Sets/src/mage/cards/s/SmolderingWerewolf.java index 755b9fa4600..42825ddfeed 100644 --- a/Mage.Sets/src/mage/cards/s/SmolderingWerewolf.java +++ b/Mage.Sets/src/mage/cards/s/SmolderingWerewolf.java @@ -31,7 +31,6 @@ public final class SmolderingWerewolf extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(2); - this.transformable = true; this.secondSideCardClazz = mage.cards.e.EruptingDreadwolf.class; // When Smoldering Werewolf enters the battlefield, it deals 1 damage to each of up to two target creatures. @@ -43,7 +42,7 @@ public final class SmolderingWerewolf extends CardImpl { // {4}{R}{R}: Transform Smoldering Werewolf. this.addAbility(new TransformAbility()); - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new TransformSourceEffect(true), new ManaCostsImpl("{4}{R}{R}"))); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new TransformSourceEffect(), new ManaCostsImpl("{4}{R}{R}"))); } private SmolderingWerewolf(final SmolderingWerewolf card) { diff --git a/Mage.Sets/src/mage/cards/s/SmotheringTithe.java b/Mage.Sets/src/mage/cards/s/SmotheringTithe.java index 2ac6e026d5f..626b9b169a7 100644 --- a/Mage.Sets/src/mage/cards/s/SmotheringTithe.java +++ b/Mage.Sets/src/mage/cards/s/SmotheringTithe.java @@ -4,7 +4,6 @@ import mage.abilities.Ability; import mage.abilities.common.DrawCardOpponentTriggeredAbility; import mage.abilities.costs.Cost; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -44,9 +43,7 @@ class SmotheringTitheEffect extends OneShotEffect { SmotheringTitheEffect() { super(Outcome.Benefit); - staticText = "that player may pay {2}. If the player doesn't, " + - "you create a colorless Treasure artifact token " + - "with \"{T}, Sacrifice this artifact: Add one mana of any color.\""; + staticText = "that player may pay {2}. If the player doesn't, you create a Treasure token"; } private SmotheringTitheEffect(final SmotheringTitheEffect effect) { @@ -67,7 +64,7 @@ class SmotheringTitheEffect extends OneShotEffect { Cost cost = ManaUtil.createManaCost(2, false); if (!player.chooseUse(Outcome.Detriment, "Pay {2} to prevent this effect?", source, game) || !cost.pay(source, game, source, player.getId(), false)) { - return new CreateTokenEffect(new TreasureToken()).apply(game, source); + return new TreasureToken().putOntoBattlefield(1, game, source, source.getControllerId()); } return false; } diff --git a/Mage.Sets/src/mage/cards/s/Snapback.java b/Mage.Sets/src/mage/cards/s/Snapback.java index 07157766c7d..ff0a902980b 100644 --- a/Mage.Sets/src/mage/cards/s/Snapback.java +++ b/Mage.Sets/src/mage/cards/s/Snapback.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.ObjectColor; import mage.abilities.costs.AlternativeCostSourceAbility; import mage.abilities.costs.common.ExileFromHandCost; @@ -10,28 +8,30 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.filter.common.FilterOwnedCard; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardIdPredicate; import mage.filter.predicate.mageobject.ColorPredicate; import mage.target.common.TargetCardInHand; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author emerald000 */ public final class Snapback extends CardImpl { - public Snapback(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{U}"); + private static final FilterOwnedCard filter + = new FilterOwnedCard("a blue card from your hand"); + static { + filter.add(new ColorPredicate(ObjectColor.BLUE)); + } + + public Snapback(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}"); // You may exile a blue card from your hand rather than pay Snapback's mana cost. - FilterOwnedCard filterCardInHand = new FilterOwnedCard("a blue card from your hand"); - filterCardInHand.add(new ColorPredicate(ObjectColor.BLUE)); - filterCardInHand.add(Predicates.not(new CardIdPredicate(this.getId()))); - this.addAbility(new AlternativeCostSourceAbility(new ExileFromHandCost(new TargetCardInHand(filterCardInHand)))); - + this.addAbility(new AlternativeCostSourceAbility(new ExileFromHandCost(new TargetCardInHand(filter)))); + // Return target creature to its owner's hand. this.getSpellAbility().addTarget(new TargetCreaturePermanent()); this.getSpellAbility().addEffect(new ReturnToHandTargetEffect()); diff --git a/Mage.Sets/src/mage/cards/s/SnapcasterMage.java b/Mage.Sets/src/mage/cards/s/SnapcasterMage.java index 58a03c9c026..ed9da041295 100644 --- a/Mage.Sets/src/mage/cards/s/SnapcasterMage.java +++ b/Mage.Sets/src/mage/cards/s/SnapcasterMage.java @@ -1,4 +1,3 @@ - package mage.cards.s; import mage.MageInt; @@ -41,11 +40,11 @@ public final class SnapcasterMage extends CardImpl { // Flash this.addAbility(FlashAbility.getInstance()); + // When Snapcaster Mage enters the battlefield, target instant or sorcery card in your graveyard gains flashback until end of turn. The flashback cost is equal to its mana cost. Ability ability = new EntersBattlefieldTriggeredAbility(new SnapcasterMageEffect()); ability.addTarget(new TargetCardInYourGraveyard(filter)); this.addAbility(ability); - } private SnapcasterMage(final SnapcasterMage card) { diff --git a/Mage.Sets/src/mage/cards/s/SneakAttack.java b/Mage.Sets/src/mage/cards/s/SneakAttack.java index 9d0d7508186..1f832a7ab5c 100644 --- a/Mage.Sets/src/mage/cards/s/SneakAttack.java +++ b/Mage.Sets/src/mage/cards/s/SneakAttack.java @@ -22,6 +22,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInHand; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.UUID; @@ -80,7 +81,7 @@ class SneakAttackEffect extends OneShotEffect { if (card == null || !controller.moveCards(card, Zone.BATTLEFIELD, source, game)) { return false; } - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = game.getPermanent(CardUtil.getDefaultCardSideForBattlefield(game, card).getId()); if (permanent == null) { return true; } diff --git a/Mage.Sets/src/mage/cards/s/SnuffOut.java b/Mage.Sets/src/mage/cards/s/SnuffOut.java index 1258afb76d6..2d22affd606 100644 --- a/Mage.Sets/src/mage/cards/s/SnuffOut.java +++ b/Mage.Sets/src/mage/cards/s/SnuffOut.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import mage.ObjectColor; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.costs.AlternativeCostSourceAbility; import mage.abilities.costs.common.PayLifeCost; @@ -10,10 +8,8 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.filter.common.FilterLandPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ColorPredicate; import mage.target.common.TargetCreaturePermanent; import java.util.UUID; @@ -24,18 +20,15 @@ import java.util.UUID; */ public final class SnuffOut extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); private static final FilterLandPermanent filterSwamp = new FilterLandPermanent("If you control a Swamp"); static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); filterSwamp.add(SubType.SWAMP.getPredicate()); } public SnuffOut(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{3}{B}"); - // If you control a Swamp, you may pay 4 life rather than pay Snuff Out's mana cost. this.addAbility(new AlternativeCostSourceAbility( new PayLifeCost(4), @@ -43,7 +36,7 @@ public final class SnuffOut extends CardImpl { // // Destroy target nonblack creature. It can't be regenerated. this.getSpellAbility().addEffect(new DestroyTargetEffect(true)); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); } private SnuffOut(final SnuffOut card) { diff --git a/Mage.Sets/src/mage/cards/s/SoaringThoughtThief.java b/Mage.Sets/src/mage/cards/s/SoaringThoughtThief.java index deae92bc1fb..517a053c7c9 100644 --- a/Mage.Sets/src/mage/cards/s/SoaringThoughtThief.java +++ b/Mage.Sets/src/mage/cards/s/SoaringThoughtThief.java @@ -50,7 +50,7 @@ public final class SoaringThoughtThief extends CardImpl { // Whenever one or more Rogues you control attack, each opponent mills two cards. this.addAbility(new AttacksWithCreaturesTriggeredAbility( new MillCardsEachPlayerEffect(2, TargetController.OPPONENT), 1, filter - )); + ).setTriggerPhrase("Whenever one or more Rogues you control attack, ")); } private SoaringThoughtThief(final SoaringThoughtThief card) { diff --git a/Mage.Sets/src/mage/cards/s/Soilshaper.java b/Mage.Sets/src/mage/cards/s/Soilshaper.java index b567baa5733..57d2bb86c5b 100644 --- a/Mage.Sets/src/mage/cards/s/Soilshaper.java +++ b/Mage.Sets/src/mage/cards/s/Soilshaper.java @@ -30,7 +30,7 @@ public final class Soilshaper extends CardImpl { this.toughness = new MageInt(1); // Whenever you cast a Spirit or Arcane spell, target land becomes a 3/3 creature until end of turn. It's still a land. - Ability ability = new SpellCastControllerTriggeredAbility(new BecomesCreatureTargetEffect(new CreatureToken(3, 3), false, true, Duration.EndOfTurn), StaticFilters.SPIRIT_OR_ARCANE_CARD, false); + Ability ability = new SpellCastControllerTriggeredAbility(new BecomesCreatureTargetEffect(new CreatureToken(3, 3), false, true, Duration.EndOfTurn), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false); ability.addTarget(new TargetLandPermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SokenzanCrucibleOfDefiance.java b/Mage.Sets/src/mage/cards/s/SokenzanCrucibleOfDefiance.java new file mode 100644 index 00000000000..fceb6b076c8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SokenzanCrucibleOfDefiance.java @@ -0,0 +1,76 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.costs.costadjusters.LegendaryCreatureCostAdjuster; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.ChannelAbility; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.mana.RedManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SuperType; +import mage.game.Game; +import mage.game.permanent.token.SpiritToken; +import mage.game.permanent.token.Token; +import mage.target.targetpointer.FixedTargets; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SokenzanCrucibleOfDefiance extends CardImpl { + + public SokenzanCrucibleOfDefiance(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + this.addSuperType(SuperType.LEGENDARY); + + // {T}: Add {R}. + this.addAbility(new RedManaAbility()); + + // Channel — {3}{R}, Discard Sokenzan, Crucible of Defiance: Create two colorless 1/1 Spirit creature tokens. They gain haste until end of turn. This ability costs {1} less to activate for each legendary creature you control. + Ability ability = new ChannelAbility("{3}{R}", new SokenzanCrucibleOfDefianceEffect()); + ability.setCostAdjuster(LegendaryCreatureCostAdjuster.instance); + this.addAbility(ability); + } + + private SokenzanCrucibleOfDefiance(final SokenzanCrucibleOfDefiance card) { + super(card); + } + + @Override + public SokenzanCrucibleOfDefiance copy() { + return new SokenzanCrucibleOfDefiance(this); + } +} + +class SokenzanCrucibleOfDefianceEffect extends OneShotEffect { + + SokenzanCrucibleOfDefianceEffect() { + super(Outcome.Benefit); + staticText = "create two 1/1 colorless Spirit creature tokens. They gain haste until end of turn. " + + "This ability costs {1} less to activate for each legendary creature you control"; + } + + private SokenzanCrucibleOfDefianceEffect(final SokenzanCrucibleOfDefianceEffect effect) { + super(effect); + } + + @Override + public SokenzanCrucibleOfDefianceEffect copy() { + return new SokenzanCrucibleOfDefianceEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Token token = new SpiritToken(); + token.putOntoBattlefield(2, game, source); + game.addEffect(new GainAbilityTargetEffect(HasteAbility.getInstance()) + .setTargetPointer(new FixedTargets(token, game)), source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SokenzanRenegade.java b/Mage.Sets/src/mage/cards/s/SokenzanRenegade.java index 7e699568f60..e51dfa55c27 100644 --- a/Mage.Sets/src/mage/cards/s/SokenzanRenegade.java +++ b/Mage.Sets/src/mage/cards/s/SokenzanRenegade.java @@ -1,4 +1,3 @@ - package mage.cards.s; import java.util.UUID; @@ -94,8 +93,8 @@ class SokenzanRenegadeEffect extends OneShotEffect { } } if (newController != null) { - ContinuousEffect effect = new GainControlTargetEffect(Duration.Custom, newController.getId()); - effect.setTargetPointer(new FixedTarget(sourcePermanent.getId())); + ContinuousEffect effect = new GainControlTargetEffect(Duration.EndOfGame, newController.getId()); + effect.setTargetPointer(new FixedTarget(sourcePermanent.getId(), game)); game.addEffect(effect, source); if (!source.isControlledBy(newController.getId())) { game.informPlayers(newController.getLogName() + " got control of " + sourcePermanent.getLogName()); diff --git a/Mage.Sets/src/mage/cards/s/SokenzanSmelter.java b/Mage.Sets/src/mage/cards/s/SokenzanSmelter.java new file mode 100644 index 00000000000..12747f795fa --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SokenzanSmelter.java @@ -0,0 +1,54 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.BeginningOfCombatTriggeredAbility; +import mage.abilities.costs.CompositeCost; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.StaticFilters; +import mage.game.permanent.token.ConstructRedToken; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SokenzanSmelter extends CardImpl { + + public SokenzanSmelter(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); + + this.subtype.add(SubType.GOBLIN); + this.subtype.add(SubType.ARTIFICER); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // At the beginning of combat on your turn, you may pay {1} and sacrifice an artifact. If you do, create a 3/1 red Construct artifact creature token with haste. + this.addAbility(new BeginningOfCombatTriggeredAbility(new DoIfCostPaid( + new CreateTokenEffect(new ConstructRedToken()), + new CompositeCost( + new GenericManaCost(1), + new SacrificeTargetCost(new TargetControlledPermanent( + StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT_AN + )), "pay {1} and sacrifice an artifact" + ) + ), TargetController.YOU, false)); + } + + private SokenzanSmelter(final SokenzanSmelter card) { + super(card); + } + + @Override + public SokenzanSmelter copy() { + return new SokenzanSmelter(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SoldeviExcavations.java b/Mage.Sets/src/mage/cards/s/SoldeviExcavations.java index ed27fc895cc..7e297f38243 100644 --- a/Mage.Sets/src/mage/cards/s/SoldeviExcavations.java +++ b/Mage.Sets/src/mage/cards/s/SoldeviExcavations.java @@ -44,7 +44,7 @@ public final class SoldeviExcavations extends CardImpl { this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, new Mana(0, 1, 0, 0, 0, 0, 0, 1), new TapSourceCost())); // {1}, {tap}: Scry 1. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ScryEffect(1), new GenericManaCost(1)); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ScryEffect(1, false), new GenericManaCost(1)); ability.addCost(new TapSourceCost()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SoldeviSteamBeast.java b/Mage.Sets/src/mage/cards/s/SoldeviSteamBeast.java index 4271fed44e3..aaa5bc6a884 100644 --- a/Mage.Sets/src/mage/cards/s/SoldeviSteamBeast.java +++ b/Mage.Sets/src/mage/cards/s/SoldeviSteamBeast.java @@ -1,4 +1,3 @@ - package mage.cards.s; import java.util.UUID; @@ -34,7 +33,7 @@ public final class SoldeviSteamBeast extends CardImpl { this.addAbility(ability); // {2}: Regenerate Soldevi Steam Beast. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateSourceEffect(), new GenericManaCost(3))); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateSourceEffect(), new GenericManaCost(2))); } private SoldeviSteamBeast(final SoldeviSteamBeast card) { diff --git a/Mage.Sets/src/mage/cards/s/SolitaryHunter.java b/Mage.Sets/src/mage/cards/s/SolitaryHunter.java index 12a6694d70f..29ca759d8e4 100644 --- a/Mage.Sets/src/mage/cards/s/SolitaryHunter.java +++ b/Mage.Sets/src/mage/cards/s/SolitaryHunter.java @@ -24,7 +24,6 @@ public final class SolitaryHunter extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(4); - this.transformable = true; this.secondSideCardClazz = mage.cards.o.OneOfThePack.class; // At the beginning of each upkeep, if no spells were cast last turn, transform Solitary Hunter. diff --git a/Mage.Sets/src/mage/cards/s/SomberwaldStag.java b/Mage.Sets/src/mage/cards/s/SomberwaldStag.java index 4bf30c5e7ce..3d3e33fda47 100644 --- a/Mage.Sets/src/mage/cards/s/SomberwaldStag.java +++ b/Mage.Sets/src/mage/cards/s/SomberwaldStag.java @@ -27,7 +27,7 @@ public final class SomberwaldStag extends CardImpl { // When Somberwald Stag enters the battlefield, you may have it fight target creature you don't control. Effect effect = new FightTargetSourceEffect(); - effect.setText("have it fight target creature you don't control"); + effect.setText("you may have it fight target creature you don't control"); Ability ability = new EntersBattlefieldTriggeredAbility(effect, true); ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/s/SongOfFreyalise.java b/Mage.Sets/src/mage/cards/s/SongOfFreyalise.java index e7bcf1e20d9..5d84aaa97fe 100644 --- a/Mage.Sets/src/mage/cards/s/SongOfFreyalise.java +++ b/Mage.Sets/src/mage/cards/s/SongOfFreyalise.java @@ -31,7 +31,7 @@ public final class SongOfFreyalise extends CardImpl { this.subtype.add(SubType.SAGA); // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) - SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); + SagaAbility sagaAbility = new SagaAbility(this); // I, II — Until your next turn, creatures you control gain "T: Add one mana of any color." sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_II, diff --git a/Mage.Sets/src/mage/cards/s/SonorousHowlbonder.java b/Mage.Sets/src/mage/cards/s/SonorousHowlbonder.java index c0c7bd752e1..6f3cfed3244 100644 --- a/Mage.Sets/src/mage/cards/s/SonorousHowlbonder.java +++ b/Mage.Sets/src/mage/cards/s/SonorousHowlbonder.java @@ -38,7 +38,7 @@ public final class SonorousHowlbonder extends CardImpl { this.toughness = new MageInt(2); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // Each creature you control with menace can't be blocked except by three or more creatures. ContinuousEffect effect = new CantBeBlockedByOneAllEffect(3, filter); diff --git a/Mage.Sets/src/mage/cards/s/SophinaSpearsageDeserter.java b/Mage.Sets/src/mage/cards/s/SophinaSpearsageDeserter.java new file mode 100644 index 00000000000..a37d62dff78 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SophinaSpearsageDeserter.java @@ -0,0 +1,62 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.keyword.InvestigateEffect; +import mage.abilities.keyword.FriendsForeverAbility; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterAttackingCreature; +import mage.filter.predicate.permanent.TokenPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SophinaSpearsageDeserter extends CardImpl { + + private static final FilterPermanent filter = new FilterAttackingCreature(); + + static { + filter.add(TokenPredicate.FALSE); + } + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter); + + public SophinaSpearsageDeserter(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Menace + this.addAbility(new MenaceAbility(false)); + + // Whenever Chief Jim Hopper attacks, investigate once for each nontoken attacking creature. + this.addAbility(new AttacksTriggeredAbility(new InvestigateEffect(xValue) + .setText("investigate once for each nontoken attacking creature"))); + + // Friends forever + this.addAbility(FriendsForeverAbility.getInstance()); + } + + private SophinaSpearsageDeserter(final SophinaSpearsageDeserter card) { + super(card); + } + + @Override + public SophinaSpearsageDeserter copy() { + return new SophinaSpearsageDeserter(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SorcererClass.java b/Mage.Sets/src/mage/cards/s/SorcererClass.java index 54ce5a22260..98ae21ab217 100644 --- a/Mage.Sets/src/mage/cards/s/SorcererClass.java +++ b/Mage.Sets/src/mage/cards/s/SorcererClass.java @@ -27,6 +27,7 @@ import mage.game.Game; import mage.game.events.GameEvent; import mage.game.stack.Spell; import mage.players.Player; +import mage.util.CardUtil; import mage.watchers.Watcher; import java.util.HashMap; @@ -182,7 +183,7 @@ class SorcererClassWatcher extends Watcher { if (spell == null || !spell.isInstantOrSorcery(game)) { return; } - spellMap.compute(spell.getControllerId(), (u, i) -> i == null ? 1 : Integer.sum(i, 1)); + spellMap.compute(spell.getControllerId(), CardUtil::setOrIncrementValue); } @Override diff --git a/Mage.Sets/src/mage/cards/s/SorinGrimNemesis.java b/Mage.Sets/src/mage/cards/s/SorinGrimNemesis.java index 3f480e66256..ddd6121791a 100644 --- a/Mage.Sets/src/mage/cards/s/SorinGrimNemesis.java +++ b/Mage.Sets/src/mage/cards/s/SorinGrimNemesis.java @@ -2,7 +2,6 @@ package mage.cards.s; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.dynamicvalue.common.GetXLoyaltyValue; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; @@ -28,7 +27,7 @@ public final class SorinGrimNemesis extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.SORIN); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(6)); + this.setStartingLoyalty(6); // +1: Reveal the top card of your library and put that card into your hand. Each opponent loses life equal to its converted mana cost. this.addAbility(new LoyaltyAbility(new SorinGrimNemesisRevealEffect(), 1)); diff --git a/Mage.Sets/src/mage/cards/s/SorinImperiousBloodlord.java b/Mage.Sets/src/mage/cards/s/SorinImperiousBloodlord.java index e1a7394fd9c..b5431960c15 100644 --- a/Mage.Sets/src/mage/cards/s/SorinImperiousBloodlord.java +++ b/Mage.Sets/src/mage/cards/s/SorinImperiousBloodlord.java @@ -2,7 +2,6 @@ package mage.cards.s; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.effects.OneShotEffect; @@ -52,7 +51,7 @@ public final class SorinImperiousBloodlord extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.SORIN); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Target creature you control gains deathtouch and lifelink until end of turn. If it's a Vampire, put a +1/+1 counter on it. Ability ability = new LoyaltyAbility(new SorinImperiousBloodlordEffect(), 1); diff --git a/Mage.Sets/src/mage/cards/s/SorinLordOfInnistrad.java b/Mage.Sets/src/mage/cards/s/SorinLordOfInnistrad.java index 0c4afea24bd..5a32d0a34a8 100644 --- a/Mage.Sets/src/mage/cards/s/SorinLordOfInnistrad.java +++ b/Mage.Sets/src/mage/cards/s/SorinLordOfInnistrad.java @@ -6,7 +6,6 @@ import java.util.Set; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.GetEmblemEffect; @@ -46,7 +45,7 @@ public final class SorinLordOfInnistrad extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.SORIN); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +1: Create a 1/1 black Vampire creature token with lifelink. this.addAbility(new LoyaltyAbility(new CreateTokenEffect(new SorinLordOfInnistradVampireToken()), 1)); diff --git a/Mage.Sets/src/mage/cards/s/SorinMarkov.java b/Mage.Sets/src/mage/cards/s/SorinMarkov.java index ec6f36d0976..55141a923f6 100644 --- a/Mage.Sets/src/mage/cards/s/SorinMarkov.java +++ b/Mage.Sets/src/mage/cards/s/SorinMarkov.java @@ -4,7 +4,6 @@ package mage.cards.s; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.GainLifeEffect; @@ -32,7 +31,7 @@ public final class SorinMarkov extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.SORIN); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +2: Sorin Markov deals 2 damage to any target and you gain 2 life. LoyaltyAbility ability1 = new LoyaltyAbility(new DamageTargetEffect(2), 2); diff --git a/Mage.Sets/src/mage/cards/s/SorinSolemnVisitor.java b/Mage.Sets/src/mage/cards/s/SorinSolemnVisitor.java index c4db2f47e8f..16670a0dfb4 100644 --- a/Mage.Sets/src/mage/cards/s/SorinSolemnVisitor.java +++ b/Mage.Sets/src/mage/cards/s/SorinSolemnVisitor.java @@ -3,7 +3,6 @@ package mage.cards.s; import java.util.UUID; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.GetEmblemEffect; @@ -31,7 +30,7 @@ public final class SorinSolemnVisitor extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.SORIN); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Until your next turn, creatures you control get +1/+0 and gain lifelink. Effect effect = new BoostControlledEffect(1, 0, Duration.UntilYourNextTurn, StaticFilters.FILTER_PERMANENT_CREATURES); diff --git a/Mage.Sets/src/mage/cards/s/SorinTheMirthless.java b/Mage.Sets/src/mage/cards/s/SorinTheMirthless.java new file mode 100644 index 00000000000..9985c1a51cf --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SorinTheMirthless.java @@ -0,0 +1,88 @@ +package mage.cards.s; + +import java.util.UUID; + +import mage.abilities.Ability; +import mage.abilities.LoyaltyAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.cards.*; +import mage.constants.*; +import mage.game.Game; +import mage.game.permanent.token.VampireLifelinkToken; +import mage.players.Player; +import mage.target.common.TargetAnyTarget; + +/** + * + * @author weirddan455 + */ +public final class SorinTheMirthless extends CardImpl { + + public SorinTheMirthless(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{2}{B}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.SORIN); + this.setStartingLoyalty(4); + + // +1: Look at the top card of your library. You may reveal that card and put it into your hand. If you do, you lose life equal to its mana value. + this.addAbility(new LoyaltyAbility(new SorinTheMirthlessEffect(), 1)); + + // −2: Create a 2/3 black Vampire creature token with flying and lifelink. + this.addAbility(new LoyaltyAbility(new CreateTokenEffect(new VampireLifelinkToken()), -2)); + + // −7: Sorin the Mirthless deals 13 damage to any target. You gain 13 life. + Ability ability = new LoyaltyAbility(new DamageTargetEffect(13), -7); + ability.addEffect(new GainLifeEffect(13)); + ability.addTarget(new TargetAnyTarget()); + this.addAbility(ability); + } + + private SorinTheMirthless(final SorinTheMirthless card) { + super(card); + } + + @Override + public SorinTheMirthless copy() { + return new SorinTheMirthless(this); + } +} + +class SorinTheMirthlessEffect extends OneShotEffect { + + public SorinTheMirthlessEffect() { + super(Outcome.DrawCard); + staticText = "Look at the top card of your library. You may reveal that card and put it into your hand. If you do, you lose life equal to its mana value"; + } + + private SorinTheMirthlessEffect(final SorinTheMirthlessEffect effect) { + super(effect); + } + + @Override + public SorinTheMirthlessEffect copy() { + return new SorinTheMirthlessEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + Card topCard = controller.getLibrary().getFromTop(game); + if (topCard != null) { + Cards cards = new CardsImpl(topCard); + controller.lookAtCards(source, null, cards, game); + if (controller.chooseUse(outcome, "Reveal " + topCard.getName() + " and put it into your hand?", source, game)) { + controller.revealCards(source, cards, game); + controller.moveCards(cards, Zone.HAND, source, game); + controller.loseLife(topCard.getManaValue(), game, source, false); + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SorinVampireLord.java b/Mage.Sets/src/mage/cards/s/SorinVampireLord.java index e82870f88db..0be6b50ddad 100644 --- a/Mage.Sets/src/mage/cards/s/SorinVampireLord.java +++ b/Mage.Sets/src/mage/cards/s/SorinVampireLord.java @@ -2,7 +2,6 @@ package mage.cards.s; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.common.DamageTargetEffect; @@ -35,7 +34,7 @@ public final class SorinVampireLord extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.SORIN); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Up to one target creature gets +2/+0 until end of turn. Ability ability = new LoyaltyAbility(new BoostTargetEffect(2, 0), 1); diff --git a/Mage.Sets/src/mage/cards/s/SorinVengefulBloodlord.java b/Mage.Sets/src/mage/cards/s/SorinVengefulBloodlord.java index a19cba0367f..fad3bfe7b01 100644 --- a/Mage.Sets/src/mage/cards/s/SorinVengefulBloodlord.java +++ b/Mage.Sets/src/mage/cards/s/SorinVengefulBloodlord.java @@ -2,7 +2,6 @@ package mage.cards.s; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.MyTurnCondition; import mage.abilities.costs.Cost; @@ -39,7 +38,7 @@ public final class SorinVengefulBloodlord extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.SORIN); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // As long as it's your turn, creatures and planeswalkers you control have lifelink. this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( diff --git a/Mage.Sets/src/mage/cards/s/SoulOfInnistrad.java b/Mage.Sets/src/mage/cards/s/SoulOfInnistrad.java index 8323d3ae7b9..618d80d18d7 100644 --- a/Mage.Sets/src/mage/cards/s/SoulOfInnistrad.java +++ b/Mage.Sets/src/mage/cards/s/SoulOfInnistrad.java @@ -1,4 +1,3 @@ - package mage.cards.s; import java.util.UUID; @@ -14,7 +13,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.common.FilterCreatureCard; +import mage.filter.StaticFilters; import mage.target.common.TargetCardInYourGraveyard; /** @@ -34,13 +33,13 @@ public final class SoulOfInnistrad extends CardImpl { this.addAbility(DeathtouchAbility.getInstance()); // {3}{B}{B}: Return up to three target creature cards from your graveyard to your hand. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ReturnFromGraveyardToHandTargetEffect(), new ManaCostsImpl("{3}{B}{B}")); - ability.addTarget(new TargetCardInYourGraveyard(0, 3, new FilterCreatureCard("creature cards from your graveyard"))); + ability.addTarget(new TargetCardInYourGraveyard(0, 3, StaticFilters.FILTER_CARD_CREATURES_YOUR_GRAVEYARD)); this.addAbility(ability); // {3}{B}{B}, Exile Soul of Innistrad from your graveyard: Return up to three target creature cards from your graveyard to your hand. ability = new SimpleActivatedAbility(Zone.GRAVEYARD, new ReturnFromGraveyardToHandTargetEffect(), new ManaCostsImpl("{3}{B}{B}")); ability.addCost(new ExileSourceFromGraveCost()); - ability.addTarget(new TargetCardInYourGraveyard(0, 3, new FilterCreatureCard("creature cards from your graveyard"))); + ability.addTarget(new TargetCardInYourGraveyard(0, 3, StaticFilters.FILTER_CARD_CREATURES_YOUR_GRAVEYARD)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SoulOfMagma.java b/Mage.Sets/src/mage/cards/s/SoulOfMagma.java index 3e47f4b9243..fa7299d412a 100644 --- a/Mage.Sets/src/mage/cards/s/SoulOfMagma.java +++ b/Mage.Sets/src/mage/cards/s/SoulOfMagma.java @@ -25,7 +25,7 @@ public final class SoulOfMagma extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); - Ability ability = new SpellCastControllerTriggeredAbility(new DamageTargetEffect(1), StaticFilters.SPIRIT_OR_ARCANE_CARD, false); + Ability ability = new SpellCastControllerTriggeredAbility(new DamageTargetEffect(1), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SoulReap.java b/Mage.Sets/src/mage/cards/s/SoulReap.java index f43e3b84089..e555ecec3d6 100644 --- a/Mage.Sets/src/mage/cards/s/SoulReap.java +++ b/Mage.Sets/src/mage/cards/s/SoulReap.java @@ -1,19 +1,14 @@ - package mage.cards.s; -import java.util.UUID; +import mage.MageObjectReference; import mage.ObjectColor; import mage.abilities.Ability; -import mage.abilities.condition.Condition; -import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.WatcherScope; -import mage.filter.FilterSpell; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ColorPredicate; @@ -26,9 +21,10 @@ import mage.players.Player; import mage.target.common.TargetCreaturePermanent; import mage.watchers.Watcher; +import java.util.*; + /** - * - * @author jeffwadsworth + * @author TheElk801 */ public final class SoulReap extends CardImpl { @@ -38,17 +34,13 @@ public final class SoulReap extends CardImpl { filter.add(Predicates.not(new ColorPredicate(ObjectColor.GREEN))); } - private String rule = "Its controller loses 3 life if you've cast another black spell this turn"; - public SoulReap(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}"); // Destroy target nongreen creature. Its controller loses 3 life if you've cast another black spell this turn. - this.getSpellAbility().addEffect(new DestroyTargetEffect()); + this.getSpellAbility().addEffect(new SoulReapEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); - this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new SoulReapEffect(), new CastBlackSpellThisTurnCondition(), rule)); - this.getSpellAbility().addWatcher(new SoulReapWatcher(this.getId())); - + this.getSpellAbility().addWatcher(new SoulReapWatcher()); } private SoulReap(final SoulReap card) { @@ -61,59 +53,12 @@ public final class SoulReap extends CardImpl { } } -class CastBlackSpellThisTurnCondition implements Condition { - - @Override - public boolean apply(Game game, Ability source) { - SoulReapWatcher watcher = game.getState().getWatcher(SoulReapWatcher.class, source.getControllerId()); - if (watcher != null) { - return watcher.conditionMet(); - } - return false; - } -} - -class SoulReapWatcher extends Watcher { - - private static final FilterSpell filter = new FilterSpell(); - - static { - filter.add(new ColorPredicate(ObjectColor.BLACK)); - } - - private UUID cardId; - - public SoulReapWatcher(UUID cardId) { - super(WatcherScope.PLAYER); - this.cardId = cardId; - } - - @Override - public void watch(GameEvent event, Game game) { - if (condition == true) { //no need to check - condition has already occured - return; - } - if (event.getType() == GameEvent.EventType.SPELL_CAST - && controllerId.equals(event.getPlayerId())) { - Spell spell = game.getStack().getSpell(event.getTargetId()); - if (!spell.getSourceId().equals(cardId) && filter.match(spell, game)) { - condition = true; - } - } - } - - @Override - public void reset() { - super.reset(); - condition = false; - } -} - class SoulReapEffect extends OneShotEffect { public SoulReapEffect() { super(Outcome.Detriment); - this.staticText = "Its controller loses 3 life if you've cast another black spell this turn"; + this.staticText = "destroy target nongreen creature. Its controller " + + "loses 3 life if you've cast another black spell this turn"; } public SoulReapEffect(final SoulReapEffect effect) { @@ -127,14 +72,55 @@ class SoulReapEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent creature = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); - if (creature != null) { - Player controller = game.getPlayer(creature.getControllerId()); - if (controller != null) { - controller.loseLife(3, game, source, false); - return true; - } + Permanent creature = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (creature == null) { + return false; } - return false; + creature.destroy(source, game); + if (!SoulReapWatcher.checkSpell(source, game)) { + return true; + } + Player controller = game.getPlayer(creature.getControllerId()); + if (controller != null) { + controller.loseLife(3, game, source, false); + } + return true; + } +} + +class SoulReapWatcher extends Watcher { + + private final Map> spellMap = new HashMap<>(); + private static final List emptyList = new ArrayList<>(); + + SoulReapWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() != EventType.SPELL_CAST) { + return; + } + Spell spell = game.getSpell(event.getSourceId()); + if (spell != null && spell.getColor(game).isBlack()) { + spellMap.computeIfAbsent(event.getPlayerId(), x -> new ArrayList<>()) + .add(new MageObjectReference(event.getSourceId(), game)); + } + } + + @Override + public void reset() { + super.reset(); + spellMap.clear(); + } + + static boolean checkSpell(Ability source, Game game) { + return game.getState() + .getWatcher(SoulReapWatcher.class) + .spellMap + .getOrDefault(source.getControllerId(), emptyList) + .stream() + .anyMatch(mor -> !mor.refersTo(source, game)); } } diff --git a/Mage.Sets/src/mage/cards/s/SoulSalvage.java b/Mage.Sets/src/mage/cards/s/SoulSalvage.java index c520775e3fc..edaba3341d6 100644 --- a/Mage.Sets/src/mage/cards/s/SoulSalvage.java +++ b/Mage.Sets/src/mage/cards/s/SoulSalvage.java @@ -1,4 +1,3 @@ - package mage.cards.s; import java.util.UUID; @@ -6,7 +5,7 @@ import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterCreatureCard; +import mage.filter.StaticFilters; import mage.target.common.TargetCardInYourGraveyard; /** @@ -20,7 +19,7 @@ public final class SoulSalvage extends CardImpl { // Return up to two target creature cards from your graveyard to your hand. this.getSpellAbility().addEffect(new ReturnFromGraveyardToHandTargetEffect()); - this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, 2, new FilterCreatureCard("creature cards from your graveyard"))); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, 2, StaticFilters.FILTER_CARD_CREATURES_YOUR_GRAVEYARD)); } private SoulSalvage(final SoulSalvage card) { diff --git a/Mage.Sets/src/mage/cards/s/SoulScarMage.java b/Mage.Sets/src/mage/cards/s/SoulScarMage.java index ba360f06b96..48a236ad182 100644 --- a/Mage.Sets/src/mage/cards/s/SoulScarMage.java +++ b/Mage.Sets/src/mage/cards/s/SoulScarMage.java @@ -1,4 +1,3 @@ - package mage.cards.s; import mage.MageInt; @@ -71,7 +70,7 @@ class SoulScarMageDamageReplacementEffect extends ReplacementEffectImpl { Permanent toGetCounters = game.getPermanent(event.getTargetId()); if (toGetCounters != null) { AddCountersTargetEffect addCounters = new AddCountersTargetEffect(CounterType.M1M1.createInstance(), StaticValue.get(event.getAmount())); - addCounters.setTargetPointer(new FixedTarget(toGetCounters.getId())); + addCounters.setTargetPointer(new FixedTarget(toGetCounters.getId(), game)); addCounters.apply(game, source); return true; } diff --git a/Mage.Sets/src/mage/cards/s/SoulSeizer.java b/Mage.Sets/src/mage/cards/s/SoulSeizer.java index f3b909ec3f6..0902e44ae2e 100644 --- a/Mage.Sets/src/mage/cards/s/SoulSeizer.java +++ b/Mage.Sets/src/mage/cards/s/SoulSeizer.java @@ -1,8 +1,5 @@ - package mage.cards.s; -import java.util.UUID; - import mage.MageInt; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; @@ -12,19 +9,20 @@ import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.game.Game; import mage.game.events.DamagedPlayerEvent; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** * @author BetaSteward */ @@ -34,7 +32,6 @@ public final class SoulSeizer extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{U}"); this.subtype.add(SubType.SPIRIT); - this.transformable = true; this.secondSideCardClazz = mage.cards.g.GhastlyHaunting.class; this.power = new MageInt(1); @@ -113,16 +110,11 @@ class SoulSeizerEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent != null && permanent.isTransformable()) { - if (permanent.transform(game)) { - game.informPlayers(permanent.getName() + " transforms into " + permanent.getSecondCardFace().getName()); - Permanent attachTo = game.getPermanent(targetPointer.getFirst(game, source)); - if (attachTo != null) { - return attachTo.addAttachment(source.getSourceId(), source, game); - } - } + if (permanent == null || !permanent.transform(source, game)) { + return false; } - return false; + Permanent attachTo = game.getPermanent(targetPointer.getFirst(game, source)); + return attachTo != null && attachTo.addAttachment(source.getSourceId(), source, game); } @Override diff --git a/Mage.Sets/src/mage/cards/s/SoulShred.java b/Mage.Sets/src/mage/cards/s/SoulShred.java index 5d9605db7f7..32bc68bb3de 100644 --- a/Mage.Sets/src/mage/cards/s/SoulShred.java +++ b/Mage.Sets/src/mage/cards/s/SoulShred.java @@ -1,16 +1,12 @@ - package mage.cards.s; import java.util.UUID; -import mage.ObjectColor; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.GainLifeEffect; 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; /** @@ -18,19 +14,13 @@ import mage.target.common.TargetCreaturePermanent; * @author fireshoes */ public final class SoulShred extends CardImpl { - - static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } public SoulShred(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{B}{B}"); // Soul Shred deals 3 damage to target nonblack creature. You gain 3 life. this.getSpellAbility().addEffect(new DamageTargetEffect(3)); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); this.getSpellAbility().addEffect(new GainLifeEffect(3)); } diff --git a/Mage.Sets/src/mage/cards/s/SoulStrings.java b/Mage.Sets/src/mage/cards/s/SoulStrings.java index 2dc59f32dee..4baa0b5c03d 100644 --- a/Mage.Sets/src/mage/cards/s/SoulStrings.java +++ b/Mage.Sets/src/mage/cards/s/SoulStrings.java @@ -6,7 +6,7 @@ import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterCreatureCard; +import mage.filter.StaticFilters; import mage.target.common.TargetCardInYourGraveyard; import java.util.UUID; @@ -25,7 +25,7 @@ public final class SoulStrings extends CardImpl { Effect effect = new DoUnlessAnyPlayerPaysEffect( new ReturnFromGraveyardToHandTargetEffect(), ManacostVariableValue.REGULAR); this.getSpellAbility().addEffect(effect); - this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(2, new FilterCreatureCard("creature cards from your graveyard"))); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(2, StaticFilters.FILTER_CARD_CREATURES_YOUR_GRAVEYARD)); } private SoulStrings(final SoulStrings card) { diff --git a/Mage.Sets/src/mage/cards/s/SoulTransfer.java b/Mage.Sets/src/mage/cards/s/SoulTransfer.java new file mode 100644 index 00000000000..47c5caeaf75 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SoulTransfer.java @@ -0,0 +1,81 @@ +package mage.cards.s; + +import java.util.UUID; + +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.condition.Condition; +import mage.abilities.effects.common.ExileTargetEffect; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.FilterCard; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCardInYourGraveyard; +import mage.target.common.TargetCreatureOrPlaneswalker; + +/** + * + * @author weirddan455 + */ +public final class SoulTransfer extends CardImpl { + + private static final FilterCard filter = new FilterCard("creature or planeswalker card from your graveyard"); + + static { + filter.add(Predicates.or(CardType.CREATURE.getPredicate(), CardType.PLANESWALKER.getPredicate())); + } + + public SoulTransfer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}{B}"); + + // Choose one. If you control an artifact and an enchantment as you cast this spell, you may choose both. + this.getSpellAbility().getModes().setChooseText( + "Choose one. If you control an artifact and an enchantment as you cast this spell, you may choose both." + ); + this.getSpellAbility().getModes().setMoreCondition(SoulTransferCondition.instance); + + // • Exile target creature or planeswalker. + this.getSpellAbility().addEffect(new ExileTargetEffect()); + this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalker()); + + // • Return target creature or planeswalker card from your graveyard to your hand. + Mode mode = new Mode(new ReturnFromGraveyardToHandTargetEffect()); + mode.addTarget(new TargetCardInYourGraveyard(filter)); + this.getSpellAbility().addMode(mode); + } + + private SoulTransfer(final SoulTransfer card) { + super(card); + } + + @Override + public SoulTransfer copy() { + return new SoulTransfer(this); + } +} + +enum SoulTransferCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + boolean artifact = false; + boolean enchantment = false; + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(source.getControllerId())) { + if (permanent.isArtifact(game)) { + artifact = true; + } + if (permanent.isEnchantment(game)) { + enchantment = true; + } + if (artifact && enchantment) { + return true; + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SoulbladeCorrupter.java b/Mage.Sets/src/mage/cards/s/SoulbladeCorrupter.java index 4216f7a17d3..ff625a7444c 100644 --- a/Mage.Sets/src/mage/cards/s/SoulbladeCorrupter.java +++ b/Mage.Sets/src/mage/cards/s/SoulbladeCorrupter.java @@ -13,8 +13,7 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SetTargetPointer; import mage.constants.SubType; -import mage.counters.CounterType; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -57,17 +56,11 @@ public final class SoulbladeCorrupter extends CardImpl { class SoulbladeCorrupterTriggeredAbility extends AttacksAllTriggeredAbility { - private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent("creature with a +1/+1 counter on it"); - - static { - filter2.add(CounterType.P1P1.getPredicate()); - } - SoulbladeCorrupterTriggeredAbility() { super(new GainAbilityTargetEffect( DeathtouchAbility.getInstance(), Duration.EndOfTurn - ).setText("that creature gains deathtouch until end of turn"), false, filter2, SetTargetPointer.PERMANENT, false); + ).setText("that creature gains deathtouch until end of turn"), false, StaticFilters.FILTER_CREATURE_P1P1, SetTargetPointer.PERMANENT, false); } SoulbladeCorrupterTriggeredAbility(final SoulbladeCorrupterTriggeredAbility effect) { diff --git a/Mage.Sets/src/mage/cards/s/SoulcipherBoard.java b/Mage.Sets/src/mage/cards/s/SoulcipherBoard.java new file mode 100644 index 00000000000..1f476a6b796 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SoulcipherBoard.java @@ -0,0 +1,74 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.common.PutCardIntoGraveFromAnywhereAllTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.SourceHasCounterCondition; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.common.LookLibraryAndPickControllerEffect; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.effects.common.counter.RemoveCounterSourceEffect; +import mage.abilities.keyword.TransformAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SoulcipherBoard extends CardImpl { + + private static final Condition condition = new SourceHasCounterCondition(CounterType.OMEN, 0, 0); + + public SoulcipherBoard(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}{U}"); + this.secondSideCardClazz = mage.cards.c.CipherboundSpirit.class; + + // Soulcipher Board enters the battlefield with three omen counters on it. + this.addAbility(new EntersBattlefieldAbility( + new AddCountersSourceEffect(CounterType.OMEN.createInstance(3)), + "with three omen counters on it" + )); + + // {1}{U}, {T}: Look at the top two cards of your library. Put one of them into your graveyard. + Ability ability = new SimpleActivatedAbility(new LookLibraryAndPickControllerEffect( + StaticValue.get(2), false, StaticValue.get(1), StaticFilters.FILTER_CARD, + Zone.LIBRARY, true, false, false, Zone.GRAVEYARD, false + ).setText("look at the top two cards of your library. Put one of them into your graveyard"), new ManaCostsImpl<>("{1}{U}")); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + + // Whenever a creature card is put into your graveyard from anywhere, remove an omen counter from Soulcipher Board. Then if it has no omen counters on it, transform it. + this.addAbility(new TransformAbility()); + ability = new PutCardIntoGraveFromAnywhereAllTriggeredAbility( + new RemoveCounterSourceEffect(CounterType.OMEN.createInstance()), + false, StaticFilters.FILTER_CARD_CREATURE_A, TargetController.YOU + ); + ability.addEffect(new ConditionalOneShotEffect( + new TransformSourceEffect(), condition, + "Then if it has no omen counters on it, transform it" + )); + this.addAbility(ability); + } + + private SoulcipherBoard(final SoulcipherBoard card) { + super(card); + } + + @Override + public SoulcipherBoard copy() { + return new SoulcipherBoard(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SoulstealerAxe.java b/Mage.Sets/src/mage/cards/s/SoulstealerAxe.java new file mode 100644 index 00000000000..addb47b7869 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SoulstealerAxe.java @@ -0,0 +1,81 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.common.DealsDamageToAPlayerAttachedTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.EquipAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.game.Game; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SoulstealerAxe extends CardImpl { + + public SoulstealerAxe(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}"); + + this.subtype.add(SubType.EQUIPMENT); + + // Equipped creature has trample. + this.addAbility(new SimpleStaticAbility(new GainAbilityAttachedEffect( + TrampleAbility.getInstance(), AttachmentType.EQUIPMENT + ))); + + // Whenever equipped creature deals combat damage to a player, seek a card with mana value equal to that damage. + this.addAbility(new DealsDamageToAPlayerAttachedTriggeredAbility( + new SoulstealerAxeEffect(), "equipped", false + )); + + // Equip {2} + this.addAbility(new EquipAbility(2)); + } + + private SoulstealerAxe(final SoulstealerAxe card) { + super(card); + } + + @Override + public SoulstealerAxe copy() { + return new SoulstealerAxe(this); + } +} + +class SoulstealerAxeEffect extends OneShotEffect { + + SoulstealerAxeEffect() { + super(Outcome.Benefit); + staticText = "seek a card with mana value equal to that damage"; + } + + private SoulstealerAxeEffect(final SoulstealerAxeEffect effect) { + super(effect); + } + + @Override + public SoulstealerAxeEffect copy() { + return new SoulstealerAxeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + int damage = (Integer) getValue("damage"); + FilterCard filter = new FilterCard(); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, damage)); + return player.seekCard(filter, source, game); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SoulswornSpirit.java b/Mage.Sets/src/mage/cards/s/SoulswornSpirit.java index a45cc07687c..fbde9965605 100644 --- a/Mage.Sets/src/mage/cards/s/SoulswornSpirit.java +++ b/Mage.Sets/src/mage/cards/s/SoulswornSpirit.java @@ -11,8 +11,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; /** @@ -20,12 +19,6 @@ import mage.target.common.TargetCreaturePermanent; * @author LevelX2 */ public final class SoulswornSpirit extends CardImpl { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } public SoulswornSpirit(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{U}"); @@ -40,7 +33,7 @@ public final class SoulswornSpirit extends CardImpl { // When Soulsworn Spirit enters the battlefield, detain target creature an opponent controls. //(Until your next turn, that creature can't attack or block and its activated abilities can't be activated.) Ability ability = new EntersBattlefieldTriggeredAbility(new DetainTargetEffect()); - TargetCreaturePermanent target = new TargetCreaturePermanent(filter); + TargetCreaturePermanent target = new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE); ability.addTarget(target); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SovereignsOfLostAlara.java b/Mage.Sets/src/mage/cards/s/SovereignsOfLostAlara.java index 3e55f7e6b4b..071bd68591f 100644 --- a/Mage.Sets/src/mage/cards/s/SovereignsOfLostAlara.java +++ b/Mage.Sets/src/mage/cards/s/SovereignsOfLostAlara.java @@ -1,10 +1,8 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.AttacksAloneControlledTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.ExaltedAbility; import mage.cards.Card; @@ -17,14 +15,13 @@ import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.predicate.card.AuraCardCanAttachToPermanentId; import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInLibrary; -import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; /** - * * @author jeffwadsworth */ public final class SovereignsOfLostAlara extends CardImpl { @@ -40,7 +37,7 @@ public final class SovereignsOfLostAlara extends CardImpl { this.addAbility(new ExaltedAbility()); // Whenever a creature you control attacks alone, you may search your library for an Aura card that could enchant that creature, put it onto the battlefield attached to that creature, then shuffle your library. - this.addAbility(new CreatureControlledAttacksAloneTriggeredAbility()); + this.addAbility(new AttacksAloneControlledTriggeredAbility(new SovereignsOfLostAlaraEffect())); } private SovereignsOfLostAlara(final SovereignsOfLostAlara card) { @@ -53,43 +50,6 @@ public final class SovereignsOfLostAlara extends CardImpl { } } -class CreatureControlledAttacksAloneTriggeredAbility extends TriggeredAbilityImpl { - - public CreatureControlledAttacksAloneTriggeredAbility() { - super(Zone.BATTLEFIELD, new SovereignsOfLostAlaraEffect(), true); - } - - public CreatureControlledAttacksAloneTriggeredAbility(final CreatureControlledAttacksAloneTriggeredAbility ability) { - super(ability); - } - - @Override - public CreatureControlledAttacksAloneTriggeredAbility copy() { - return new CreatureControlledAttacksAloneTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DECLARED_ATTACKERS; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (game.isActivePlayer(this.controllerId)) { - if (game.getCombat().attacksAlone()) { - this.getEffects().get(0).setTargetPointer(new FixedTarget(game.getCombat().getAttackers().get(0))); - return true; - } - } - return false; - } - - @Override - public String getTriggerPhrase() { - return "Whenever a creature you control attacks alone, " ; - } -} - class SovereignsOfLostAlaraEffect extends OneShotEffect { public SovereignsOfLostAlaraEffect() { @@ -109,7 +69,7 @@ class SovereignsOfLostAlaraEffect extends OneShotEffect { FilterCard filter = new FilterCard("aura that could enchant the lone attacking creature"); filter.add(SubType.AURA.getPredicate()); filter.add(new AuraCardCanAttachToPermanentId(attackingCreature.getId())); - if (controller.chooseUse(Outcome.Benefit, "Do you want to search your library?", source, game)) { + if (controller.chooseUse(Outcome.Benefit, "Search your library?", source, game)) { TargetCardInLibrary target = new TargetCardInLibrary(filter); target.setNotTarget(true); if (controller.searchLibrary(target, source, game)) { @@ -117,7 +77,7 @@ class SovereignsOfLostAlaraEffect extends OneShotEffect { Card aura = game.getCard(target.getFirstTarget()); game.getState().setValue("attachTo:" + aura.getId(), attackingCreature); controller.moveCards(aura, Zone.BATTLEFIELD, source, game); - return attackingCreature.addAttachment(aura.getId(), source, game); + attackingCreature.addAttachment(aura.getId(), source, game); } } } diff --git a/Mage.Sets/src/mage/cards/s/SparasHeadquarters.java b/Mage.Sets/src/mage/cards/s/SparasHeadquarters.java new file mode 100644 index 00000000000..445d1fb86ff --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SparasHeadquarters.java @@ -0,0 +1,48 @@ +package mage.cards.s; + +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.keyword.CyclingAbility; +import mage.abilities.mana.BlueManaAbility; +import mage.abilities.mana.GreenManaAbility; +import mage.abilities.mana.WhiteManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SparasHeadquarters extends CardImpl { + + public SparasHeadquarters(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + this.subtype.add(SubType.FOREST); + this.subtype.add(SubType.PLAINS); + this.subtype.add(SubType.ISLAND); + + // ({T}: Add {G}, {W}, or {U}.) + this.addAbility(new GreenManaAbility()); + this.addAbility(new WhiteManaAbility()); + this.addAbility(new BlueManaAbility()); + + // Spara's Headquarters enters the battlefield tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // Cycling {3} + this.addAbility(new CyclingAbility(new GenericManaCost(3))); + } + + private SparasHeadquarters(final SparasHeadquarters card) { + super(card); + } + + @Override + public SparasHeadquarters copy() { + return new SparasHeadquarters(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SpectralBinding.java b/Mage.Sets/src/mage/cards/s/SpectralBinding.java new file mode 100644 index 00000000000..d2aed3b2fd4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SpectralBinding.java @@ -0,0 +1,54 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.common.PutIntoGraveFromAnywhereSourceAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.ExileSourceEffect; +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.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SpectralBinding extends CardImpl { + + public SpectralBinding(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, ""); + + this.subtype.add(SubType.AURA); + this.color.setBlue(true); + this.nightCard = true; + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // Enchanted creature gets -2/-0. + this.addAbility(new SimpleStaticAbility(new BoostEnchantedEffect(-2, 0))); + + // If Spectral Binding would be put into a graveyard from anywhere, exile it instead. + this.addAbility(new PutIntoGraveFromAnywhereSourceAbility(new ExileSourceEffect().setText("exile it instead"))); + } + + private SpectralBinding(final SpectralBinding card) { + super(card); + } + + @Override + public SpectralBinding copy() { + return new SpectralBinding(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SpectralGuardian.java b/Mage.Sets/src/mage/cards/s/SpectralGuardian.java index 2c6ec2cfa37..4a93bd39fb8 100644 --- a/Mage.Sets/src/mage/cards/s/SpectralGuardian.java +++ b/Mage.Sets/src/mage/cards/s/SpectralGuardian.java @@ -40,7 +40,7 @@ public final class SpectralGuardian extends CardImpl { // As long as Spectral Guardian is untapped, noncreature artifacts have shroud. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect( new GainAbilityAllEffect(ShroudAbility.getInstance(), Duration.WhileOnBattlefield, filter, false), - new InvertCondition(SourceTappedCondition.instance), + SourceTappedCondition.UNTAPPED, "as long as {this} is untapped, noncreature artifacts have shroud."))); } diff --git a/Mage.Sets/src/mage/cards/s/SpectralPrison.java b/Mage.Sets/src/mage/cards/s/SpectralPrison.java index 1d27b0c154f..32b82d1d3fd 100644 --- a/Mage.Sets/src/mage/cards/s/SpectralPrison.java +++ b/Mage.Sets/src/mage/cards/s/SpectralPrison.java @@ -1,13 +1,11 @@ - package mage.cards.s; import java.util.UUID; -import mage.MageObject; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetAttachedTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.AttachEffect; -import mage.abilities.effects.common.DestroySourceEffect; import mage.abilities.effects.common.DontUntapInControllersUntapStepEnchantedEffect; +import mage.abilities.effects.common.SacrificeSourceEffect; import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -15,11 +13,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Outcome; import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.permanent.Permanent; -import mage.game.stack.Spell; +import mage.filter.StaticFilters; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; @@ -33,18 +27,17 @@ public final class SpectralPrison extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{U}"); this.subtype.add(SubType.AURA); - // Enchant creature TargetPermanent auraTarget = new TargetCreaturePermanent(); this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.Detriment)); this.addAbility(new EnchantAbility(auraTarget.getTargetName())); - + // Enchanted creature doesn't untap during its controller's untap step. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DontUntapInControllersUntapStepEnchantedEffect())); // When enchanted creature becomes the target of a spell, sacrifice Spectral Prison. - this.addAbility(new SpectralPrisonAbility()); + this.addAbility(new BecomesTargetAttachedTriggeredAbility(new SacrificeSourceEffect(), StaticFilters.FILTER_SPELL_A)); } private SpectralPrison(final SpectralPrison card) { @@ -56,44 +49,3 @@ public final class SpectralPrison extends CardImpl { return new SpectralPrison(this); } } - -class SpectralPrisonAbility extends TriggeredAbilityImpl { - - public SpectralPrisonAbility() { - super(Zone.BATTLEFIELD, new DestroySourceEffect()); - } - - public SpectralPrisonAbility(final SpectralPrisonAbility ability) { - super(ability); - } - - @Override - public SpectralPrisonAbility copy() { - return new SpectralPrisonAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - MageObject eventSourceObject = game.getObject(event.getSourceId()); - if (eventSourceObject instanceof Spell) { - Permanent enchantment = game.getPermanent(sourceId); - if (enchantment != null && enchantment.getAttachedTo() != null) { - if (event.getTargetId().equals(enchantment.getAttachedTo())) { - return true; - } - } - } - return false; - } - - @Override - public String getRule() { - return "When enchanted creature becomes the target of a spell or ability, destroy {this}."; - } - -} diff --git a/Mage.Sets/src/mage/cards/s/SpectralShepherd.java b/Mage.Sets/src/mage/cards/s/SpectralShepherd.java index 998cad57028..0805892178a 100644 --- a/Mage.Sets/src/mage/cards/s/SpectralShepherd.java +++ b/Mage.Sets/src/mage/cards/s/SpectralShepherd.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -12,35 +10,29 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; -import mage.constants.Zone; import mage.filter.common.FilterControlledPermanent; import mage.target.common.TargetControlledPermanent; +import java.util.UUID; + /** - * * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com) */ public final class SpectralShepherd extends CardImpl { - - private static final FilterControlledPermanent filter = new FilterControlledPermanent("Spirit"); - static { - filter.add(TargetController.YOU.getControllerPredicate()); - filter.add(SubType.SPIRIT.getPredicate()); - } + private static final FilterControlledPermanent filter = new FilterControlledPermanent(SubType.SPIRIT); public SpectralShepherd(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); this.subtype.add(SubType.SPIRIT); this.power = new MageInt(2); this.toughness = new MageInt(2); // Flying this.addAbility(FlyingAbility.getInstance()); - + // {1}{U}: Return target Spirit you control to its owner's hand. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ReturnToHandTargetEffect(), new ManaCostsImpl("{1}{U}")); + Ability ability = new SimpleActivatedAbility(new ReturnToHandTargetEffect(), new ManaCostsImpl<>("{1}{U}")); ability.addTarget(new TargetControlledPermanent(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SpectralSliver.java b/Mage.Sets/src/mage/cards/s/SpectralSliver.java index b617abfbaf5..f2777d2e113 100644 --- a/Mage.Sets/src/mage/cards/s/SpectralSliver.java +++ b/Mage.Sets/src/mage/cards/s/SpectralSliver.java @@ -33,7 +33,7 @@ public final class SpectralSliver extends CardImpl { // All Sliver creatures have "{2}: This creature gets +1/+1 until end of turn." Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1, 1, Duration.EndOfTurn).setText("this creature gets +1/+1 until end of turn"), new ManaCostsImpl("{2}")); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(ability, Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(ability, Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_ALL_SLIVERS))); } private SpectralSliver(final SpectralSliver card) { diff --git a/Mage.Sets/src/mage/cards/s/SpellPierce.java b/Mage.Sets/src/mage/cards/s/SpellPierce.java index 5bec4428b12..91525d3980f 100644 --- a/Mage.Sets/src/mage/cards/s/SpellPierce.java +++ b/Mage.Sets/src/mage/cards/s/SpellPierce.java @@ -8,8 +8,7 @@ import mage.abilities.effects.common.CounterUnlessPaysEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.FilterSpell; -import mage.filter.predicate.Predicates; +import mage.filter.StaticFilters; import mage.target.TargetSpell; /** @@ -18,19 +17,11 @@ import mage.target.TargetSpell; */ public final class SpellPierce extends CardImpl { - - private static final FilterSpell filter = new FilterSpell("noncreature spell"); - - static { - filter.add(Predicates.not(CardType.CREATURE.getPredicate())); - } - public SpellPierce(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{U}"); - - // Counter target noncreature spell unless its controller pays . - this.getSpellAbility().addTarget(new TargetSpell(filter)); + // Counter target noncreature spell unless its controller pays 2. + this.getSpellAbility().addTarget(new TargetSpell(StaticFilters.FILTER_SPELL_NON_CREATURE)); this.getSpellAbility().addEffect(new CounterUnlessPaysEffect(new GenericManaCost(2))); } @@ -42,5 +33,4 @@ public final class SpellPierce extends CardImpl { public SpellPierce copy() { return new SpellPierce(this); } - } diff --git a/Mage.Sets/src/mage/cards/s/SpellSwindle.java b/Mage.Sets/src/mage/cards/s/SpellSwindle.java index b224891a194..a284209fc04 100644 --- a/Mage.Sets/src/mage/cards/s/SpellSwindle.java +++ b/Mage.Sets/src/mage/cards/s/SpellSwindle.java @@ -1,10 +1,7 @@ - package mage.cards.s; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -14,8 +11,9 @@ import mage.game.permanent.token.TreasureToken; import mage.game.stack.StackObject; import mage.target.TargetSpell; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class SpellSwindle extends CardImpl { @@ -42,8 +40,7 @@ class SpellSwindleEffect extends OneShotEffect { public SpellSwindleEffect() { super(Outcome.Detriment); - staticText = "Counter target spell. Create X colorless Treasure artifact tokens, where X is that spell's mana value. " - + "They have \"{T}, Sacrifice this artifact: Add one mana of any color.\""; + staticText = "Counter target spell. Create X Treasure tokens, where X is that spell's mana value."; } public SpellSwindleEffect(final SpellSwindleEffect effect) { @@ -60,7 +57,7 @@ class SpellSwindleEffect extends OneShotEffect { StackObject stackObject = game.getStack().getStackObject(targetPointer.getFirst(game, source)); if (stackObject != null) { game.getStack().counter(source.getFirstTarget(), source, game); - return new CreateTokenEffect(new TreasureToken(), stackObject.getManaValue()).apply(game, source); + return new TreasureToken().putOntoBattlefield(stackObject.getManaValue(), game, source); } return false; } diff --git a/Mage.Sets/src/mage/cards/s/SpellbaneCentaur.java b/Mage.Sets/src/mage/cards/s/SpellbaneCentaur.java index ed5fe7e7721..9c0d8a73004 100644 --- a/Mage.Sets/src/mage/cards/s/SpellbaneCentaur.java +++ b/Mage.Sets/src/mage/cards/s/SpellbaneCentaur.java @@ -14,7 +14,7 @@ import mage.constants.Duration; import mage.constants.Zone; import mage.filter.FilterObject; import mage.filter.FilterStackObject; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; import mage.filter.predicate.mageobject.ColorPredicate; /** @@ -37,7 +37,7 @@ public final class SpellbaneCentaur extends CardImpl { // Creatures you control can't be the targets of blue spells or abilities from blue sources. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, - new CantBeTargetedAllEffect(new FilterControlledCreaturePermanent("Creatures you control"), + new CantBeTargetedAllEffect(StaticFilters.FILTER_CONTROLLED_CREATURES, filter, Duration.WhileOnBattlefield))); } diff --git a/Mage.Sets/src/mage/cards/s/SpellruneHowler.java b/Mage.Sets/src/mage/cards/s/SpellruneHowler.java index a7419fbe12c..e8c5b16fdcf 100644 --- a/Mage.Sets/src/mage/cards/s/SpellruneHowler.java +++ b/Mage.Sets/src/mage/cards/s/SpellruneHowler.java @@ -26,7 +26,6 @@ public final class SpellruneHowler extends CardImpl { this.toughness = new MageInt(4); this.color.setRed(true); this.nightCard = true; - this.transformable = true; // Whenever you cast an instant or sorcery spell, Spellrune Howler gets +2/+2 until end of turn. this.addAbility(new SpellCastControllerTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/s/SpellrunePainter.java b/Mage.Sets/src/mage/cards/s/SpellrunePainter.java index 8a7b20c3458..db0d71157e1 100644 --- a/Mage.Sets/src/mage/cards/s/SpellrunePainter.java +++ b/Mage.Sets/src/mage/cards/s/SpellrunePainter.java @@ -4,7 +4,6 @@ import mage.MageInt; import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.keyword.DayboundAbility; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -27,7 +26,6 @@ public final class SpellrunePainter extends CardImpl { this.subtype.add(SubType.WEREWOLF); this.power = new MageInt(2); this.toughness = new MageInt(3); - this.transformable = true; this.secondSideCardClazz = mage.cards.s.SpellruneHowler.class; // Whenever you cast an instant or sorcery spell, Spellrune Painter gets +1/+1 until end of turn. @@ -37,7 +35,6 @@ public final class SpellrunePainter extends CardImpl { )); // Daybound - this.addAbility(new TransformAbility()); this.addAbility(new DayboundAbility()); } diff --git a/Mage.Sets/src/mage/cards/s/SphereOfSafety.java b/Mage.Sets/src/mage/cards/s/SphereOfSafety.java index 2c5dbfe3137..7da3b29b7f6 100644 --- a/Mage.Sets/src/mage/cards/s/SphereOfSafety.java +++ b/Mage.Sets/src/mage/cards/s/SphereOfSafety.java @@ -54,7 +54,7 @@ class SphereOfSafetyPayManaToAttackAllEffect extends CantAttackYouUnlessPayManaA @Override public ManaCosts getManaCostToPay(GameEvent event, Ability source, Game game) { - int enchantments = game.getBattlefield().countAll(StaticFilters.FILTER_ENCHANTMENT_PERMANENT, source.getControllerId(), game); + int enchantments = game.getBattlefield().countAll(StaticFilters.FILTER_PERMANENT_ENCHANTMENT, source.getControllerId(), game); if (enchantments > 0) { return new ManaCostsImpl<>("{" + enchantments + '}'); } diff --git a/Mage.Sets/src/mage/cards/s/SphinxOfForesight.java b/Mage.Sets/src/mage/cards/s/SphinxOfForesight.java index a3aac1877c6..383488804f0 100644 --- a/Mage.Sets/src/mage/cards/s/SphinxOfForesight.java +++ b/Mage.Sets/src/mage/cards/s/SphinxOfForesight.java @@ -43,7 +43,7 @@ public final class SphinxOfForesight extends CardImpl { // At the beginning of your upkeep, scry 1. this.addAbility(new BeginningOfUpkeepTriggeredAbility( - Zone.BATTLEFIELD, new ScryEffect(1), + Zone.BATTLEFIELD, new ScryEffect(1, false), TargetController.YOU, false )); } diff --git a/Mage.Sets/src/mage/cards/s/SpikedRipsaw.java b/Mage.Sets/src/mage/cards/s/SpikedRipsaw.java new file mode 100644 index 00000000000..6b16b2be202 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SpikedRipsaw.java @@ -0,0 +1,55 @@ +package mage.cards.s; + +import java.util.UUID; + +import mage.abilities.common.AttacksAttachedTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.continuous.BoostEquippedEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.EquipAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.constants.AttachmentType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.common.FilterControlledPermanent; + +/** + * + * @author weirddan455 + */ +public final class SpikedRipsaw extends CardImpl { + + private static final FilterControlledPermanent filter = new FilterControlledPermanent(SubType.FOREST, "Forest"); + + public SpikedRipsaw(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{G}"); + + this.subtype.add(SubType.EQUIPMENT); + + // Equipped creature gets +3/+3. + this.addAbility(new SimpleStaticAbility(new BoostEquippedEffect(3, 3))); + + // Whenever equipped creature attacks, you may sacrifice a Forest. If you do, that creature gains trample until end of turn. + this.addAbility(new AttacksAttachedTriggeredAbility( + new DoIfCostPaid(new GainAbilityTargetEffect(TrampleAbility.getInstance(), Duration.EndOfTurn, "that creature gains trample until end of turn"), new SacrificeTargetCost(filter)), + AttachmentType.EQUIPMENT, false, true + )); + + // Equip {3} + this.addAbility(new EquipAbility(3)); + } + + private SpikedRipsaw(final SpikedRipsaw card) { + super(card); + } + + @Override + public SpikedRipsaw copy() { + return new SpikedRipsaw(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SpinalGraft.java b/Mage.Sets/src/mage/cards/s/SpinalGraft.java index 5b07b2a3f44..fc3b67bfe20 100644 --- a/Mage.Sets/src/mage/cards/s/SpinalGraft.java +++ b/Mage.Sets/src/mage/cards/s/SpinalGraft.java @@ -1,11 +1,10 @@ - package mage.cards.s; import java.util.UUID; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetAttachedTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.AttachEffect; -import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.DestroyAttachedToEffect; import mage.abilities.effects.common.continuous.BoostEnchantedEffect; import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; @@ -15,13 +14,8 @@ import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.permanent.Permanent; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; -import mage.target.targetpointer.FixedTarget; /** * @@ -30,7 +24,7 @@ import mage.target.targetpointer.FixedTarget; public final class SpinalGraft extends CardImpl { public SpinalGraft(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}"); this.subtype.add(SubType.AURA); // Enchant creature @@ -38,12 +32,12 @@ public final class SpinalGraft extends CardImpl { this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); this.addAbility(new EnchantAbility(auraTarget.getTargetName())); - + // Enchanted creature gets +3/+3. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(3, 3, Duration.WhileOnBattlefield))); - + // When enchanted creature becomes the target of a spell or ability, destroy that creature. It can't be regenerated. - this.addAbility(new SpinalGraftTriggeredAbility()); + this.addAbility(new BecomesTargetAttachedTriggeredAbility(new DestroyAttachedToEffect("that creature", true))); } private SpinalGraft(final SpinalGraft card) { @@ -55,42 +49,3 @@ public final class SpinalGraft extends CardImpl { return new SpinalGraft(this); } } - -class SpinalGraftTriggeredAbility extends TriggeredAbilityImpl { - - public SpinalGraftTriggeredAbility() { - super(Zone.BATTLEFIELD, new DestroyTargetEffect(true)); - } - - public SpinalGraftTriggeredAbility(final SpinalGraftTriggeredAbility ability) { - super(ability); - } - - @Override - public SpinalGraftTriggeredAbility copy() { - return new SpinalGraftTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent enchantment = game.getPermanent(sourceId); - if (enchantment != null && enchantment.getAttachedTo() != null) { - UUID enchanted = enchantment.getAttachedTo(); - if (event.getTargetId().equals(enchanted)) { - getEffects().get(0).setTargetPointer(new FixedTarget(enchanted)); - return true; - } - } - return false; - } - - @Override - public String getRule() { - return "When enchanted creature becomes the target of a spell or ability, destroy that creature. It can't be regenerated."; - } -} diff --git a/Mage.Sets/src/mage/cards/s/SpinedFluke.java b/Mage.Sets/src/mage/cards/s/SpinedFluke.java index 928b2863634..e4b46bf04c3 100644 --- a/Mage.Sets/src/mage/cards/s/SpinedFluke.java +++ b/Mage.Sets/src/mage/cards/s/SpinedFluke.java @@ -14,7 +14,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.ColoredManaSymbol; import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; /** * @@ -31,7 +31,8 @@ public final class SpinedFluke extends CardImpl { this.toughness = new MageInt(1); // When Spined Fluke enters the battlefield, sacrifice a creature. - this.addAbility(new EntersBattlefieldTriggeredAbility(new SacrificeControllerEffect(new FilterCreaturePermanent("a creature"), 1, ""))); + this.addAbility(new EntersBattlefieldTriggeredAbility(new SacrificeControllerEffect(StaticFilters.FILTER_PERMANENT_A_CREATURE, 1, ""))); + // {B}: Regenerate Spined Fluke. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateSourceEffect(), new ColoredManaCost(ColoredManaSymbol.B))); } diff --git a/Mage.Sets/src/mage/cards/s/SpinedSliver.java b/Mage.Sets/src/mage/cards/s/SpinedSliver.java index 5fa93ec12f1..3f45af3206c 100644 --- a/Mage.Sets/src/mage/cards/s/SpinedSliver.java +++ b/Mage.Sets/src/mage/cards/s/SpinedSliver.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.BecomesBlockedAllTriggeredAbility; @@ -11,14 +9,15 @@ import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; +import mage.constants.SubType; import mage.filter.StaticFilters; import mage.game.Game; import mage.game.combat.CombatGroup; +import java.util.UUID; + /** - * * @author KholdFuzion */ public final class SpinedSliver extends CardImpl { @@ -31,10 +30,11 @@ public final class SpinedSliver extends CardImpl { this.toughness = new MageInt(2); // Whenever a Sliver becomes blocked, that Sliver gets +1/+1 until end of turn for each creature blocking it. - BlockersCount value = new BlockersCount(); - Effect effect = new BoostTargetEffect(value, value, Duration.EndOfTurn, true); - effect.setText("it gets +1/+1 until end of turn for each creature blocking it"); - this.addAbility(new BecomesBlockedAllTriggeredAbility(effect, false, StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS, true)); + this.addAbility(new BecomesBlockedAllTriggeredAbility( + new BoostTargetEffect(BlockersCount.instance, BlockersCount.instance, Duration.EndOfTurn, true) + .setText("that Sliver gets +1/+1 until end of turn for each creature blocking it"), + false, StaticFilters.FILTER_PERMANENT_ALL_SLIVERS, true + ).setTriggerPhrase("Whenever a Sliver becomes blocked, ")); } private SpinedSliver(final SpinedSliver card) { @@ -47,18 +47,8 @@ public final class SpinedSliver extends CardImpl { } } -class BlockersCount implements DynamicValue { - - private final String message; - - public BlockersCount() { - this.message = "each creature blocking it"; - } - - public BlockersCount(final BlockersCount blockersCount) { - super(); - this.message = blockersCount.message; - } +enum BlockersCount implements DynamicValue { + instance; @Override public int calculate(Game game, Ability sourceAbility, Effect effect) { @@ -73,12 +63,12 @@ class BlockersCount implements DynamicValue { @Override public BlockersCount copy() { - return new BlockersCount(this); + return this; } @Override public String getMessage() { - return message; + return "each creature blocking it"; } @Override diff --git a/Mage.Sets/src/mage/cards/s/SpinningDarkness.java b/Mage.Sets/src/mage/cards/s/SpinningDarkness.java index 8346a1b7001..ddebe66d13b 100644 --- a/Mage.Sets/src/mage/cards/s/SpinningDarkness.java +++ b/Mage.Sets/src/mage/cards/s/SpinningDarkness.java @@ -1,4 +1,3 @@ - package mage.cards.s; import java.util.Iterator; @@ -19,8 +18,7 @@ import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.Predicates; +import mage.filter.StaticFilters; import mage.filter.predicate.mageobject.ColorPredicate; import mage.game.Game; import mage.players.Player; @@ -31,11 +29,6 @@ import mage.target.common.TargetCreaturePermanent; * @author emerald000 */ public final class SpinningDarkness extends CardImpl { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } public SpinningDarkness(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{4}{B}{B}"); @@ -46,7 +39,7 @@ public final class SpinningDarkness extends CardImpl { // Spinning Darkness deals 3 damage to target nonblack creature. You gain 3 life. this.getSpellAbility().addEffect(new DamageTargetEffect(3)); this.getSpellAbility().addEffect(new GainLifeEffect(3)); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); } private SpinningDarkness(final SpinningDarkness card) { @@ -60,7 +53,7 @@ public final class SpinningDarkness extends CardImpl { } class SpinningDarknessCost extends CostImpl { - + private static final FilterCard filter = new FilterCard("black card"); static { filter.add(new ColorPredicate(ObjectColor.BLACK)); diff --git a/Mage.Sets/src/mage/cards/s/SpinningWheelKick.java b/Mage.Sets/src/mage/cards/s/SpinningWheelKick.java new file mode 100644 index 00000000000..c4dedd9b9db --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SpinningWheelKick.java @@ -0,0 +1,88 @@ +package mage.cards.s; + +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.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.targetadjustment.TargetAdjuster; +import mage.target.targetpointer.SecondTargetPointer; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SpinningWheelKick extends CardImpl { + + public SpinningWheelKick(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{X}{G}{G}"); + + // Target creature you control deals damage equal to its power to each of X target creatures and/or planeswalkers. + this.getSpellAbility().addEffect(new SpinningWheelKickEffect()); + this.getSpellAbility().setTargetAdjuster(SpinningWheelKickAdjuster.instance); + } + + private SpinningWheelKick(final SpinningWheelKick card) { + super(card); + } + + @Override + public SpinningWheelKick copy() { + return new SpinningWheelKick(this); + } +} + +enum SpinningWheelKickAdjuster implements TargetAdjuster { + instance; + + @Override + public void adjustTargets(Ability ability, Game game) { + ability.getTargets().clear(); + ability.addTarget(new TargetControlledCreaturePermanent()); + ability.addTarget(new TargetPermanent( + ability.getManaCostsToPay().getX(), + StaticFilters.FILTER_PERMANENT_CREATURE_OR_PLANESWALKER + )); + } +} + +class SpinningWheelKickEffect extends OneShotEffect { + + SpinningWheelKickEffect() { + super(Outcome.Benefit); + staticText = "target creature you control deals damage equal " + + "to its power to each of X target creatures and/or planeswalkers"; + this.setTargetPointer(new SecondTargetPointer()); + } + + private SpinningWheelKickEffect(final SpinningWheelKickEffect effect) { + super(effect); + } + + @Override + public SpinningWheelKickEffect copy() { + return new SpinningWheelKickEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent creature = game.getPermanent(source.getFirstTarget()); + if (creature == null) { + return false; + } + for (UUID targetId : getTargetPointer().getTargets(game, source)) { + Permanent permanent = game.getPermanent(targetId); + if (permanent != null) { + permanent.damage(creature.getPower().getValue(), creature.getId(), source, game); + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SpireTracer.java b/Mage.Sets/src/mage/cards/s/SpireTracer.java index 36b5acc85c4..f1aafbfc45b 100644 --- a/Mage.Sets/src/mage/cards/s/SpireTracer.java +++ b/Mage.Sets/src/mage/cards/s/SpireTracer.java @@ -1,9 +1,8 @@ package mage.cards.s; import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.RestrictionEffect; +import mage.abilities.common.SimpleEvasionAbility; +import mage.abilities.effects.common.combat.CantBeBlockedByCreaturesSourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.ReachAbility; import mage.cards.CardImpl; @@ -11,9 +10,9 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.AbilityPredicate; import java.util.UUID; @@ -22,6 +21,17 @@ import java.util.UUID; */ public final class SpireTracer extends CardImpl { + private static final FilterCreaturePermanent notFlyingorReachCreatures = new FilterCreaturePermanent("except by creatures with flying or reach"); + + static { + notFlyingorReachCreatures.add(Predicates.not( + Predicates.or( + new AbilityPredicate(FlyingAbility.class), + new AbilityPredicate(ReachAbility.class) + ) + )); + } + public SpireTracer(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}"); this.subtype.add(SubType.ELF); @@ -31,7 +41,7 @@ public final class SpireTracer extends CardImpl { this.toughness = new MageInt(1); // Spire Tracer can't be blocked except by creatures with flying or reach. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantBeBlockedExceptByCreaturesWithFlyingOrReachEffect())); + this.addAbility(new SimpleEvasionAbility(new CantBeBlockedByCreaturesSourceEffect(notFlyingorReachCreatures, Duration.WhileOnBattlefield))); } @@ -44,31 +54,3 @@ public final class SpireTracer extends CardImpl { return new SpireTracer(this); } } - -class CantBeBlockedExceptByCreaturesWithFlyingOrReachEffect extends RestrictionEffect { - - public CantBeBlockedExceptByCreaturesWithFlyingOrReachEffect() { - super(Duration.WhileOnBattlefield); - staticText = "{this} can't be blocked except by creatures with flying or reach"; - } - - public CantBeBlockedExceptByCreaturesWithFlyingOrReachEffect(final CantBeBlockedExceptByCreaturesWithFlyingOrReachEffect effect) { - super(effect); - } - - @Override - public boolean applies(Permanent permanent, Ability source, Game game) { - return permanent.getId().equals(source.getSourceId()); - } - - @Override - public boolean canBeBlocked(Permanent attacker, Permanent blocker, Ability source, Game game, boolean canUseChooseDialogs) { - return blocker.getAbilities().containsKey(FlyingAbility.getInstance().getId()) - || blocker.getAbilities().containsKey(ReachAbility.getInstance().getId()); - } - - @Override - public CantBeBlockedExceptByCreaturesWithFlyingOrReachEffect copy() { - return new CantBeBlockedExceptByCreaturesWithFlyingOrReachEffect(this); - } -} diff --git a/Mage.Sets/src/mage/cards/s/SpiritShield.java b/Mage.Sets/src/mage/cards/s/SpiritShield.java index bb71b953aeb..922fcd4ff29 100644 --- a/Mage.Sets/src/mage/cards/s/SpiritShield.java +++ b/Mage.Sets/src/mage/cards/s/SpiritShield.java @@ -30,7 +30,7 @@ public final class SpiritShield extends CardImpl { this.addAbility(new SkipUntapOptionalAbility()); // {2}, {tap}: Target creature gets +0/+2 for as long as Spirit Shield remains tapped. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect( - new BoostTargetEffect(0, 2, Duration.Custom), SourceTappedCondition.instance, + new BoostTargetEffect(0, 2, Duration.Custom), SourceTappedCondition.TAPPED, "target creature gets +0/+2 for as long as {this} remains tapped"), new ManaCostsImpl("{2}")); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent()); diff --git a/Mage.Sets/src/mage/cards/s/SpiritSistersCall.java b/Mage.Sets/src/mage/cards/s/SpiritSistersCall.java new file mode 100644 index 00000000000..70ae4598860 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SpiritSistersCall.java @@ -0,0 +1,181 @@ +package mage.cards.s; + +import java.util.HashSet; +import java.util.UUID; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfYourEndStepTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +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.Zone; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.common.FilterPermanentCard; +import mage.filter.predicate.Predicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCardInYourGraveyard; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author weirddan455 + */ +public final class SpiritSistersCall extends CardImpl { + + private static final FilterPermanentCard filter = new FilterPermanentCard("permanent card in your graveyard"); + + public SpiritSistersCall(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{W}{B}"); + + // At the beginning of your end step, choose target permanent card in your graveyard. + // You may sacrifice a permanent that shares a card type with the chosen card. + // If you do, return the chosen card from your graveyard to the battlefield and it gains "If this permanent would leave the battlefield, exile it instead of putting it anywhere else." + Ability ability = new BeginningOfYourEndStepTriggeredAbility(new SpiritSistersCallDoIfEffect(), false); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + this.addAbility(ability); + } + + private SpiritSistersCall(final SpiritSistersCall card) { + super(card); + } + + @Override + public SpiritSistersCall copy() { + return new SpiritSistersCall(this); + } +} + +class SpiritSistersCallDoIfEffect extends OneShotEffect { + + public SpiritSistersCallDoIfEffect() { + super(Outcome.PutCardInPlay); + this.staticText = "choose target permanent card in your graveyard. " + + "You may sacrifice a permanent that shares a card type with the chosen card. " + + "If you do, return the chosen card from your graveyard to the battlefield and it gains \"If this permanent would leave the battlefield, exile it instead of putting it anywhere else.\""; + } + + private SpiritSistersCallDoIfEffect(final SpiritSistersCallDoIfEffect effect) { + super(effect); + } + + @Override + public SpiritSistersCallDoIfEffect copy() { + return new SpiritSistersCallDoIfEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + UUID targetId = source.getFirstTarget(); + Card card = game.getCard(targetId); + if (card == null || game.getState().getZone(targetId) != Zone.GRAVEYARD) { + return false; + } + FilterControlledPermanent filter = new FilterControlledPermanent("a permanent that shares a card type with the chosen card"); + filter.add(new SpiritSistersCallPredicate(new HashSet(card.getCardType(game)))); + return new DoIfCostPaid(new SpiritSistersCallReturnToBattlefieldEffect(), new SacrificeTargetCost(filter)).apply(game, source); + } +} + +class SpiritSistersCallReturnToBattlefieldEffect extends OneShotEffect { + + public SpiritSistersCallReturnToBattlefieldEffect() { + super(Outcome.PutCardInPlay); + } + + private SpiritSistersCallReturnToBattlefieldEffect(final SpiritSistersCallReturnToBattlefieldEffect effect) { + super(effect); + } + + @Override + public SpiritSistersCallReturnToBattlefieldEffect copy() { + return new SpiritSistersCallReturnToBattlefieldEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + UUID targetId = source.getFirstTarget(); + Card card = game.getCard(targetId); + if (controller == null || card == null || game.getState().getZone(targetId) != Zone.GRAVEYARD) { + return false; + } + controller.moveCards(card, Zone.BATTLEFIELD, source, game); + Permanent permanent = game.getPermanent(targetId); + if (permanent != null) { + ContinuousEffect effect = new GainAbilityTargetEffect(new SimpleStaticAbility(new SpiritSistersCallReplacementEffect()), Duration.Custom); + effect.setTargetPointer(new FixedTarget(permanent, game)); + game.addEffect(effect, source); + } + return true; + } +} + +class SpiritSistersCallReplacementEffect extends ReplacementEffectImpl { + + public SpiritSistersCallReplacementEffect() { + super(Duration.WhileOnBattlefield, Outcome.Exile); + this.staticText = "If {this} would leave the battlefield, exile it instead of putting it anywhere else"; + } + + private SpiritSistersCallReplacementEffect(final SpiritSistersCallReplacementEffect effect) { + super(effect); + } + + @Override + public SpiritSistersCallReplacementEffect copy() { + return new SpiritSistersCallReplacementEffect(this); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + ((ZoneChangeEvent) event).setToZone(Zone.EXILED); + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + UUID targetId = zEvent.getTargetId(); + return targetId != null && targetId.equals(source.getSourceId()) + && zEvent.getFromZone() == Zone.BATTLEFIELD && zEvent.getToZone() != Zone.EXILED; + } +} + +class SpiritSistersCallPredicate implements Predicate { + + private final HashSet cardTypes; + + public SpiritSistersCallPredicate(HashSet cardTypes) { + this.cardTypes = cardTypes; + } + + @Override + public boolean apply(MageObject input, Game game) { + for (CardType type : input.getCardType(game)) { + if (cardTypes.contains(type)) { + return true; + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SpiritedCompanion.java b/Mage.Sets/src/mage/cards/s/SpiritedCompanion.java new file mode 100644 index 00000000000..bc23191f44c --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SpiritedCompanion.java @@ -0,0 +1,37 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SpiritedCompanion extends CardImpl { + + public SpiritedCompanion(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{1}{W}"); + + this.subtype.add(SubType.DOG); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // When Spirited Companion enters the battlefield, draw a card. + this.addAbility(new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(1))); + } + + private SpiritedCompanion(final SpiritedCompanion card) { + super(card); + } + + @Override + public SpiritedCompanion copy() { + return new SpiritedCompanion(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SpiteMalice.java b/Mage.Sets/src/mage/cards/s/SpiteMalice.java index 99ddf290b70..27a4b2fc5fd 100644 --- a/Mage.Sets/src/mage/cards/s/SpiteMalice.java +++ b/Mage.Sets/src/mage/cards/s/SpiteMalice.java @@ -1,18 +1,13 @@ - package mage.cards.s; import java.util.UUID; -import mage.ObjectColor; import mage.abilities.effects.common.CounterTargetEffect; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardSetInfo; import mage.cards.SplitCard; import mage.constants.CardType; import mage.constants.SpellAbilityType; -import mage.filter.FilterSpell; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ColorPredicate; +import mage.filter.StaticFilters; import mage.target.TargetSpell; import mage.target.common.TargetCreaturePermanent; @@ -22,30 +17,18 @@ import mage.target.common.TargetCreaturePermanent; */ public final class SpiteMalice extends SplitCard { - private static final FilterSpell filterNonCreatureSpell = new FilterSpell("noncreature spell"); - - static { - filterNonCreatureSpell.add(Predicates.not(CardType.CREATURE.getPredicate())); - } - - private static final FilterCreaturePermanent filterNonBlackCreature = new FilterCreaturePermanent("nonblack creature"); - - static { - filterNonBlackCreature.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public SpiteMalice(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{U}", "{3}{B}", SpellAbilityType.SPLIT); // Spite // Counter target noncreature spell. this.getLeftHalfCard().getSpellAbility().addEffect(new CounterTargetEffect()); - this.getLeftHalfCard().getSpellAbility().addTarget(new TargetSpell(filterNonCreatureSpell)); + this.getLeftHalfCard().getSpellAbility().addTarget(new TargetSpell(StaticFilters.FILTER_SPELL_NON_CREATURE)); // Malice // Destroy target nonblack creature. It can't be regenerated. this.getRightHalfCard().getSpellAbility().addEffect(new DestroyTargetEffect(true)); - this.getRightHalfCard().getSpellAbility().addTarget(new TargetCreaturePermanent(filterNonBlackCreature)); + this.getRightHalfCard().getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); } private SpiteMalice(final SpiteMalice card) { diff --git a/Mage.Sets/src/mage/cards/s/SpitefulSliver.java b/Mage.Sets/src/mage/cards/s/SpitefulSliver.java index 099381472d2..fab6c735873 100644 --- a/Mage.Sets/src/mage/cards/s/SpitefulSliver.java +++ b/Mage.Sets/src/mage/cards/s/SpitefulSliver.java @@ -31,11 +31,11 @@ public final class SpitefulSliver extends CardImpl { // Sliver creatures you control have "Whenever this creature is dealt damage, it deals that much damage to target player or planeswalker." Ability ability = new DealtDamageToSourceTriggeredAbility( new SpitefulSliverEffect(), - false, false, true + false, false ); ability.addTarget(new TargetPlayer()); this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( - ability, Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS + ability, Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_ALL_SLIVERS ).setText("Sliver creatures you control have \"Whenever this creature is dealt damage, " + "it deals that much damage to target player or planeswalker.\"") )); diff --git a/Mage.Sets/src/mage/cards/s/SplinterTwin.java b/Mage.Sets/src/mage/cards/s/SplinterTwin.java index 2ed704d7408..584ff927417 100644 --- a/Mage.Sets/src/mage/cards/s/SplinterTwin.java +++ b/Mage.Sets/src/mage/cards/s/SplinterTwin.java @@ -81,7 +81,7 @@ class SplinterTwinEffect extends OneShotEffect { CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(source.getControllerId(), null, true); effect.setTargetPointer(new FixedTarget(permanent, game)); effect.apply(game, source); - for (Permanent addedToken : effect.getAddedPermanent()) { + for (Permanent addedToken : effect.getAddedPermanents()) { ExileTargetEffect exileEffect = new ExileTargetEffect(); exileEffect.setTargetPointer(new FixedTarget(addedToken, game)); DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); diff --git a/Mage.Sets/src/mage/cards/s/SplinteringWind.java b/Mage.Sets/src/mage/cards/s/SplinteringWind.java index 13e98844e98..c51db803580 100644 --- a/Mage.Sets/src/mage/cards/s/SplinteringWind.java +++ b/Mage.Sets/src/mage/cards/s/SplinteringWind.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; @@ -13,7 +11,10 @@ import mage.abilities.effects.common.DamageControllerEffect; import mage.abilities.effects.common.DamageTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; import mage.game.Game; import mage.game.events.GameEvent; @@ -23,8 +24,9 @@ import mage.game.permanent.token.SplinterToken; import mage.players.Player; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author L_J */ public final class SplinteringWind extends CardImpl { @@ -53,7 +55,7 @@ class SplinteringWindCreateTokenEffect extends OneShotEffect { public SplinteringWindCreateTokenEffect() { super(Outcome.PutCreatureInPlay); - staticText = "create a 1/1 green Splinter creature token. It has flying and “Cumulative upkeep {G}.” When it leaves the battlefield, it deals 1 damage to you and each creature you control"; + staticText = "create a 1/1 green Splinter creature token. It has flying and \"Cumulative upkeep {G}.\" When it leaves the battlefield, it deals 1 damage to you and each creature you control"; } public SplinteringWindCreateTokenEffect(final SplinteringWindCreateTokenEffect effect) { @@ -83,7 +85,7 @@ class SplinteringWindCreateTokenEffect extends OneShotEffect { } class SplinteringWindDelayedTriggeredAbility extends DelayedTriggeredAbility { - + private UUID tokenId; SplinteringWindDelayedTriggeredAbility(UUID tokenId) { diff --git a/Mage.Sets/src/mage/cards/s/SplittingHeadache.java b/Mage.Sets/src/mage/cards/s/SplittingHeadache.java index 457341276a0..fff7ab904a3 100644 --- a/Mage.Sets/src/mage/cards/s/SplittingHeadache.java +++ b/Mage.Sets/src/mage/cards/s/SplittingHeadache.java @@ -1,20 +1,12 @@ - package mage.cards.s; -import mage.abilities.Ability; import mage.abilities.Mode; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.discard.DiscardCardYouChooseTargetEffect; import mage.abilities.effects.common.discard.DiscardTargetEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.filter.FilterCard; -import mage.game.Game; -import mage.players.Player; -import mage.target.TargetCard; +import mage.constants.TargetController; import mage.target.TargetPlayer; import java.util.UUID; @@ -28,14 +20,16 @@ public final class SplittingHeadache extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{B}"); - // Choose one - Target player discards two cards; or target player reveals their hand, you choose a card from it, then that player discards that card. + // Choose one — + // • Target player discards two cards. this.getSpellAbility().addTarget(new TargetPlayer()); this.getSpellAbility().addEffect(new DiscardTargetEffect(2)); + + // • Target player reveals their hand. You choose a card from it. That player discards that card. Mode mode = new Mode(); - mode.addEffect(new SplittingHeadacheEffect()); + mode.addEffect(new DiscardCardYouChooseTargetEffect(TargetController.ANY)); mode.addTarget(new TargetPlayer()); this.getSpellAbility().addMode(mode); - } private SplittingHeadache(final SplittingHeadache card) { @@ -47,38 +41,3 @@ public final class SplittingHeadache extends CardImpl { return new SplittingHeadache(this); } } - -class SplittingHeadacheEffect extends OneShotEffect { - - public SplittingHeadacheEffect() { - super(Outcome.Discard); - this.staticText = "Target player reveals their hand, you choose a card from it, then that player discards that card."; - } - - public SplittingHeadacheEffect(final SplittingHeadacheEffect effect) { - super(effect); - } - - @Override - public SplittingHeadacheEffect copy() { - return new SplittingHeadacheEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getFirstTarget()); - if (player != null) { - player.revealCards("Splitting Headache", player.getHand(), game); - Player you = game.getPlayer(source.getControllerId()); - if (you != null) { - TargetCard target = new TargetCard(Zone.HAND, new FilterCard()); - if (you.choose(Outcome.Benefit, player.getHand(), target, game)) { - Card card = player.getHand().get(target.getFirstTarget(), game); - return player.discard(card, false, source, game); - - } - } - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/s/SporeCloud.java b/Mage.Sets/src/mage/cards/s/SporeCloud.java index f6a74c5efed..a79d04cad4a 100644 --- a/Mage.Sets/src/mage/cards/s/SporeCloud.java +++ b/Mage.Sets/src/mage/cards/s/SporeCloud.java @@ -1,4 +1,3 @@ - package mage.cards.s; import java.util.ArrayList; @@ -15,6 +14,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.AttackingPredicate; @@ -30,16 +30,11 @@ import mage.target.targetpointer.FixedTargets; */ public final class SporeCloud extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("blocking creatures"); - static { - filter.add(BlockingPredicate.instance); - } - public SporeCloud(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{G}{G}"); // Tap all blocking creatures. - this.getSpellAbility().addEffect(new TapAllEffect(filter)); + this.getSpellAbility().addEffect(new TapAllEffect(StaticFilters.FILTER_BLOCKING_CREATURES)); // Prevent all combat damage that would be dealt this turn. this.getSpellAbility().addEffect(new PreventAllDamageByAllPermanentsEffect(Duration.EndOfTurn, true)); // Each attacking creature and each blocking creature doesn't untap during its controller's next untap step. diff --git a/Mage.Sets/src/mage/cards/s/SporeCrawler.java b/Mage.Sets/src/mage/cards/s/SporeCrawler.java new file mode 100644 index 00000000000..54d52eba0d8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SporeCrawler.java @@ -0,0 +1,37 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SporeCrawler extends CardImpl { + + public SporeCrawler(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); + + this.subtype.add(SubType.FUNGUS); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // When Spore Crawler dies, draw a card. + this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1))); + } + + private SporeCrawler(final SporeCrawler card) { + super(card); + } + + @Override + public SporeCrawler copy() { + return new SporeCrawler(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SporebackTroll.java b/Mage.Sets/src/mage/cards/s/SporebackTroll.java index d694352465a..b49be9ed728 100644 --- a/Mage.Sets/src/mage/cards/s/SporebackTroll.java +++ b/Mage.Sets/src/mage/cards/s/SporebackTroll.java @@ -13,8 +13,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; -import mage.counters.CounterType; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; /** @@ -22,11 +21,6 @@ import mage.target.common.TargetCreaturePermanent; * @author fireshoes */ public final class SporebackTroll extends CardImpl { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature with a +1/+1 counter on it"); - static { - filter.add(CounterType.P1P1.getPredicate()); - } public SporebackTroll(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{G}"); @@ -40,7 +34,7 @@ public final class SporebackTroll extends CardImpl { // {1}{G}: Regenerate target creature with a +1/+1 counter on it. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateTargetEffect(), new ManaCostsImpl("{1}{G}")); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_A_CREATURE_P1P1)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SporebackWolf.java b/Mage.Sets/src/mage/cards/s/SporebackWolf.java new file mode 100644 index 00000000000..4829b90571e --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SporebackWolf.java @@ -0,0 +1,44 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.MyTurnCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.hint.common.MyTurnHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SporebackWolf extends CardImpl { + + public SporebackWolf(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); + + this.subtype.add(SubType.WOLF); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // As long as it's your turn, Sporeback Wolf gets +0/+2. + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new BoostSourceEffect(0, 2, Duration.WhileOnBattlefield), + MyTurnCondition.instance, "as long as it's your turn, {this} gets +0/+2." + )).addHint(MyTurnHint.instance)); + } + + private SporebackWolf(final SporebackWolf card) { + super(card); + } + + @Override + public SporebackWolf copy() { + return new SporebackWolf(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/Sporogenesis.java b/Mage.Sets/src/mage/cards/s/Sporogenesis.java index 5d3f1461ca0..1659defc86b 100644 --- a/Mage.Sets/src/mage/cards/s/Sporogenesis.java +++ b/Mage.Sets/src/mage/cards/s/Sporogenesis.java @@ -1,4 +1,3 @@ - package mage.cards.s; import java.util.UUID; @@ -52,7 +51,7 @@ public final class Sporogenesis extends CardImpl { this.addAbility(new SporogenesisTriggeredAbility()); // When Sporogenesis leaves the battlefield, remove all fungus counters from all creatures. - this.addAbility(new LeavesBattlefieldTriggeredAbility(new SporogenesisRemoveCountersEffect(), false)); + this.addAbility(new LeavesBattlefieldTriggeredAbility(new SporogenesisRemoveCountersEffect(), false)); } private Sporogenesis(final Sporogenesis card) { @@ -94,7 +93,7 @@ class SporogenesisTriggeredAbility extends TriggeredAbilityImpl { && permanent.isCreature(game) && permanent.getCounters(game).containsKey(CounterType.FUNGUS)) { Effect effect = this.getEffects().get(0); - effect.setTargetPointer(new FixedTarget(event.getTargetId())); + effect.setTargetPointer(new FixedTarget(event.getTargetId(), game)); return true; } } diff --git a/Mage.Sets/src/mage/cards/s/SpringLeafAvenger.java b/Mage.Sets/src/mage/cards/s/SpringLeafAvenger.java new file mode 100644 index 00000000000..d53f32e91e0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SpringLeafAvenger.java @@ -0,0 +1,52 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.abilities.keyword.NinjutsuAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.common.FilterPermanentCard; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SpringLeafAvenger extends CardImpl { + + private static final FilterCard filter = new FilterPermanentCard("permanent card from your graveyard"); + + public SpringLeafAvenger(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}{G}"); + + this.subtype.add(SubType.INSECT); + this.subtype.add(SubType.NINJA); + this.power = new MageInt(6); + this.toughness = new MageInt(5); + + // Ninjutsu {3}{G} + this.addAbility(new NinjutsuAbility("{3}{G}")); + + // Whenever Spring-Leaf Avenger deals combat damage to a player, return target permanent card from your graveyard to your hand. + Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility( + new ReturnFromGraveyardToHandTargetEffect(), false + ); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + this.addAbility(ability); + } + + private SpringLeafAvenger(final SpringLeafAvenger card) { + super(card); + } + + @Override + public SpringLeafAvenger copy() { + return new SpringLeafAvenger(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SquirrelMob.java b/Mage.Sets/src/mage/cards/s/SquirrelMob.java index ecca5201dee..a0db1770d85 100644 --- a/Mage.Sets/src/mage/cards/s/SquirrelMob.java +++ b/Mage.Sets/src/mage/cards/s/SquirrelMob.java @@ -1,44 +1,44 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.PermanentIdPredicate; +import mage.filter.FilterPermanent; +import mage.filter.predicate.mageobject.AnotherPredicate; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class SquirrelMob extends CardImpl { + private static final FilterPermanent filter = new FilterPermanent(SubType.SQUIRREL, ""); + + static { + filter.add(AnotherPredicate.instance); + } + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter); + public SquirrelMob(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{G}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}{G}"); this.subtype.add(SubType.SQUIRREL); this.power = new MageInt(2); this.toughness = new MageInt(2); // Squirrel Mob gets +1/+1 for each other Squirrel on the battlefield. - FilterCreaturePermanent filter = new FilterCreaturePermanent("other Squirrel"); - filter.add(SubType.SQUIRREL.getPredicate()); - filter.add(Predicates.not(new PermanentIdPredicate(this.getId()))); - DynamicValue xValue = new PermanentsOnBattlefieldCount(filter); - Effect effect = new BoostSourceEffect(xValue, xValue, Duration.WhileOnBattlefield, false); - effect.setText("{this} gets +1/+1 for each other Squirrel on the battlefield"); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); + this.addAbility(new SimpleStaticAbility(new BoostSourceEffect( + xValue, xValue, Duration.WhileOnBattlefield, false + ).setText("{this} gets +1/+1 for each other Squirrel on the battlefield"))); } private SquirrelMob(final SquirrelMob card) { diff --git a/Mage.Sets/src/mage/cards/s/SquirrelWrangler.java b/Mage.Sets/src/mage/cards/s/SquirrelWrangler.java index 8ae89334cd8..662c87a9eec 100644 --- a/Mage.Sets/src/mage/cards/s/SquirrelWrangler.java +++ b/Mage.Sets/src/mage/cards/s/SquirrelWrangler.java @@ -1,14 +1,12 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.CreateTokenEffect; -import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.abilities.effects.common.continuous.BoostAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -20,8 +18,9 @@ import mage.filter.common.FilterCreaturePermanent; import mage.game.permanent.token.SquirrelToken; import mage.target.common.TargetControlledPermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class SquirrelWrangler extends CardImpl { @@ -29,7 +28,7 @@ public final class SquirrelWrangler extends CardImpl { private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(SubType.SQUIRREL, "Squirrel creatures"); public SquirrelWrangler(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{G}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{G}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.DRUID); @@ -42,7 +41,7 @@ public final class SquirrelWrangler extends CardImpl { this.addAbility(ability); // {1}{G}, Sacrifice a land: Squirrel creatures get +1/+1 until end of turn. - ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostControlledEffect(1,1,Duration.EndOfTurn, filter), new ManaCostsImpl("{1}{G}")); + ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostAllEffect(1, 1, Duration.EndOfTurn, filter, false), new ManaCostsImpl("{1}{G}")); ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(new FilterControlledLandPermanent("a land")))); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/s/StalkingPredator.java b/Mage.Sets/src/mage/cards/s/StalkingPredator.java index 98ce16017b5..41a12f42f97 100644 --- a/Mage.Sets/src/mage/cards/s/StalkingPredator.java +++ b/Mage.Sets/src/mage/cards/s/StalkingPredator.java @@ -22,7 +22,6 @@ public final class StalkingPredator extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(4); this.color.setBlack(true); - this.transformable = true; this.nightCard = true; // Menace diff --git a/Mage.Sets/src/mage/cards/s/StalkingVampire.java b/Mage.Sets/src/mage/cards/s/StalkingVampire.java index 66065d0fbac..c126b2078ec 100644 --- a/Mage.Sets/src/mage/cards/s/StalkingVampire.java +++ b/Mage.Sets/src/mage/cards/s/StalkingVampire.java @@ -1,23 +1,17 @@ package mage.cards.s; import mage.MageInt; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.TransformSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; +import mage.constants.TargetController; import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.costs.Cost; -import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.TransformSourceEffect; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; /** * @author nantuko @@ -30,13 +24,15 @@ public final class StalkingVampire extends CardImpl { this.color.setBlack(true); this.nightCard = true; - this.transformable = true; this.power = new MageInt(5); this.toughness = new MageInt(5); // At the beginning of your upkeep, you may pay {2}{B}{B}. If you do, transform Stalking Vampire. - this.addAbility(new StalkingVampireBeginningOfUpkeepTriggeredAbility()); + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new DoIfCostPaid( + new TransformSourceEffect(), + new ManaCostsImpl<>("{2}{B}{B}") + ), TargetController.YOU, false)); } private StalkingVampire(final StalkingVampire card) { @@ -48,65 +44,3 @@ public final class StalkingVampire extends CardImpl { return new StalkingVampire(this); } } - -class StalkingVampireBeginningOfUpkeepTriggeredAbility extends TriggeredAbilityImpl { - - public StalkingVampireBeginningOfUpkeepTriggeredAbility() { - super(Zone.BATTLEFIELD, new StalkingVampireTransformSourceEffect(), true); - } - - public StalkingVampireBeginningOfUpkeepTriggeredAbility(final StalkingVampireBeginningOfUpkeepTriggeredAbility ability) { - super(ability); - } - - @Override - public StalkingVampireBeginningOfUpkeepTriggeredAbility copy() { - return new StalkingVampireBeginningOfUpkeepTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.UPKEEP_STEP_PRE; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return event.getPlayerId().equals(this.controllerId); - } - - @Override - public String getRule() { - return "At the beginning of your upkeep, you may pay {2}{B}{B}. If you do, transform {this}."; - } -} - -class StalkingVampireTransformSourceEffect extends OneShotEffect { - - public StalkingVampireTransformSourceEffect() { - super(Outcome.Transform); - staticText = "transform {this}"; - } - - public StalkingVampireTransformSourceEffect(final StalkingVampireTransformSourceEffect effect) { - super(effect); - } - - @Override - public StalkingVampireTransformSourceEffect copy() { - return new StalkingVampireTransformSourceEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent != null) { - Cost cost = new ManaCostsImpl("{2}{B}{B}"); - if (cost.pay(source, game, source, permanent.getControllerId(), false, null)) { - new TransformSourceEffect(false).apply(game, source); - } - return true; - } - return false; - } - -} diff --git a/Mage.Sets/src/mage/cards/s/Stampede.java b/Mage.Sets/src/mage/cards/s/Stampede.java index 11a806beab0..38f68f67150 100644 --- a/Mage.Sets/src/mage/cards/s/Stampede.java +++ b/Mage.Sets/src/mage/cards/s/Stampede.java @@ -10,7 +10,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.common.FilterAttackingCreature; +import mage.filter.StaticFilters; /** * @@ -22,10 +22,10 @@ public final class Stampede extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{G}{G}"); // Attacking creatures get +1/+0 and gain trample until end of turn. - Effect effect = new BoostAllEffect(1, 0, Duration.EndOfTurn, new FilterAttackingCreature(), false); + Effect effect = new BoostAllEffect(1, 0, Duration.EndOfTurn, StaticFilters.FILTER_ATTACKING_CREATURES, false); effect.setText("attacking creatures get +1/+0"); this.getSpellAbility().addEffect(effect); - effect = new GainAbilityAllEffect(TrampleAbility.getInstance(), Duration.EndOfTurn, new FilterAttackingCreature()); + effect = new GainAbilityAllEffect(TrampleAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_ATTACKING_CREATURES); effect.setText("and gain trample until end of turn"); this.getSpellAbility().addEffect(effect); } diff --git a/Mage.Sets/src/mage/cards/s/StartledAwake.java b/Mage.Sets/src/mage/cards/s/StartledAwake.java index ee34baf9f96..d2709bba456 100644 --- a/Mage.Sets/src/mage/cards/s/StartledAwake.java +++ b/Mage.Sets/src/mage/cards/s/StartledAwake.java @@ -1,8 +1,6 @@ - package mage.cards.s; -import java.util.UUID; - +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.ActivateAsSorceryActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; @@ -19,6 +17,8 @@ import mage.game.Game; import mage.players.Player; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** * @author LevelX2 */ @@ -27,7 +27,6 @@ public final class StartledAwake extends CardImpl { public StartledAwake(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{U}{U}"); - this.transformable = true; this.secondSideCardClazz = mage.cards.p.PersistentNightmare.class; // Target opponent puts the top thirteen cards of their library into their graveyard. @@ -36,9 +35,9 @@ public final class StartledAwake extends CardImpl { // {3}{U}{U}: Put Startled Awake from your graveyard onto the battlefield transformed. Activate this ability only any time you could cast a sorcery. this.addAbility(new TransformAbility()); - Ability ability = new ActivateAsSorceryActivatedAbility(Zone.GRAVEYARD, new StartledAwakeReturnTransformedEffect(), new ManaCostsImpl("{3}{U}{U}")); - this.addAbility(ability); - + this.addAbility(new ActivateAsSorceryActivatedAbility( + Zone.GRAVEYARD, new StartledAwakeReturnTransformedEffect(), new ManaCostsImpl<>("{3}{U}{U}") + )); } private StartledAwake(final StartledAwake card) { @@ -70,16 +69,16 @@ class StartledAwakeReturnTransformedEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - if (game.getState().getZone(source.getSourceId()) == Zone.GRAVEYARD) { - game.getState().setValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + source.getSourceId(), Boolean.TRUE); - Card card = game.getCard(source.getSourceId()); - if (card != null) { - controller.moveCards(card, Zone.BATTLEFIELD, source, game); - } - } + MageObject sourceObject = source.getSourceObjectIfItStillExists(game); + if (controller == null || !(sourceObject instanceof Card)) { + return false; + } + if (game.getState().getZone(source.getSourceId()) != Zone.GRAVEYARD) { return true; } - return false; + Card card = (Card) sourceObject; + game.getState().setValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + source.getSourceId(), true); + controller.moveCards(card, Zone.BATTLEFIELD, source, game); + return true; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/s/StatuteOfDenial.java b/Mage.Sets/src/mage/cards/s/StatuteOfDenial.java index c81b0c419c3..794d2716ad5 100644 --- a/Mage.Sets/src/mage/cards/s/StatuteOfDenial.java +++ b/Mage.Sets/src/mage/cards/s/StatuteOfDenial.java @@ -36,7 +36,7 @@ public final class StatuteOfDenial extends CardImpl { this.getSpellAbility().addTarget(new TargetSpell()); this.getSpellAbility().addEffect(new ConditionalOneShotEffect( new DrawDiscardControllerEffect(1,1), - new PermanentsOnTheBattlefieldCondition(filter, ComparisonType.MORE_THAN, 0, true), + new PermanentsOnTheBattlefieldCondition(filter), "If you control a blue creature, draw a card, then discard a card")); } diff --git a/Mage.Sets/src/mage/cards/s/SteelcladSpirit.java b/Mage.Sets/src/mage/cards/s/SteelcladSpirit.java new file mode 100644 index 00000000000..010fbe4265a --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SteelcladSpirit.java @@ -0,0 +1,48 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.effects.common.combat.CanAttackAsThoughItDidntHaveDefenderSourceEffect; +import mage.abilities.keyword.DefenderAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterEnchantmentPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SteelcladSpirit extends CardImpl { + + private static final FilterPermanent filter = new FilterEnchantmentPermanent("an enchantment"); + + public SteelcladSpirit(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); + + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Defender + this.addAbility(DefenderAbility.getInstance()); + + // Whenever an enchantment enters the battlefield under your control, Steelclad Spirit can attack this turn as though it didn't have defender. + this.addAbility(new EntersBattlefieldControlledTriggeredAbility( + new CanAttackAsThoughItDidntHaveDefenderSourceEffect(Duration.EndOfTurn), filter + )); + } + + private SteelcladSpirit(final SteelcladSpirit card) { + super(card); + } + + @Override + public SteelcladSpirit copy() { + return new SteelcladSpirit(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SteelformSliver.java b/Mage.Sets/src/mage/cards/s/SteelformSliver.java index 1f7c1206a89..8e639460ab5 100644 --- a/Mage.Sets/src/mage/cards/s/SteelformSliver.java +++ b/Mage.Sets/src/mage/cards/s/SteelformSliver.java @@ -29,7 +29,7 @@ public final class SteelformSliver extends CardImpl { // Sliver creatures you control get +0/+1. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(0, 1, Duration.WhileOnBattlefield, - StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS, false))); + StaticFilters.FILTER_PERMANENT_SLIVERS, false))); } private SteelformSliver(final SteelformSliver card) { diff --git a/Mage.Sets/src/mage/cards/s/StensiaUprising.java b/Mage.Sets/src/mage/cards/s/StensiaUprising.java new file mode 100644 index 00000000000..ee79b894364 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/StensiaUprising.java @@ -0,0 +1,69 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.DoWhenCostPaid; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.TargetController; +import mage.filter.StaticFilters; +import mage.game.permanent.token.RedHumanToken; +import mage.target.common.TargetAnyTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class StensiaUprising extends CardImpl { + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition( + StaticFilters.FILTER_PERMANENT, ComparisonType.EQUAL_TO, 13, true + ); + private static final Hint hint = new ValueHint( + "Permanents you control", + new PermanentsOnBattlefieldCount( + StaticFilters.FILTER_CONTROLLED_PERMANENT + ) + ); + + public StensiaUprising(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{R}{R}"); + + // At the beginning of your end step, create a 1/1 red Human creature token. Then if you control exactly thirteen permanents, you may sacrifice Stensia Uprising. When you do, it deals 7 damage to any target. + ReflexiveTriggeredAbility reflexiveTrigger = new ReflexiveTriggeredAbility( + new DamageTargetEffect(7), false, "{this} deals 7 damage to any target" + ); + reflexiveTrigger.addTarget(new TargetAnyTarget()); + Ability ability = new BeginningOfEndStepTriggeredAbility( + new CreateTokenEffect(new RedHumanToken()), TargetController.YOU, false + ); + ability.addEffect(new ConditionalOneShotEffect( + new DoWhenCostPaid(reflexiveTrigger, new SacrificeSourceCost(), "Sacrifice {this}?"), + condition, "Then if you control exactly thirteen permanents, " + + "you may sacrifice {this}. When you do, it deals 7 damage to any target." + )); + this.addAbility(ability.addHint(hint)); + } + + private StensiaUprising(final StensiaUprising card) { + super(card); + } + + @Override + public StensiaUprising copy() { + return new StensiaUprising(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SteppeGlider.java b/Mage.Sets/src/mage/cards/s/SteppeGlider.java index 2e13aaf7bba..ea4a6830d26 100644 --- a/Mage.Sets/src/mage/cards/s/SteppeGlider.java +++ b/Mage.Sets/src/mage/cards/s/SteppeGlider.java @@ -16,8 +16,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Zone; -import mage.counters.CounterType; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; /** @@ -26,12 +25,6 @@ import mage.target.common.TargetCreaturePermanent; */ public final class SteppeGlider extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature with a +1/+1 counter on it"); - - static { - filter.add(CounterType.P1P1.getPredicate()); - } - public SteppeGlider(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{W}"); this.subtype.add(SubType.ELEMENTAL); @@ -51,7 +44,7 @@ public final class SteppeGlider extends CardImpl { effect = new GainAbilityTargetEffect(VigilanceAbility.getInstance(), Duration.EndOfTurn); effect.setText("and vigilance until end of turn"); ability.addEffect(effect); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_A_CREATURE_P1P1)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SternJudge.java b/Mage.Sets/src/mage/cards/s/SternJudge.java index 11cd7d885f9..234bbef5740 100644 --- a/Mage.Sets/src/mage/cards/s/SternJudge.java +++ b/Mage.Sets/src/mage/cards/s/SternJudge.java @@ -1,23 +1,23 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.OneShotEffect; -import mage.constants.SubType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; import mage.game.Game; import mage.players.Player; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class SternJudge extends CardImpl { @@ -46,11 +46,7 @@ public final class SternJudge extends CardImpl { class SternJudgeEffect extends OneShotEffect { - private static final FilterPermanent filter = new FilterPermanent("Swamp"); - - static { - filter.add(SubType.SWAMP.getPredicate()); - } + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.SWAMP); SternJudgeEffect() { super(Outcome.Benefit); @@ -68,12 +64,14 @@ class SternJudgeEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - for (UUID playerId : game.getState().getPlayersInRange(source.getSourceId(), game)) { + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { Player player = game.getPlayer(playerId); - if (player != null) { - int lifeToLose = game.getBattlefield().getAllActivePermanents(filter, playerId, game).size(); - player.loseLife(lifeToLose, game, source, false); + if (player == null) { + continue; } + player.loseLife(game.getBattlefield().count( + filter, playerId, source.getSourceId(), game + ), game, source, false); } return true; } diff --git a/Mage.Sets/src/mage/cards/s/Stingscourger.java b/Mage.Sets/src/mage/cards/s/Stingscourger.java index a7abcc1a3ef..ed9b214ad50 100644 --- a/Mage.Sets/src/mage/cards/s/Stingscourger.java +++ b/Mage.Sets/src/mage/cards/s/Stingscourger.java @@ -11,8 +11,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; /** @@ -21,12 +20,6 @@ import mage.target.common.TargetCreaturePermanent; */ public final class Stingscourger extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public Stingscourger(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{R}"); this.subtype.add(SubType.GOBLIN); @@ -39,7 +32,7 @@ public final class Stingscourger extends CardImpl { this.addAbility(new EchoAbility("{3}{R}")); // When Stingscourger enters the battlefield, return target creature an opponent controls to its owner's hand. Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnToHandTargetEffect()); - TargetCreaturePermanent target = new TargetCreaturePermanent(filter); + TargetCreaturePermanent target = new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE); ability.addTarget(target); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/StitchedAssistant.java b/Mage.Sets/src/mage/cards/s/StitchedAssistant.java new file mode 100644 index 00000000000..ad559079333 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/StitchedAssistant.java @@ -0,0 +1,45 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.ExploitCreatureTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.keyword.ScryEffect; +import mage.abilities.keyword.ExploitAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class StitchedAssistant extends CardImpl { + + public StitchedAssistant(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); + + this.subtype.add(SubType.ZOMBIE); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // Exploit + this.addAbility(new ExploitAbility()); + + // When Stitched Assistant exploits a creature, scry 1, then draw a card. + Ability ability = new ExploitCreatureTriggeredAbility(new ScryEffect(1, false)); + ability.addEffect(new DrawCardSourceControllerEffect(1).concatBy(", then")); + this.addAbility(ability); + } + + private StitchedAssistant(final StitchedAssistant card) { + super(card); + } + + @Override + public StitchedAssistant copy() { + return new StitchedAssistant(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/StitchedMangler.java b/Mage.Sets/src/mage/cards/s/StitchedMangler.java index 58d7104f64a..0487d1754ee 100644 --- a/Mage.Sets/src/mage/cards/s/StitchedMangler.java +++ b/Mage.Sets/src/mage/cards/s/StitchedMangler.java @@ -12,8 +12,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; /** @@ -22,12 +21,6 @@ import mage.target.common.TargetCreaturePermanent; */ public final class StitchedMangler extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public StitchedMangler(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{U}"); this.subtype.add(SubType.ZOMBIE); @@ -43,7 +36,7 @@ public final class StitchedMangler extends CardImpl { Effect effect = new DontUntapInControllersNextUntapStepTargetEffect(); effect.setText("That creature doesn't untap during its controller's next untap step"); ability.addEffect(effect); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/StoneboundMentor.java b/Mage.Sets/src/mage/cards/s/StoneboundMentor.java index a51001f56fb..7680a178f6e 100644 --- a/Mage.Sets/src/mage/cards/s/StoneboundMentor.java +++ b/Mage.Sets/src/mage/cards/s/StoneboundMentor.java @@ -24,7 +24,7 @@ public final class StoneboundMentor extends CardImpl { this.toughness = new MageInt(3); // Whenever one or more cards leave your graveyard, scry 1. - this.addAbility(new CardsLeaveGraveyardTriggeredAbility(new ScryEffect(1))); + this.addAbility(new CardsLeaveGraveyardTriggeredAbility(new ScryEffect(1, false))); } private StoneboundMentor(final StoneboundMentor card) { diff --git a/Mage.Sets/src/mage/cards/s/StormChargedSlasher.java b/Mage.Sets/src/mage/cards/s/StormChargedSlasher.java index 93ba611b364..7557903736a 100644 --- a/Mage.Sets/src/mage/cards/s/StormChargedSlasher.java +++ b/Mage.Sets/src/mage/cards/s/StormChargedSlasher.java @@ -30,7 +30,6 @@ public final class StormChargedSlasher extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(4); this.color.setRed(true); - this.transformable = true; this.nightCard = true; // At the beginning of combat on your turn, target creature you control gets +2/+0 and gains trample and haste until end of turn. diff --git a/Mage.Sets/src/mage/cards/s/StormOfSouls.java b/Mage.Sets/src/mage/cards/s/StormOfSouls.java new file mode 100644 index 00000000000..d73bc1d2df9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/StormOfSouls.java @@ -0,0 +1,143 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ExileSpellEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.targetpointer.FixedTargets; + +import java.util.UUID; + +/** + * @author Alex-Vasile + */ +public class StormOfSouls extends CardImpl { + public StormOfSouls(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{W}{W}"); + + // Return all creature cards from your graveyard to the battlefield. + // Each of them is a 1/1 Spirit with flying in addition to its other types. + this.getSpellAbility().addEffect(new StormOfSoulsReturnEffect()); + + // Exile Storm of Souls. + this.getSpellAbility().addEffect(new ExileSpellEffect()); + } + + private StormOfSouls(final StormOfSouls card) { + super(card); + } + + @Override + public StormOfSouls copy() { + return new StormOfSouls(this); + } +} + +class StormOfSoulsReturnEffect extends OneShotEffect { + public StormOfSoulsReturnEffect() { + super(Outcome.Benefit); + staticText = "Return all creature cards from your graveyard to the battlefield. " + + "Each of them is a 1/1 Spirit with flying in addition to its other types."; + } + + private StormOfSoulsReturnEffect(final StormOfSoulsReturnEffect effect) { + super(effect); + } + + @Override + public StormOfSoulsReturnEffect copy() { + return new StormOfSoulsReturnEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + + Cards cards = new CardsImpl(player.getGraveyard().getCards(StaticFilters.FILTER_CARD_CREATURE, game)); + if (cards.isEmpty()) { + return false; + } + + player.moveCards(cards, Zone.BATTLEFIELD, source, game); + + // Figure out which cards were successfuly moved so that they can be turned into 1/1 Spirits + cards.retainZone(Zone.BATTLEFIELD, game); + + // Change the creatures + game.addEffect(new StormOfSoulsChangeCreatureEffect().setTargetPointer(new FixedTargets(cards, game)), source); + return true; + } +} + +class StormOfSoulsChangeCreatureEffect extends ContinuousEffectImpl { + + public StormOfSoulsChangeCreatureEffect() { + super(Duration.Custom, Outcome.Benefit); + } + + private StormOfSoulsChangeCreatureEffect(final StormOfSoulsChangeCreatureEffect effect) { + super(effect); + } + + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + + // Each of them is a 1/1 Spirit with flying in addition to its other types + for (UUID cardID : targetPointer.getTargets(game, source)) { + Permanent permanent = game.getPermanent(cardID); + if (permanent == null) { + continue; + } + + switch (layer) { + case TypeChangingEffects_4: + permanent.addSubType(game, SubType.SPIRIT); + break; + case AbilityAddingRemovingEffects_6: + permanent.addAbility(FlyingAbility.getInstance(), source.getSourceId(), game); + break; + case PTChangingEffects_7: + if (sublayer == SubLayer.SetPT_7b) { + permanent.getPower().setValue(1); + permanent.getToughness().setValue(1); + } + break; + } + } + return true; + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public StormOfSoulsChangeCreatureEffect copy() { + return new StormOfSoulsChangeCreatureEffect(this); + } + + @Override + public boolean hasLayer(Layer layer) { + return layer == Layer.TypeChangingEffects_4 + || layer == Layer.AbilityAddingRemovingEffects_6 + || layer == Layer.PTChangingEffects_7; + } +} diff --git a/Mage.Sets/src/mage/cards/s/StormStrike.java b/Mage.Sets/src/mage/cards/s/StormStrike.java index bfccf2c5907..d894983407f 100644 --- a/Mage.Sets/src/mage/cards/s/StormStrike.java +++ b/Mage.Sets/src/mage/cards/s/StormStrike.java @@ -28,7 +28,7 @@ public final class StormStrike extends CardImpl { this.getSpellAbility().addEffect(new GainAbilityTargetEffect( FirstStrikeAbility.getInstance(), Duration.EndOfTurn ).setText("and gains first strike until end of turn")); - this.getSpellAbility().addEffect(new ScryEffect(1)); + this.getSpellAbility().addEffect(new ScryEffect(1, false)); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } diff --git a/Mage.Sets/src/mage/cards/s/StormTheVault.java b/Mage.Sets/src/mage/cards/s/StormTheVault.java index 5dd55b22a31..7e40fbc0493 100644 --- a/Mage.Sets/src/mage/cards/s/StormTheVault.java +++ b/Mage.Sets/src/mage/cards/s/StormTheVault.java @@ -29,7 +29,6 @@ public final class StormTheVault extends CardImpl { this.addSuperType(SuperType.LEGENDARY); - this.transformable = true; this.secondSideCardClazz = mage.cards.v.VaultOfCatlacan.class; // Whenever one or more creatures you control deal combat damage to a player, create a colorless Treasure artifact token with "{T}, Sacrifice this artifact: Add one mana of any color." @@ -38,7 +37,7 @@ public final class StormTheVault extends CardImpl { // At the beginning of your end step, if you control five or more artifacts, transform Storm the Vault. this.addAbility(new TransformAbility()); this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfEndStepTriggeredAbility(new TransformSourceEffect(true), TargetController.YOU, false), + new BeginningOfEndStepTriggeredAbility(new TransformSourceEffect(), TargetController.YOU, false), new PermanentsOnTheBattlefieldCondition(StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT, ComparisonType.MORE_THAN, 4), "At the beginning of your end step, if you control five or more artifacts, transform {this}")); diff --git a/Mage.Sets/src/mage/cards/s/StormcarvedCoast.java b/Mage.Sets/src/mage/cards/s/StormcarvedCoast.java new file mode 100644 index 00000000000..2e26994b334 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/StormcarvedCoast.java @@ -0,0 +1,49 @@ +package mage.cards.s; + +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.TapSourceEffect; +import mage.abilities.mana.BlueManaAbility; +import mage.abilities.mana.RedManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class StormcarvedCoast extends CardImpl { + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition( + StaticFilters.FILTER_LANDS, ComparisonType.FEWER_THAN, 2 + ); + + public StormcarvedCoast(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // Stormcarved Coast enters the battlefield tapped unless you control two or more other lands. + this.addAbility(new EntersBattlefieldAbility( + new ConditionalOneShotEffect(new TapSourceEffect(), condition), + "tapped unless you control two or more other lands" + )); + + // {T}: Add {U} or {R}. + this.addAbility(new BlueManaAbility()); + this.addAbility(new RedManaAbility()); + } + + private StormcarvedCoast(final StormcarvedCoast card) { + super(card); + } + + @Override + public StormcarvedCoast copy() { + return new StormcarvedCoast(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/StormchaserChimera.java b/Mage.Sets/src/mage/cards/s/StormchaserChimera.java index bab134f8061..176ce12367b 100644 --- a/Mage.Sets/src/mage/cards/s/StormchaserChimera.java +++ b/Mage.Sets/src/mage/cards/s/StormchaserChimera.java @@ -40,7 +40,7 @@ public final class StormchaserChimera extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); // {2}{U}{R}: Scry 1, then reveal the top card of your library. Stormchaser Chimera gets +X/+0 until end of turn, where X is that card's converted mana cost. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ScryEffect(1), new ManaCostsImpl("{2}{U}{R}")); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ScryEffect(1, false), new ManaCostsImpl("{2}{U}{R}")); ability.addEffect(new StormchaserChimeraEffect()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/StormchaserDrake.java b/Mage.Sets/src/mage/cards/s/StormchaserDrake.java new file mode 100644 index 00000000000..c9a323e28d1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/StormchaserDrake.java @@ -0,0 +1,51 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.BecomesTargetTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.FilterSpell; + +import java.util.UUID; + +/** + * @author weirddan455 + */ +public final class StormchaserDrake extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("a spell you control"); + + static { + filter.add(TargetController.YOU.getControllerPredicate()); + } + + public StormchaserDrake(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); + + this.subtype.add(SubType.DRAKE); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever Stormchaser Drake becomes the target of a spell you control, draw a card. + this.addAbility(new BecomesTargetTriggeredAbility( + new DrawCardSourceControllerEffect(1), filter + ).setTriggerPhrase("Whenever {this} becomes the target of a spell you control, ")); + } + + private StormchaserDrake(final StormchaserDrake card) { + super(card); + } + + @Override + public StormchaserDrake copy() { + return new StormchaserDrake(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/StormfistCrusader.java b/Mage.Sets/src/mage/cards/s/StormfistCrusader.java index 8fd35a8ffe4..20bb58fd36c 100644 --- a/Mage.Sets/src/mage/cards/s/StormfistCrusader.java +++ b/Mage.Sets/src/mage/cards/s/StormfistCrusader.java @@ -29,7 +29,7 @@ public final class StormfistCrusader extends CardImpl { this.toughness = new MageInt(2); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // At the beginning of your upkeep, each player draws a card and loses 1 life. Ability ability = new BeginningOfUpkeepTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/s/StormfrontRiders.java b/Mage.Sets/src/mage/cards/s/StormfrontRiders.java index dcb5d741988..4ff4e48bcd6 100644 --- a/Mage.Sets/src/mage/cards/s/StormfrontRiders.java +++ b/Mage.Sets/src/mage/cards/s/StormfrontRiders.java @@ -14,7 +14,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.TargetController; import mage.constants.Zone; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.game.permanent.token.SoldierToken; @@ -41,7 +41,7 @@ public final class StormfrontRiders extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); // When Stormfront Riders enters the battlefield, return two creatures you control to their owner's hand. - this.addAbility(new EntersBattlefieldTriggeredAbility(new ReturnToHandChosenControlledPermanentEffect(new FilterControlledCreaturePermanent("creatures you control"), 2))); + this.addAbility(new EntersBattlefieldTriggeredAbility(new ReturnToHandChosenControlledPermanentEffect(StaticFilters.FILTER_CONTROLLED_CREATURES, 2))); // Whenever Stormfront Riders or another creature is returned to your hand from the battlefield, create a 1/1 white Soldier creature token. this.addAbility(new ZoneChangeAllTriggeredAbility(Zone.BATTLEFIELD, Zone.BATTLEFIELD, Zone.HAND, new CreateTokenEffect(new SoldierToken()), filter, "Whenever {this} or another creature is returned to your hand from the battlefield, ", false)); diff --git a/Mage.Sets/src/mage/cards/s/StormscapeBattlemage.java b/Mage.Sets/src/mage/cards/s/StormscapeBattlemage.java index 737305fc919..676e6c643ca 100644 --- a/Mage.Sets/src/mage/cards/s/StormscapeBattlemage.java +++ b/Mage.Sets/src/mage/cards/s/StormscapeBattlemage.java @@ -1,9 +1,7 @@ - package mage.cards.s; import java.util.UUID; import mage.MageInt; -import mage.ObjectColor; import mage.abilities.TriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.KickedCostCondition; @@ -15,9 +13,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -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; /** @@ -26,11 +22,6 @@ import mage.target.common.TargetCreaturePermanent; */ public final class StormscapeBattlemage extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public StormscapeBattlemage(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{U}"); this.subtype.add(SubType.METATHRAN); @@ -49,13 +40,13 @@ public final class StormscapeBattlemage extends CardImpl { new EntersBattlefieldTriggeredAbility(new GainLifeEffect(3),false), new KickedCostCondition("{W}"), "When Stormscape Battlemage enters the battlefield, if it was kicked with its {W} kicker, you gain 3 life.")); - - // When Stormscape Battlemage enters the battlefield, if it was kicked with its {2}{B} kicker, destroy target nonblack creature. That creature can't be regenerated. + + // When Stormscape Battlemage enters the battlefield, if it was kicked with its {2}{B} kicker, destroy target nonblack creature. That creature can't be regenerated. TriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect(true),false); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); this.addAbility(new ConditionalInterveningIfTriggeredAbility( ability, new KickedCostCondition("{2}{B}"), - "When Stormscape Battlemage enters the battlefield, if it was kicked with its {2}{B} kicker, destroy target nonblack creature. That creature can't be regenerated.")); + "When Stormscape Battlemage enters the battlefield, if it was kicked with its {2}{B} kicker, destroy target nonblack creature. That creature can't be regenerated.")); } private StormscapeBattlemage(final StormscapeBattlemage card) { diff --git a/Mage.Sets/src/mage/cards/s/Storyweave.java b/Mage.Sets/src/mage/cards/s/Storyweave.java new file mode 100644 index 00000000000..ac9cf594864 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/Storyweave.java @@ -0,0 +1,164 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.game.Game; +import mage.game.events.EntersTheBattlefieldEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.util.CardUtil; +import mage.watchers.Watcher; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Storyweave extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.SAGA); + + public Storyweave(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{G}"); + + // Choose one — + // • Put two +1/+1 counters on target creature you control. + this.getSpellAbility().addEffect(new AddCountersTargetEffect(CounterType.P1P1.createInstance(2))); + this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); + + // • Put two lore counters on target Saga you control. The next time one or more enchantment creatures enter the battlefield under your control this turn, each enters with two additional +1/+1 counters on it. + Mode mode = new Mode(new AddCountersTargetEffect(CounterType.LORE.createInstance(2))); + mode.addEffect(new StoryweaveReplacementEffect()); + mode.addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addMode(mode); + this.getSpellAbility().addWatcher(new StoryweaveWatcher()); + } + + private Storyweave(final Storyweave card) { + super(card); + } + + @Override + public Storyweave copy() { + return new Storyweave(this); + } + + public static Ability makeAbility() { + // for testing purposes + Ability ability = new SimpleActivatedAbility(new StoryweaveReplacementEffect(), new GenericManaCost(0)); + ability.addWatcher(new StoryweaveWatcher()); + return ability; + } +} + +class StoryweaveReplacementEffect extends ReplacementEffectImpl { + + private int counter = 0; + + StoryweaveReplacementEffect() { + super(Duration.EndOfTurn, Outcome.BoostCreature); + staticText = "The next time one or more enchantment creatures enter the battlefield " + + "under your control this turn, each enters with two additional +1/+1 counters on it"; + } + + StoryweaveReplacementEffect(StoryweaveReplacementEffect effect) { + super(effect); + this.counter = effect.counter; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; + } + + @Override + public void init(Ability source, Game game) { + super.init(source, game); + this.counter = StoryweaveWatcher.getCounter(game, source); + } + + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (StoryweaveWatcher.getCounter(game, source) > counter) { + discard(); + return false; + } + Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget(); + return permanent != null + && permanent.isControlledBy(source.getControllerId()) + && permanent.isEnchantment(game) + && permanent.isCreature(game); + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Permanent creature = ((EntersTheBattlefieldEvent) event).getTarget(); + if (creature != null) { + creature.addCounters( + CounterType.P1P1.createInstance(2), + source.getControllerId(), source, + game, event.getAppliedEffects() + ); + } + return false; + } + + @Override + public StoryweaveReplacementEffect copy() { + return new StoryweaveReplacementEffect(this); + } +} + +class StoryweaveWatcher extends Watcher { + + private final Map playerMap = new HashMap<>(); + + StoryweaveWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() != GameEvent.EventType.ENTERS_THE_BATTLEFIELD) { + return; + } + EntersTheBattlefieldEvent zEvent = ((EntersTheBattlefieldEvent) event); + if (zEvent.getTarget().isEnchantment(game) && zEvent.getTarget().isCreature(game)) { + playerMap.compute(zEvent.getPlayerId(), CardUtil::setOrIncrementValue); + } + } + + @Override + public void reset() { + super.reset(); + this.playerMap.clear(); + } + + static int getCounter(Game game, Ability source) { + return game + .getState() + .getWatcher(StoryweaveWatcher.class) + .playerMap + .getOrDefault(source.getControllerId(), 0); + } +} diff --git a/Mage.Sets/src/mage/cards/s/StranglingGrasp.java b/Mage.Sets/src/mage/cards/s/StranglingGrasp.java index c3665cff1ae..2c78cb5ff18 100644 --- a/Mage.Sets/src/mage/cards/s/StranglingGrasp.java +++ b/Mage.Sets/src/mage/cards/s/StranglingGrasp.java @@ -38,7 +38,6 @@ public final class StranglingGrasp extends CardImpl { this.subtype.add(SubType.AURA); this.color.setBlack(true); - this.transformable = true; this.nightCard = true; // Enchant creature or planeswalker an opponent controls diff --git a/Mage.Sets/src/mage/cards/s/StrefanMaurerProgenitor.java b/Mage.Sets/src/mage/cards/s/StrefanMaurerProgenitor.java new file mode 100644 index 00000000000..ba2c4b378c2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/StrefanMaurerProgenitor.java @@ -0,0 +1,173 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.IndestructibleAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.BloodToken; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInHand; +import mage.target.common.TargetControlledPermanent; +import mage.target.targetpointer.FixedTarget; +import mage.watchers.common.PlayerLostLifeWatcher; + +import java.util.*; + +/** + * @author Alex-Vasile + */ +public class StrefanMaurerProgenitor extends CardImpl { + + private static final Hint hint = new ValueHint("Players who lost life this turn", StrefanMaurerProgenitorNumberPlayersLostLifeDynamicValue.instance); + private static final FilterControlledPermanent bloodTokenFilter = new FilterControlledPermanent("Blood tokens"); + + static { + bloodTokenFilter.add(SubType.BLOOD.getPredicate()); + bloodTokenFilter.add(TokenPredicate.TRUE); + } + + public StrefanMaurerProgenitor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{R}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.VAMPIRE); + this.subtype.add(SubType.NOBLE); + + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // At the beginning of your end step, create a Blood token for each player who lost life this turn. + this.addAbility( + new BeginningOfEndStepTriggeredAbility( + new CreateTokenEffect( + new BloodToken(), + StrefanMaurerProgenitorNumberPlayersLostLifeDynamicValue.instance), + TargetController.YOU, + false) + .addHint(hint) + ); + + // Whenever Strefan attacks, you may sacrifice two Blood tokens. + // If you do, you may put a Vampire card from your hand onto the battlefield tapped and attacking. + // It gains indestructible until end of turn. + this.addAbility(new AttacksTriggeredAbility( + new DoIfCostPaid( + new StrefanMaurerProgenitorPlayVampireEffect(), + new SacrificeTargetCost(new TargetControlledPermanent( + 2, + 2, + bloodTokenFilter, + true) + )), + false + ) + ); + } + + private StrefanMaurerProgenitor(final StrefanMaurerProgenitor card) { super(card);} + + @Override + public Card copy() { + return new StrefanMaurerProgenitor(this); + } +} + +class StrefanMaurerProgenitorPlayVampireEffect extends OneShotEffect { + + private static final FilterCreatureCard vampireCardFilter = new FilterCreatureCard(); + static { vampireCardFilter.add(SubType.VAMPIRE.getPredicate()); } + + public StrefanMaurerProgenitorPlayVampireEffect() { + super(Outcome.PutCreatureInPlay); + staticText = "you may put a Vampire card from your hand onto the battlefield tapped and attacking. " + + "It gains indestructible until end of turn."; + } + + private StrefanMaurerProgenitorPlayVampireEffect(final StrefanMaurerProgenitorPlayVampireEffect effect) { super(effect); } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { return false; } + + TargetCard target = new TargetCardInHand(0, 1, vampireCardFilter); + if (!player.choose(outcome, player.getHand(), target, game)) { return false; } + + Card card = game.getCard(target.getFirstTarget()); + if (card == null) { return false; } + + player.moveCards(card, Zone.BATTLEFIELD, source, game, true, false, true, null); + + Permanent permanent = game.getPermanent(card.getId()); + if (permanent == null) { return false; } + + game.getCombat().addAttackingCreature(permanent.getId(), game); + + // Gains indestructable until end of turn + ContinuousEffect effect = new GainAbilityTargetEffect(IndestructibleAbility.getInstance(), Duration.EndOfTurn); + effect.setTargetPointer(new FixedTarget(permanent.getId(), game)); + game.addEffect(effect, source); + + return true; + } + + @Override + public StrefanMaurerProgenitorPlayVampireEffect copy() { return new StrefanMaurerProgenitorPlayVampireEffect(this); } +} + +enum StrefanMaurerProgenitorNumberPlayersLostLifeDynamicValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + Player controller = game.getPlayer(sourceAbility.getControllerId()); + if (controller == null) { return 0; } + + PlayerLostLifeWatcher watcher = game.getState().getWatcher(PlayerLostLifeWatcher.class); + int numPlayersWhoLostLife = 0; + + if (watcher != null) { + for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { + if (watcher.getLifeLost(playerId) > 0) { + numPlayersWhoLostLife++; + } + } + } + + return numPlayersWhoLostLife; + } + + @Override + public DynamicValue copy() { return instance; } + + @Override + public String getMessage() { return "player who lost life this turn"; } + + @Override + public String toString() { return "1"; } +} diff --git a/Mage.Sets/src/mage/cards/s/StrengthOfCedars.java b/Mage.Sets/src/mage/cards/s/StrengthOfCedars.java index b1d93df8f0b..0c636ece9c3 100644 --- a/Mage.Sets/src/mage/cards/s/StrengthOfCedars.java +++ b/Mage.Sets/src/mage/cards/s/StrengthOfCedars.java @@ -12,7 +12,6 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; import mage.filter.common.FilterControlledLandPermanent; -import mage.filter.common.FilterControlledPermanent; import mage.target.common.TargetCreaturePermanent; /** @@ -20,16 +19,17 @@ import mage.target.common.TargetCreaturePermanent; * @author Loki */ public final class StrengthOfCedars extends CardImpl { - private static final FilterControlledPermanent filter = new FilterControlledLandPermanent("the number of lands you control"); + + private static final FilterControlledLandPermanent filter = new FilterControlledLandPermanent("lands you control"); + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter, null); public StrengthOfCedars (UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{4}{G}"); this.subtype.add(SubType.ARCANE); - // Target creature gets +X/+X until end of turn, where X is the number of lands you control. - DynamicValue controlledLands = new PermanentsOnBattlefieldCount(filter, null); - this.getSpellAbility().addEffect(new BoostTargetEffect(controlledLands, controlledLands, Duration.EndOfTurn, true)); + this.getSpellAbility().addEffect(new BoostTargetEffect(xValue, xValue, Duration.EndOfTurn, true)); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } @@ -41,5 +41,4 @@ public final class StrengthOfCedars extends CardImpl { public StrengthOfCedars copy() { return new StrengthOfCedars(this); } - } diff --git a/Mage.Sets/src/mage/cards/s/StrikingSliver.java b/Mage.Sets/src/mage/cards/s/StrikingSliver.java index 00de19b7e60..083e5e51fa5 100644 --- a/Mage.Sets/src/mage/cards/s/StrikingSliver.java +++ b/Mage.Sets/src/mage/cards/s/StrikingSliver.java @@ -30,7 +30,7 @@ public final class StrikingSliver extends CardImpl { // Sliver creatures you control have first strike. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledEffect(FirstStrikeAbility.getInstance(), - Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS))); + Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_SLIVERS))); } private StrikingSliver(final StrikingSliver card) { diff --git a/Mage.Sets/src/mage/cards/s/StrionicResonator.java b/Mage.Sets/src/mage/cards/s/StrionicResonator.java index 23817a78163..df7808e549e 100644 --- a/Mage.Sets/src/mage/cards/s/StrionicResonator.java +++ b/Mage.Sets/src/mage/cards/s/StrionicResonator.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.common.SimpleActivatedAbility; @@ -12,15 +10,13 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.Zone; import mage.game.Game; -import mage.game.permanent.Permanent; import mage.game.stack.StackAbility; -import mage.players.Player; import mage.target.common.TargetTriggeredAbility; +import java.util.UUID; + /** - * * @author Plopman */ public final class StrionicResonator extends CardImpl { @@ -29,7 +25,7 @@ public final class StrionicResonator extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); // {2}, {T}: Copy target triggered ability you control. You may choose new targets for the copy. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new StrionicResonatorEffect(), new ManaCostsImpl("{2}")); + Ability ability = new SimpleActivatedAbility(new StrionicResonatorEffect(), new ManaCostsImpl<>("{2}")); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetTriggeredAbility()); this.addAbility(ability); @@ -58,15 +54,11 @@ class StrionicResonatorEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(targetPointer.getFirst(game, source)); - if (stackAbility != null) { - Player controller = game.getPlayer(source.getControllerId()); - Permanent sourcePermanent = game.getPermanent(source.getSourceId()); - if (controller != null && sourcePermanent != null) { - stackAbility.createCopyOnStack(game, source, source.getControllerId(), true); - return true; - } + if (stackAbility == null) { + return false; } - return false; + stackAbility.createCopyOnStack(game, source, source.getControllerId(), true); + return true; } diff --git a/Mage.Sets/src/mage/cards/s/StromkirkCaptain.java b/Mage.Sets/src/mage/cards/s/StromkirkCaptain.java index 9382b751340..690c079a8a2 100644 --- a/Mage.Sets/src/mage/cards/s/StromkirkCaptain.java +++ b/Mage.Sets/src/mage/cards/s/StromkirkCaptain.java @@ -1,8 +1,7 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; +import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.continuous.BoostControlledEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; @@ -12,22 +11,20 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; +import java.util.UUID; + /** - * * @author Loki */ public final class StromkirkCaptain extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Vampire creatures"); - static { - filter.add(SubType.VAMPIRE.getPredicate()); - } + private static final FilterCreaturePermanent filter + = new FilterCreaturePermanent(SubType.VAMPIRE, "Vampire creatures"); public StromkirkCaptain(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{R}"); this.subtype.add(SubType.VAMPIRE); this.subtype.add(SubType.SOLDIER); @@ -36,8 +33,13 @@ public final class StromkirkCaptain extends CardImpl { this.addAbility(FirstStrikeAbility.getInstance()); // Other Vampire creatures you control get +1/+1 and have first strike. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield, filter, true))); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledEffect(FirstStrikeAbility.getInstance(), Duration.WhileOnBattlefield, filter, true))); + Ability ability = new SimpleStaticAbility(new BoostControlledEffect( + 1, 1, Duration.WhileOnBattlefield, filter, true + )); + ability.addEffect(new GainAbilityControlledEffect( + FirstStrikeAbility.getInstance(), Duration.WhileOnBattlefield, filter, true + ).setText("and have first strike")); + this.addAbility(ability); } private StromkirkCaptain(final StromkirkCaptain card) { diff --git a/Mage.Sets/src/mage/cards/s/StromkirkOccultist.java b/Mage.Sets/src/mage/cards/s/StromkirkOccultist.java index 6ecdc25ecbf..a33db53e385 100644 --- a/Mage.Sets/src/mage/cards/s/StromkirkOccultist.java +++ b/Mage.Sets/src/mage/cards/s/StromkirkOccultist.java @@ -3,27 +3,15 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.AsThoughEffectImpl; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ExileTopXMayPlayUntilEndOfTurnEffect; import mage.abilities.keyword.MadnessAbility; import mage.abilities.keyword.TrampleAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AsThoughEffectType; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.targetpointer.FixedTarget; /** * @@ -42,7 +30,7 @@ public final class StromkirkOccultist extends CardImpl { this.addAbility(TrampleAbility.getInstance()); // Whenever Stromkirk Mystic deals combat damage to a player, exile the top card of your library. Until end of turn, you may play that card. - this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new StromkirkOccultistExileEffect(), false)); + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new ExileTopXMayPlayUntilEndOfTurnEffect(1), false)); // Madness {1}{R} this.addAbility(new MadnessAbility(this, new ManaCostsImpl("{1}{R}"))); @@ -57,67 +45,3 @@ public final class StromkirkOccultist extends CardImpl { return new StromkirkOccultist(this); } } - -class StromkirkOccultistExileEffect extends OneShotEffect { - - public StromkirkOccultistExileEffect() { - super(Outcome.Detriment); - this.staticText = "Exile the top card of your library. Until end of turn, you may play that card"; - } - - public StromkirkOccultistExileEffect(final StromkirkOccultistExileEffect effect) { - super(effect); - } - - @Override - public StromkirkOccultistExileEffect copy() { - return new StromkirkOccultistExileEffect(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()) { - Card card = controller.getLibrary().getFromTop(game); - if (card != null) { - String exileName = sourcePermanent.getIdName() + " "; - if (controller.moveCardToExileWithInfo(card, source.getSourceId(), exileName, source, game, Zone.LIBRARY, true)) { - ContinuousEffect effect = new StromkirkOccultistPlayFromExileEffect(); - effect.setTargetPointer(new FixedTarget(card.getId())); - game.addEffect(effect, source); - } - } - return true; - } - return false; - } -} - -class StromkirkOccultistPlayFromExileEffect extends AsThoughEffectImpl { - - public StromkirkOccultistPlayFromExileEffect() { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit); - staticText = "You may play the card from exile"; - } - - public StromkirkOccultistPlayFromExileEffect(final StromkirkOccultistPlayFromExileEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public StromkirkOccultistPlayFromExileEffect copy() { - return new StromkirkOccultistPlayFromExileEffect(this); - } - - @Override - public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - return source.isControlledBy(affectedControllerId) - && objectId.equals(getTargetPointer().getFirst(game, source)); - } -} diff --git a/Mage.Sets/src/mage/cards/s/StrongholdAssassin.java b/Mage.Sets/src/mage/cards/s/StrongholdAssassin.java index f7e3849f601..d4eb12a3916 100644 --- a/Mage.Sets/src/mage/cards/s/StrongholdAssassin.java +++ b/Mage.Sets/src/mage/cards/s/StrongholdAssassin.java @@ -1,9 +1,7 @@ - package mage.cards.s; import java.util.UUID; import mage.MageInt; -import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeTargetCost; @@ -14,10 +12,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; -import static mage.filter.StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ColorPredicate; +import mage.filter.StaticFilters; import mage.target.Target; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; @@ -28,12 +23,6 @@ import mage.target.common.TargetCreaturePermanent; */ public final class StrongholdAssassin extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public StrongholdAssassin(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{B}"); this.subtype.add(SubType.PHYREXIAN); @@ -45,8 +34,8 @@ public final class StrongholdAssassin extends CardImpl { // {tap}, Sacrifice a creature: Destroy target nonblack creature. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new TapSourceCost()); - ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(FILTER_CONTROLLED_CREATURE_SHORT_TEXT))); - Target target = new TargetCreaturePermanent(filter); + ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT))); + Target target = new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK); ability.addTarget(target); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/StrongholdMachinist.java b/Mage.Sets/src/mage/cards/s/StrongholdMachinist.java index 2f80d2094fb..a403daf089b 100644 --- a/Mage.Sets/src/mage/cards/s/StrongholdMachinist.java +++ b/Mage.Sets/src/mage/cards/s/StrongholdMachinist.java @@ -14,8 +14,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.FilterSpell; -import mage.filter.predicate.Predicates; +import mage.filter.StaticFilters; import mage.target.TargetSpell; /** @@ -24,12 +23,6 @@ import mage.target.TargetSpell; */ public final class StrongholdMachinist extends CardImpl { - private static final FilterSpell filter = new FilterSpell("noncreature spell"); - - static { - filter.add(Predicates.not(CardType.CREATURE.getPredicate())); - } - public StrongholdMachinist(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); @@ -42,7 +35,7 @@ public final class StrongholdMachinist extends CardImpl { Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CounterTargetEffect(), new ManaCostsImpl("{U}{U}")); ability.addCost(new TapSourceCost()); ability.addCost(new DiscardCardCost()); - ability.addTarget(new TargetSpell(filter)); + ability.addTarget(new TargetSpell(StaticFilters.FILTER_SPELL_NON_CREATURE)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/StruggleForSkemfar.java b/Mage.Sets/src/mage/cards/s/StruggleForSkemfar.java index 9d6e3043169..13f0055c916 100644 --- a/Mage.Sets/src/mage/cards/s/StruggleForSkemfar.java +++ b/Mage.Sets/src/mage/cards/s/StruggleForSkemfar.java @@ -24,7 +24,8 @@ public final class StruggleForSkemfar extends CardImpl { // Put a +1/+1 counter on target creature you control. Then that creature fights up to one target creature you don't control. this.getSpellAbility().addEffect(new AddCountersTargetEffect(CounterType.P1P1.createInstance())); this.getSpellAbility().addEffect(new FightTargetsEffect().setText( - "Then that creature fights up to one target creature you don't control" + "Then that creature fights up to one target creature you don't control. " + + "(Each deals damage equal to its power to the other.)" )); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); this.getSpellAbility().addTarget(new TargetCreaturePermanent( diff --git a/Mage.Sets/src/mage/cards/s/StubbornDenial.java b/Mage.Sets/src/mage/cards/s/StubbornDenial.java index 8dddf60dd1b..97845fe4e99 100644 --- a/Mage.Sets/src/mage/cards/s/StubbornDenial.java +++ b/Mage.Sets/src/mage/cards/s/StubbornDenial.java @@ -10,8 +10,7 @@ import mage.abilities.hint.common.FerociousHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.FilterSpell; -import mage.filter.predicate.Predicates; +import mage.filter.StaticFilters; import mage.target.TargetSpell; import java.util.UUID; @@ -21,12 +20,6 @@ import java.util.UUID; */ public final class StubbornDenial extends CardImpl { - private static final FilterSpell filter = new FilterSpell("noncreature spell"); - - static { - filter.add(Predicates.not(CardType.CREATURE.getPredicate())); - } - public StubbornDenial(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{U}"); @@ -41,7 +34,7 @@ public final class StubbornDenial extends CardImpl { new CounterTargetEffect(), FerociousCondition.instance, "
Ferocious — If you control a creature with power 4 or greater, counter that spell instead")); - this.getSpellAbility().addTarget(new TargetSpell(filter)); + this.getSpellAbility().addTarget(new TargetSpell(StaticFilters.FILTER_SPELL_NON_CREATURE)); this.getSpellAbility().addHint(FerociousHint.instance); } diff --git a/Mage.Sets/src/mage/cards/s/SuddenSalvation.java b/Mage.Sets/src/mage/cards/s/SuddenSalvation.java new file mode 100644 index 00000000000..5b39b997ee3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SuddenSalvation.java @@ -0,0 +1,96 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.common.FilterPermanentCard; +import mage.filter.predicate.card.PutIntoGraveFromBattlefieldThisTurnPredicate; +import mage.game.Controllable; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInGraveyard; +import mage.watchers.common.CardsPutIntoGraveyardWatcher; + +import java.util.Objects; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SuddenSalvation extends CardImpl { + + private static final FilterCard filter = new FilterPermanentCard( + "permanent cards in graveyards that were put there from the battlefield this turn" + ); + + static { + filter.add(PutIntoGraveFromBattlefieldThisTurnPredicate.instance); + } + + public SuddenSalvation(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{W}{W}"); + + // Choose up to three target permanent cards in graveyards that were put there from the battlefield this turn. Return them to the battlefield tapped under their owners' control. You draw a card for each opponent who controls one or more of those permanents. + this.getSpellAbility().addEffect(new SuddenSalvationEffect()); + this.getSpellAbility().addTarget(new TargetCardInGraveyard(0, 3, filter)); + this.getSpellAbility().addWatcher(new CardsPutIntoGraveyardWatcher()); + } + + private SuddenSalvation(final SuddenSalvation card) { + super(card); + } + + @Override + public SuddenSalvation copy() { + return new SuddenSalvation(this); + } +} + +class SuddenSalvationEffect extends OneShotEffect { + + SuddenSalvationEffect() { + super(Outcome.Benefit); + staticText = "choose up to three target permanent cards in graveyards " + + "that were put there from the battlefield this turn. " + + "Return them to the battlefield tapped under their owners' control. " + + "You draw a card for each opponent who controls one or more of those permanents"; + } + + private SuddenSalvationEffect(final SuddenSalvationEffect effect) { + super(effect); + } + + @Override + public SuddenSalvationEffect copy() { + return new SuddenSalvationEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Cards cards = new CardsImpl(getTargetPointer().getTargets(game, source)); + if (player == null || cards.isEmpty()) { + return false; + } + player.moveCards( + cards.getCards(game), Zone.BATTLEFIELD, source, game, + true, false, true, null + ); + int opponents = cards.stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .map(Controllable::getControllerId) + .distinct() + .mapToInt(uuid -> player.hasOpponent(uuid, game) ? 1 : 0) + .sum(); + player.drawCards(opponents, source, game); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SuitUp.java b/Mage.Sets/src/mage/cards/s/SuitUp.java new file mode 100644 index 00000000000..d2f3b31bd42 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SuitUp.java @@ -0,0 +1,55 @@ +package mage.cards.s; + +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.continuous.AddCardTypeTargetEffect; +import mage.abilities.effects.common.continuous.SetPowerToughnessTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicates; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SuitUp extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent("creature or Vehicle"); + + static { + filter.add(Predicates.or( + CardType.CREATURE.getPredicate(), + SubType.VEHICLE.getPredicate() + )); + } + + public SuitUp(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{U}"); + + // Until end of turn, target creature or Vehicle becomes an artifact creature with base power and toughness 4/5. + this.getSpellAbility().addEffect(new AddCardTypeTargetEffect( + Duration.EndOfTurn, CardType.ARTIFACT, CardType.CREATURE + ).setText("until end of turn, target creature or Vehicle becomes an artifact creature")); + this.getSpellAbility().addEffect(new SetPowerToughnessTargetEffect( + 4, 5, Duration.EndOfTurn + ).setText("with base power and toughness 4/5")); + this.getSpellAbility().addTarget(new TargetPermanent(filter)); + + // Draw a card. + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("
")); + } + + private SuitUp(final SuitUp card) { + super(card); + } + + @Override + public SuitUp copy() { + return new SuitUp(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SunbladeSamurai.java b/Mage.Sets/src/mage/cards/s/SunbladeSamurai.java new file mode 100644 index 00000000000..4ce1e4fc9ea --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SunbladeSamurai.java @@ -0,0 +1,61 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; +import mage.abilities.keyword.ChannelAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterCard; +import mage.target.common.TargetCardInLibrary; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SunbladeSamurai extends CardImpl { + + private static final FilterCard filter = new FilterCard("a basic Plains card"); + + static { + filter.add(SubType.PLAINS.getPredicate()); + filter.add(SuperType.BASIC.getPredicate()); + } + + public SunbladeSamurai(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{4}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SAMURAI); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // Channel — {2}, Discard Sunblade Samurai: Search your library for a basic Plains card, reveal it, put it into your hand, the shuffle. You gain 2 life. + Ability ability = new ChannelAbility( + "{2}", + new SearchLibraryPutInHandEffect( + new TargetCardInLibrary(filter), true, true + ) + ); + ability.addEffect(new GainLifeEffect(2)); + this.addAbility(ability); + } + + private SunbladeSamurai(final SunbladeSamurai card) { + super(card); + } + + @Override + public SunbladeSamurai copy() { + return new SunbladeSamurai(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SunbringersTouch.java b/Mage.Sets/src/mage/cards/s/SunbringersTouch.java index c2f965ce7ea..5d27a705998 100644 --- a/Mage.Sets/src/mage/cards/s/SunbringersTouch.java +++ b/Mage.Sets/src/mage/cards/s/SunbringersTouch.java @@ -11,29 +11,25 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.counters.CounterType; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; /** * * @author fireshoes */ public final class SunbringersTouch extends CardImpl { - - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("Each creature you control with a +1/+1 counter on it"); - static { - filter.add(CounterType.P1P1.getPredicate()); - } public SunbringersTouch(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{G}{G}"); // Bolster X, where X is the number of cards in your hand. - this.getSpellAbility().addEffect(new BolsterEffect(CardsInControllerHandCount.instance)); + this.getSpellAbility().addEffect(new BolsterEffect(CardsInControllerHandCount.instance).setText("Bolster X, where X is the number of cards in your hand.")); // Each creature you control with a +1/+1 counter on it gains trample until end of turn. - Effect effect = new GainAbilityControlledEffect(TrampleAbility.getInstance(), Duration.EndOfTurn, filter); - effect.setText("Each creature you control with a +1/+1 counter on it gains trample until end of turn"); + Effect effect = new GainAbilityControlledEffect( + TrampleAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_EACH_CONTROLLED_CREATURE_P1P1); + effect.setText("Each creature you control with a +1/+1 counter on it gains trample until end of turn. " + + "(To bolster X, choose a creature with the least toughness among creatures you control and put X +1/+1 counters on it.)"); this.getSpellAbility().addEffect(effect); } diff --git a/Mage.Sets/src/mage/cards/s/SundownPass.java b/Mage.Sets/src/mage/cards/s/SundownPass.java new file mode 100644 index 00000000000..33325cd81a4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SundownPass.java @@ -0,0 +1,49 @@ +package mage.cards.s; + +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.TapSourceEffect; +import mage.abilities.mana.RedManaAbility; +import mage.abilities.mana.WhiteManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SundownPass extends CardImpl { + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition( + StaticFilters.FILTER_LANDS, ComparisonType.FEWER_THAN, 2 + ); + + public SundownPass(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // Sundown Pass enters the battlefield tapped unless you control two or more other lands. + this.addAbility(new EntersBattlefieldAbility( + new ConditionalOneShotEffect(new TapSourceEffect(), condition), + "tapped unless you control two or more other lands" + )); + + // {T}: Add {R} or {W}. + this.addAbility(new RedManaAbility()); + this.addAbility(new WhiteManaAbility()); + } + + private SundownPass(final SundownPass card) { + super(card); + } + + @Override + public SundownPass copy() { + return new SundownPass(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SunsetPyramid.java b/Mage.Sets/src/mage/cards/s/SunsetPyramid.java index 6f294f52f91..7c02537b622 100644 --- a/Mage.Sets/src/mage/cards/s/SunsetPyramid.java +++ b/Mage.Sets/src/mage/cards/s/SunsetPyramid.java @@ -36,7 +36,7 @@ public final class SunsetPyramid extends CardImpl { this.addAbility(ability); // {2}, {T}: Scry 1. - Ability ability2 = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ScryEffect(1), new GenericManaCost(2)); + Ability ability2 = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ScryEffect(1, false), new GenericManaCost(2)); ability2.addCost(new TapSourceCost()); this.addAbility(ability2); diff --git a/Mage.Sets/src/mage/cards/s/SuntailSquadron.java b/Mage.Sets/src/mage/cards/s/SuntailSquadron.java new file mode 100644 index 00000000000..c40183e4ab5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SuntailSquadron.java @@ -0,0 +1,67 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ConjureCardEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.game.Game; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SuntailSquadron extends CardImpl { + + public SuntailSquadron(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{W}{W}"); + + // Conjure a card named Suntail Hawk into your hand. If you have fewer than 7 cards in hand, repeat this process. + this.getSpellAbility().addEffect(new SuntailSquadronEffect()); + } + + private SuntailSquadron(final SuntailSquadron card) { + super(card); + } + + @Override + public SuntailSquadron copy() { + return new SuntailSquadron(this); + } +} + +class SuntailSquadronEffect extends OneShotEffect { + + SuntailSquadronEffect() { + super(Outcome.Benefit); + staticText = "conjure a card named Suntail Hawk into your hand. " + + "If you have fewer than 7 cards in hand, repeat this process"; + } + + private SuntailSquadronEffect(final SuntailSquadronEffect effect) { + super(effect); + } + + @Override + public SuntailSquadronEffect copy() { + return new SuntailSquadronEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Effect effect = new ConjureCardEffect("Suntail Hawk"); + do { + effect.apply(game, source); + } while (player.getHand().size() < 7); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SupernaturalRescue.java b/Mage.Sets/src/mage/cards/s/SupernaturalRescue.java new file mode 100644 index 00000000000..d15e55fd1b9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SupernaturalRescue.java @@ -0,0 +1,73 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.CastSourceTriggeredAbility; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.abilities.keyword.EnchantAbility; +import mage.abilities.keyword.FlashAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledPermanent; +import mage.target.TargetPermanent; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SupernaturalRescue extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.SPIRIT); + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter); + private static final Hint hint = new ConditionHint(condition, "You control a Spirit"); + + public SupernaturalRescue(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{W}"); + + this.subtype.add(SubType.AURA); + + // This spell has flash as long as you control a Spirit. + this.addAbility(new SimpleStaticAbility( + Zone.ALL, + new ConditionalContinuousEffect(new GainAbilitySourceEffect( + FlashAbility.getInstance(), Duration.WhileOnBattlefield, true + ), condition, "this spell has flash as long as you control a Spirit") + ).setRuleAtTheTop(true).addHint(hint)); + + // When you cast this spell, tap up to two target creatures you don't control. + Ability ability = new CastSourceTriggeredAbility(new TapTargetEffect().setText("tap up to two target creatures you don't control")); + ability.addTarget(new TargetPermanent(0, 2, StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); + this.addAbility(ability); + + // Enchant creature you control + TargetPermanent auraTarget = new TargetControlledCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + this.addAbility(new EnchantAbility(auraTarget.getTargetName())); + + // Enchanted creature gets +1/+2. + this.addAbility(new SimpleStaticAbility(new BoostEnchantedEffect(1, 2))); + } + + private SupernaturalRescue(final SupernaturalRescue card) { + super(card); + } + + @Override + public SupernaturalRescue copy() { + return new SupernaturalRescue(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SupremeLeaderSnoke.java b/Mage.Sets/src/mage/cards/s/SupremeLeaderSnoke.java index 4959cd037f2..17c764b6f08 100644 --- a/Mage.Sets/src/mage/cards/s/SupremeLeaderSnoke.java +++ b/Mage.Sets/src/mage/cards/s/SupremeLeaderSnoke.java @@ -2,7 +2,6 @@ package mage.cards.s; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.costs.Cost; import mage.abilities.costs.common.PayVariableLoyaltyCost; @@ -42,7 +41,7 @@ public final class SupremeLeaderSnoke extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.SNOKE); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +1: Put a loyalty counter on Supreme Leader Snoke for each life lost by all opponents from noncombat sources this turn. Ability ability1 = new LoyaltyAbility(new SupremeLeaderSnokeCounterEffect(CounterType.LOYALTY.createInstance()), 1); diff --git a/Mage.Sets/src/mage/cards/s/SurgehackerMech.java b/Mage.Sets/src/mage/cards/s/SurgehackerMech.java new file mode 100644 index 00000000000..276bd969787 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SurgehackerMech.java @@ -0,0 +1,71 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.CrewAbility; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SurgehackerMech extends CardImpl { + + private static final FilterPermanent filter + = new FilterCreatureOrPlaneswalkerPermanent("creature or planeswalker an opponent controls"); + private static final FilterPermanent filter2 + = new FilterControlledPermanent(SubType.VEHICLE); + + static { + filter.add(TargetController.OPPONENT.getControllerPredicate()); + } + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter2, 2); + private static final Hint hint = new ValueHint( + "Vehicles you control", new PermanentsOnBattlefieldCount(filter2) + ); + + public SurgehackerMech(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); + + this.subtype.add(SubType.VEHICLE); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + + // Menace + this.addAbility(new MenaceAbility(false)); + + // When Surgehacker Mech enters the battlefield, it deals damage equal to twice the number of Vehicles you control to target creature or planeswalker an opponent controls. + Ability ability = new EntersBattlefieldTriggeredAbility(new DamageTargetEffect(xValue).setText("it deals damage equal to twice the number of Vehicles you control to target creature or planeswalker an opponent controls")); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability.addHint(hint)); + + // Crew 4 + this.addAbility(new CrewAbility(4)); + } + + private SurgehackerMech(final SurgehackerMech card) { + super(card); + } + + @Override + public SurgehackerMech copy() { + return new SurgehackerMech(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SurrakarMarauder.java b/Mage.Sets/src/mage/cards/s/SurrakarMarauder.java index 8bacd91aad3..da2d0712ea8 100644 --- a/Mage.Sets/src/mage/cards/s/SurrakarMarauder.java +++ b/Mage.Sets/src/mage/cards/s/SurrakarMarauder.java @@ -1,9 +1,9 @@ - package mage.cards.s; import java.util.UUID; import mage.MageInt; import mage.abilities.common.LandfallAbility; +import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; import mage.abilities.keyword.IntimidateAbility; import mage.cards.CardImpl; @@ -27,7 +27,9 @@ public final class SurrakarMarauder extends CardImpl { // Landfall - Whenever a land enters the battlefield under your control, Surrakar Marauder gains intimidate until end of turn. // (It can't be blocked except by artifact creatures and/or creatures that share a color with it.) - this.addAbility(new LandfallAbility(new GainAbilitySourceEffect(IntimidateAbility.getInstance(), Duration.EndOfTurn), false)); + Effect effect = new GainAbilitySourceEffect(IntimidateAbility.getInstance(), Duration.EndOfTurn); + effect.setText("{this} gains intimidate until end of turn. (It can't be blocked except by artifact creatures and/or creatures that share a color with it.)"); + this.addAbility(new LandfallAbility(effect)); } private SurrakarMarauder(final SurrakarMarauder card) { diff --git a/Mage.Sets/src/mage/cards/s/SuspiciousStowaway.java b/Mage.Sets/src/mage/cards/s/SuspiciousStowaway.java index 83c217f6fc6..a72eb131ce4 100644 --- a/Mage.Sets/src/mage/cards/s/SuspiciousStowaway.java +++ b/Mage.Sets/src/mage/cards/s/SuspiciousStowaway.java @@ -5,7 +5,6 @@ import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; import mage.abilities.effects.common.DrawDiscardControllerEffect; import mage.abilities.keyword.CantBeBlockedSourceAbility; import mage.abilities.keyword.DayboundAbility; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -26,7 +25,6 @@ public final class SuspiciousStowaway extends CardImpl { this.subtype.add(SubType.WEREWOLF); this.power = new MageInt(1); this.toughness = new MageInt(1); - this.transformable = true; this.secondSideCardClazz = mage.cards.s.SeafaringWerewolf.class; // Suspicious Stowaway can't be blocked. @@ -38,7 +36,6 @@ public final class SuspiciousStowaway extends CardImpl { )); // Daybound - this.addAbility(new TransformAbility()); this.addAbility(new DayboundAbility()); } diff --git a/Mage.Sets/src/mage/cards/s/SuturedGhoul.java b/Mage.Sets/src/mage/cards/s/SuturedGhoul.java index 1fe4a7a394a..a910577a8f8 100644 --- a/Mage.Sets/src/mage/cards/s/SuturedGhoul.java +++ b/Mage.Sets/src/mage/cards/s/SuturedGhoul.java @@ -1,4 +1,3 @@ - package mage.cards.s; import java.util.UUID; @@ -21,7 +20,7 @@ import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.Zone; -import mage.filter.common.FilterCreatureCard; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -82,7 +81,7 @@ class SuturedGhoulEffect extends OneShotEffect { return false; } if (!controller.getGraveyard().isEmpty()) { - TargetCardInYourGraveyard target = new TargetCardInYourGraveyard(0, Integer.MAX_VALUE, new FilterCreatureCard("creature cards from your graveyard")); + TargetCardInYourGraveyard target = new TargetCardInYourGraveyard(0, Integer.MAX_VALUE, StaticFilters.FILTER_CARD_CREATURES_YOUR_GRAVEYARD); if (controller.chooseTarget(Outcome.Benefit, target, source, game)) { int count = 0; for (UUID uuid : target.getTargets()) { diff --git a/Mage.Sets/src/mage/cards/s/SwanSong.java b/Mage.Sets/src/mage/cards/s/SwanSong.java index 73ca3c92964..273f13f9dc4 100644 --- a/Mage.Sets/src/mage/cards/s/SwanSong.java +++ b/Mage.Sets/src/mage/cards/s/SwanSong.java @@ -22,7 +22,7 @@ import mage.target.TargetSpell; */ public final class SwanSong extends CardImpl { - private static final FilterSpell filter = new FilterSpell("enchantment, instant or sorcery spell"); + private static final FilterSpell filter = new FilterSpell("enchantment, instant, or sorcery spell"); static { filter.add(Predicates.or(CardType.ENCHANTMENT.getPredicate(), @@ -52,7 +52,7 @@ class SwanSongEffect extends OneShotEffect { public SwanSongEffect() { super(Outcome.Benefit); - this.staticText = "Counter target enchantment, instant or sorcery spell. Its controller creates a 2/2 blue Bird creature token with flying"; + this.staticText = "Counter target enchantment, instant, or sorcery spell. Its controller creates a 2/2 blue Bird creature token with flying"; } public SwanSongEffect(final SwanSongEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/SwarmGuildmage.java b/Mage.Sets/src/mage/cards/s/SwarmGuildmage.java index a891771e260..8f514a0bd84 100644 --- a/Mage.Sets/src/mage/cards/s/SwarmGuildmage.java +++ b/Mage.Sets/src/mage/cards/s/SwarmGuildmage.java @@ -40,7 +40,8 @@ public final class SwarmGuildmage extends CardImpl { ability.addEffect(new GainAbilityControlledEffect( new MenaceAbility(), Duration.EndOfTurn, StaticFilters.FILTER_CONTROLLED_CREATURES - ).setText("and gain menace until end of turn")); + ).setText("and gain menace until end of turn. " + + "(They can't be blocked except by two or more creatures.)")); ability.addCost(new TapSourceCost()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/s/SwarmShambler.java b/Mage.Sets/src/mage/cards/s/SwarmShambler.java index dd589388dd9..e82020013b1 100644 --- a/Mage.Sets/src/mage/cards/s/SwarmShambler.java +++ b/Mage.Sets/src/mage/cards/s/SwarmShambler.java @@ -15,9 +15,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; import mage.counters.CounterType; -import mage.filter.FilterPermanent; import mage.filter.StaticFilters; -import mage.filter.common.FilterControlledCreaturePermanent; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -68,12 +66,6 @@ public final class SwarmShambler extends CardImpl { class SwarmShamblerTriggeredAbility extends TriggeredAbilityImpl { - private static final FilterPermanent filter = new FilterControlledCreaturePermanent(); - - static { - filter.add(CounterType.P1P1.getPredicate()); - } - SwarmShamblerTriggeredAbility() { super(Zone.BATTLEFIELD, new CreateTokenEffect(new InsectToken()), false); } @@ -98,7 +90,7 @@ class SwarmShamblerTriggeredAbility extends TriggeredAbilityImpl { Permanent permanent = game.getPermanent(event.getTargetId()); return sourceObject != null && permanent != null - && filter.match(permanent, getSourceId(), getControllerId(), game) + && StaticFilters.FILTER_CONTROLLED_CREATURE_P1P1.match(permanent, getSourceId(), getControllerId(), game) && StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS.match(sourceObject, getSourceId(), getControllerId(), game); } diff --git a/Mage.Sets/src/mage/cards/s/SweatworksBrawler.java b/Mage.Sets/src/mage/cards/s/SweatworksBrawler.java index 7b511734520..8ccd1395d6c 100644 --- a/Mage.Sets/src/mage/cards/s/SweatworksBrawler.java +++ b/Mage.Sets/src/mage/cards/s/SweatworksBrawler.java @@ -28,7 +28,7 @@ public final class SweatworksBrawler extends CardImpl { addAbility(new ImproviseAbility()); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); } private SweatworksBrawler(final SweatworksBrawler card) { diff --git a/Mage.Sets/src/mage/cards/s/SwiftReconfiguration.java b/Mage.Sets/src/mage/cards/s/SwiftReconfiguration.java new file mode 100644 index 00000000000..afb72f1ef1b --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SwiftReconfiguration.java @@ -0,0 +1,112 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.keyword.CrewAbility; +import mage.abilities.keyword.EnchantAbility; +import mage.abilities.keyword.FlashAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SwiftReconfiguration extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent("creature or Vehicle"); + + static { + filter.add(Predicates.or( + CardType.CREATURE.getPredicate(), + SubType.VEHICLE.getPredicate() + )); + } + + public SwiftReconfiguration(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}"); + + this.subtype.add(SubType.AURA); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // Enchant creature or Vehicle + TargetPermanent auraTarget = new TargetPermanent(filter); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + this.addAbility(new EnchantAbility(auraTarget.getTargetName())); + + // Enchanted permanent is a Vehicle artifact with crew 5 and it loses all other card types. + this.addAbility(new SimpleStaticAbility(new SwiftReconfigurationEffect())); + } + + private SwiftReconfiguration(final SwiftReconfiguration card) { + super(card); + } + + @Override + public SwiftReconfiguration copy() { + return new SwiftReconfiguration(this); + } +} + +class SwiftReconfigurationEffect extends ContinuousEffectImpl { + + SwiftReconfigurationEffect() { + super(Duration.WhileOnBattlefield, Outcome.Detriment); + staticText = "enchanted permanent is a Vehicle artifact with crew 5 and it loses all other card types"; + } + + private SwiftReconfigurationEffect(final SwiftReconfigurationEffect effect) { + super(effect); + } + + @Override + public SwiftReconfigurationEffect copy() { + return new SwiftReconfigurationEffect(this); + } + + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + Permanent aura = source.getSourcePermanentIfItStillExists(game); + if (aura == null) { + return false; + } + Permanent permanent = game.getPermanent(aura.getAttachedTo()); + if (permanent == null) { + return false; + } + switch (layer) { + case TypeChangingEffects_4: + permanent.removeAllCardTypes(game); + permanent.addCardType(game, CardType.ARTIFACT); + permanent.addSubType(game, SubType.VEHICLE); + return true; + case AbilityAddingRemovingEffects_6: + permanent.addAbility(new CrewAbility(5)); + return true; + } + return false; + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean hasLayer(Layer layer) { + return layer == Layer.TypeChangingEffects_4 + || layer == Layer.AbilityAddingRemovingEffects_6; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SwordOfHours.java b/Mage.Sets/src/mage/cards/s/SwordOfHours.java index 992da06599f..f19b014ac47 100644 --- a/Mage.Sets/src/mage/cards/s/SwordOfHours.java +++ b/Mage.Sets/src/mage/cards/s/SwordOfHours.java @@ -75,7 +75,7 @@ class SwordOfHoursEffect extends OneShotEffect { } int result = player.rollDice(outcome, source, game, 12); int damage = (Integer) getValue("damage"); - if (result != 12 && damage <= result) { + if (result != 12 && damage >= result) { return true; } Permanent sourcePermanent = source.getSourcePermanentOrLKI(game); diff --git a/Mage.Sets/src/mage/cards/s/SwordOfKaldra.java b/Mage.Sets/src/mage/cards/s/SwordOfKaldra.java index 8db9ab11fa4..85702677c71 100644 --- a/Mage.Sets/src/mage/cards/s/SwordOfKaldra.java +++ b/Mage.Sets/src/mage/cards/s/SwordOfKaldra.java @@ -1,4 +1,3 @@ - package mage.cards.s; import mage.abilities.TriggeredAbilityImpl; @@ -71,7 +70,7 @@ class SwordOfKaldraTriggeredAbility extends TriggeredAbilityImpl { if (equipment != null && equipment.getAttachedTo() != null && event.getSourceId().equals(equipment.getAttachedTo())) { - getEffects().setTargetPointer(new FixedTarget(event.getTargetId())); + getEffects().setTargetPointer(new FixedTarget(event.getTargetId(), game)); return true; } return false; @@ -79,7 +78,7 @@ class SwordOfKaldraTriggeredAbility extends TriggeredAbilityImpl { @Override public String getTriggerPhrase() { - return "Whenever equipped creature deals damage to a creature, " ; + return "Whenever equipped creature deals damage to a creature, "; } } diff --git a/Mage.Sets/src/mage/cards/s/SylvanAnthem.java b/Mage.Sets/src/mage/cards/s/SylvanAnthem.java index 8953b252ebf..e11328ed0cd 100644 --- a/Mage.Sets/src/mage/cards/s/SylvanAnthem.java +++ b/Mage.Sets/src/mage/cards/s/SylvanAnthem.java @@ -34,7 +34,7 @@ public final class SylvanAnthem extends CardImpl { ).setText("green creatures you control get +1/+1"))); // Whenever a green creature enters the battlefield under your control, scry 1. - this.addAbility(new EntersBattlefieldControlledTriggeredAbility(new ScryEffect(1), filter)); + this.addAbility(new EntersBattlefieldControlledTriggeredAbility(new ScryEffect(1, false), filter)); } private SylvanAnthem(final SylvanAnthem card) { diff --git a/Mage.Sets/src/mage/cards/s/SynapseSliver.java b/Mage.Sets/src/mage/cards/s/SynapseSliver.java index aa91c233829..094db229dad 100644 --- a/Mage.Sets/src/mage/cards/s/SynapseSliver.java +++ b/Mage.Sets/src/mage/cards/s/SynapseSliver.java @@ -1,37 +1,40 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; +import mage.abilities.Ability; import mage.abilities.common.DealsDamageToAPlayerAllTriggeredAbility; -import mage.abilities.effects.Effect; -import mage.abilities.effects.common.DrawCardTargetEffect; +import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.Outcome; import mage.constants.SetTargetPointer; import mage.constants.SubType; import mage.filter.common.FilterCreaturePermanent; +import mage.game.Game; +import mage.players.Player; + +import java.util.UUID; /** - * * @author cbt33 */ public final class SynapseSliver extends CardImpl { + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(SubType.SLIVER, "a Sliver"); + public SynapseSliver(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}"); this.subtype.add(SubType.SLIVER); this.power = new MageInt(3); this.toughness = new MageInt(3); // Whenever a Sliver deals combat damage to a player, its controller may draw a card. - Effect effect = new DrawCardTargetEffect(1); - effect.setText("its controller may draw a card"); - this.addAbility(new DealsDamageToAPlayerAllTriggeredAbility(effect, - new FilterCreaturePermanent(SubType.SLIVER, "a Sliver"), - true, SetTargetPointer.PLAYER, true)); + this.addAbility(new DealsDamageToAPlayerAllTriggeredAbility( + new SynapseSliverEffect(), filter, false, + SetTargetPointer.PLAYER, true + )); } private SynapseSliver(final SynapseSliver card) { @@ -43,3 +46,28 @@ public final class SynapseSliver extends CardImpl { return new SynapseSliver(this); } } + +class SynapseSliverEffect extends OneShotEffect { + + SynapseSliverEffect() { + super(Outcome.Benefit); + staticText = "its controller may draw a card"; + } + + private SynapseSliverEffect(final SynapseSliverEffect effect) { + super(effect); + } + + @Override + public SynapseSliverEffect copy() { + return new SynapseSliverEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(getTargetPointer().getFirst(game, source)); + return player != null + && player.chooseUse(outcome, "Draw a card?", source, game) + && player.drawCards(1, source, game) > 0; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SyndicateTrafficker.java b/Mage.Sets/src/mage/cards/s/SyndicateTrafficker.java index 635823a0206..d83a217ea9e 100644 --- a/Mage.Sets/src/mage/cards/s/SyndicateTrafficker.java +++ b/Mage.Sets/src/mage/cards/s/SyndicateTrafficker.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -13,30 +11,34 @@ import mage.abilities.keyword.IndestructibleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.SubType; import mage.counters.CounterType; -import mage.filter.common.FilterControlledArtifactPermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetControlledPermanent; +import java.util.UUID; + /** - * * @author fireshoes */ public final class SyndicateTrafficker extends CardImpl { public SyndicateTrafficker(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}"); this.subtype.add(SubType.AETHERBORN); this.subtype.add(SubType.ROGUE); this.power = new MageInt(3); this.toughness = new MageInt(1); // {1}, Sacrifice an artifact: Put a +1/+1 counter on Syndicate Trafficker. It gains indestructible until end of turn. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.P1P1.createInstance()), new GenericManaCost(1)); - ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(new FilterControlledArtifactPermanent("an artifact")))); - ability.addEffect(new GainAbilitySourceEffect(IndestructibleAbility.getInstance(), Duration.EndOfTurn)); + Ability ability = new SimpleActivatedAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), new GenericManaCost(1) + ); + ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT_AN))); + ability.addEffect(new GainAbilitySourceEffect( + IndestructibleAbility.getInstance(), Duration.EndOfTurn + ).setText("It gains indestructible until end of turn")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SyphonEssence.java b/Mage.Sets/src/mage/cards/s/SyphonEssence.java new file mode 100644 index 00000000000..b11bb7700cd --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SyphonEssence.java @@ -0,0 +1,46 @@ +package mage.cards.s; + +import mage.abilities.effects.common.CounterTargetEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.FilterSpell; +import mage.filter.predicate.Predicates; +import mage.game.permanent.token.BloodToken; +import mage.target.TargetSpell; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SyphonEssence extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("creature or planeswalker spell"); + + static { + filter.add(Predicates.or( + CardType.CREATURE.getPredicate(), + CardType.PLANESWALKER.getPredicate() + )); + } + + public SyphonEssence(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{U}"); + + // Counter target creature or planeswalker spell. Create a Blood token. + this.getSpellAbility().addEffect(new CounterTargetEffect()); + this.getSpellAbility().addTarget(new TargetSpell(filter)); + this.getSpellAbility().addEffect(new CreateTokenEffect(new BloodToken())); + } + + private SyphonEssence(final SyphonEssence card) { + super(card); + } + + @Override + public SyphonEssence copy() { + return new SyphonEssence(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SyphonSliver.java b/Mage.Sets/src/mage/cards/s/SyphonSliver.java index 88e993c08fc..d9dbe7e6d1e 100644 --- a/Mage.Sets/src/mage/cards/s/SyphonSliver.java +++ b/Mage.Sets/src/mage/cards/s/SyphonSliver.java @@ -1,7 +1,5 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; @@ -9,13 +7,13 @@ import mage.abilities.keyword.LifelinkAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.SubType; import mage.filter.StaticFilters; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class SyphonSliver extends CardImpl { @@ -28,9 +26,10 @@ public final class SyphonSliver extends CardImpl { this.toughness = new MageInt(2); // Sliver creatures you control have lifelink. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, - new GainAbilityControlledEffect(LifelinkAbility.getInstance(), - Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS))); + this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( + LifelinkAbility.getInstance(), Duration.WhileOnBattlefield, + StaticFilters.FILTER_PERMANENT_SLIVERS + ))); } private SyphonSliver(final SyphonSliver card) { diff --git a/Mage.Sets/src/mage/cards/s/SyrFarenTheHengehammer.java b/Mage.Sets/src/mage/cards/s/SyrFarenTheHengehammer.java index adfcc139e8f..9c83b8053a0 100644 --- a/Mage.Sets/src/mage/cards/s/SyrFarenTheHengehammer.java +++ b/Mage.Sets/src/mage/cards/s/SyrFarenTheHengehammer.java @@ -24,7 +24,7 @@ import java.util.UUID; */ public final class SyrFarenTheHengehammer extends CardImpl { - private static final DynamicValue xValue = new SourcePermanentPowerCount(); + private static final DynamicValue xValue = new SourcePermanentPowerCount(false); private static final FilterPermanent filter = new FilterAttackingCreature("another target attacking creature"); diff --git a/Mage.Sets/src/mage/cards/s/SyrGwynHeroOfAshvale.java b/Mage.Sets/src/mage/cards/s/SyrGwynHeroOfAshvale.java index 8d159f68b66..e8688fbce6b 100644 --- a/Mage.Sets/src/mage/cards/s/SyrGwynHeroOfAshvale.java +++ b/Mage.Sets/src/mage/cards/s/SyrGwynHeroOfAshvale.java @@ -51,7 +51,7 @@ public final class SyrGwynHeroOfAshvale extends CardImpl { this.addAbility(VigilanceAbility.getInstance()); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // Whenever an equipped creature you control attacks, you draw a card and you lose 1 life. Ability ability = new AttacksCreatureYouControlTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/s/SyrKonradTheGrim.java b/Mage.Sets/src/mage/cards/s/SyrKonradTheGrim.java index 42c196ddeec..650293218b8 100644 --- a/Mage.Sets/src/mage/cards/s/SyrKonradTheGrim.java +++ b/Mage.Sets/src/mage/cards/s/SyrKonradTheGrim.java @@ -1,6 +1,7 @@ package mage.cards.s; import mage.MageInt; +import mage.MageObject; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; @@ -93,6 +94,11 @@ class SyrKonradTheGrimTriggeredAbility extends TriggeredAbilityImpl { && zEvent.getPlayerId() == this.getControllerId(); } + @Override + public boolean isInUseableZone(Game game, MageObject source, GameEvent event) { + return TriggeredAbilityImpl.isInUseableZoneDiesTrigger(this, event, game); + } + @Override public String getRule() { return "Whenever another creature dies, or a creature card is put into a graveyard " + diff --git a/Mage.Sets/src/mage/cards/s/SzatsWill.java b/Mage.Sets/src/mage/cards/s/SzatsWill.java index b4941126f76..5e07b91a2a3 100644 --- a/Mage.Sets/src/mage/cards/s/SzatsWill.java +++ b/Mage.Sets/src/mage/cards/s/SzatsWill.java @@ -68,7 +68,7 @@ class SzatsWillEffect extends OneShotEffect { SzatsWillEffect() { super(Outcome.Benefit); - staticText = "exile all cards from all opponents' graveyards, " + staticText = "exile all opponents' graveyards, " + "then create X 0/1 black Thrull creature tokens, " + "where X is the greatest power among creature cards exiled this way"; } diff --git a/Mage.Sets/src/mage/cards/t/TahngarthsGlare.java b/Mage.Sets/src/mage/cards/t/TahngarthsGlare.java new file mode 100644 index 00000000000..e882ad3ac44 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TahngarthsGlare.java @@ -0,0 +1,74 @@ +package mage.cards.t; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetOpponent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TahngarthsGlare extends CardImpl { + + public TahngarthsGlare(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{R}"); + + // Look at the top three cards of target opponent's library, then put them back in any order. That player looks at the top three cards of your library, then puts them back in any order. + this.getSpellAbility().addEffect(new TahngarthsGlareEffect()); + this.getSpellAbility().addTarget(new TargetOpponent()); + } + + private TahngarthsGlare(final TahngarthsGlare card) { + super(card); + } + + @Override + public TahngarthsGlare copy() { + return new TahngarthsGlare(this); + } +} + +class TahngarthsGlareEffect extends OneShotEffect { + + TahngarthsGlareEffect() { + super(Outcome.Benefit); + staticText = "look at the top three cards of target opponent's library, then put them back in any order. " + + "That player looks at the top three cards of your library, then puts them back in any order"; + } + + private TahngarthsGlareEffect(final TahngarthsGlareEffect effect) { + super(effect); + } + + @Override + public TahngarthsGlareEffect copy() { + return new TahngarthsGlareEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Player opponent = game.getPlayer(source.getFirstTarget()); + if (controller == null || opponent == null) { + return false; + } + lookAndPutBack(controller, opponent, source, game); + lookAndPutBack(opponent, controller, source, game); + return true; + } + + private static final void lookAndPutBack(Player player1, Player player2, Ability source, Game game) { + Cards cards = new CardsImpl(player2.getLibrary().getTopCards(game, 3)); + player1.lookAtCards(player2.getName() + "'s library", cards, game); + player1.putCardsOnTopOfLibrary(cards, game, source, true); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TaigamSidisisHand.java b/Mage.Sets/src/mage/cards/t/TaigamSidisisHand.java index dac212a4391..945448bd291 100644 --- a/Mage.Sets/src/mage/cards/t/TaigamSidisisHand.java +++ b/Mage.Sets/src/mage/cards/t/TaigamSidisisHand.java @@ -1,4 +1,3 @@ - package mage.cards.t; import java.util.UUID; @@ -26,7 +25,6 @@ import mage.constants.SubType; import mage.constants.SuperType; import mage.constants.TargetController; import mage.constants.Zone; -import mage.filter.FilterCard; import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; @@ -60,7 +58,7 @@ public final class TaigamSidisisHand extends CardImpl { // {B}, {T}, Exile X cards from your graveyard: Target creature gets -X/-X until end of turn. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new TaigamSidisisHandEffect(), new ManaCostsImpl("{B}")); ability.addCost(new TapSourceCost()); - ability.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard(0, Integer.MAX_VALUE, new FilterCard("cards from your graveyard")))); + ability.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard(0, Integer.MAX_VALUE, StaticFilters.FILTER_CARDS_FROM_YOUR_GRAVEYARD))); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } @@ -103,7 +101,7 @@ class TaigamSidisisHandEffect extends OneShotEffect { if (cost instanceof ExileFromGraveCost) { amount = ((ExileFromGraveCost) cost).getExiledCards().size(); ContinuousEffect effect = new BoostTargetEffect(-amount, -amount, Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(source.getTargets().getFirstTarget())); + effect.setTargetPointer(new FixedTarget(source.getTargets().getFirstTarget(), game)); game.addEffect(effect, source); } } diff --git a/Mage.Sets/src/mage/cards/t/TakenumaAbandonedMire.java b/Mage.Sets/src/mage/cards/t/TakenumaAbandonedMire.java new file mode 100644 index 00000000000..027398fc150 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TakenumaAbandonedMire.java @@ -0,0 +1,93 @@ +package mage.cards.t; + +import mage.abilities.Ability; +import mage.abilities.costs.costadjusters.LegendaryCreatureCostAdjuster; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.ChannelAbility; +import mage.abilities.mana.BlackManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SuperType; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TakenumaAbandonedMire extends CardImpl { + + public TakenumaAbandonedMire(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + this.addSuperType(SuperType.LEGENDARY); + + // {T}: Add {B}. + this.addAbility(new BlackManaAbility()); + + // Channel — {3}{B}, Discard Takenuma, Abandoned Mire: Mill three cards, then return a creature or planeswalker card from your graveyard to your hand. This ability costs {1} less to activate for each legendary creature you control. + Ability ability = new ChannelAbility("{3}{B}", new TakenumaAbandonedMireEffect()); + ability.setCostAdjuster(LegendaryCreatureCostAdjuster.instance); + this.addAbility(ability); + } + + private TakenumaAbandonedMire(final TakenumaAbandonedMire card) { + super(card); + } + + @Override + public TakenumaAbandonedMire copy() { + return new TakenumaAbandonedMire(this); + } +} + +class TakenumaAbandonedMireEffect extends OneShotEffect { + + private static final FilterCard filter = new FilterCard("creature or planeswalker card"); + + static { + filter.add(Predicates.or( + CardType.CREATURE.getPredicate(), + CardType.PLANESWALKER.getPredicate() + )); + } + + TakenumaAbandonedMireEffect() { + super(Outcome.Benefit); + staticText = "mill three cards, then return a creature or planeswalker card from your graveyard to your hand. " + + "This ability costs {1} less to activate for each legendary creature you control"; + } + + private TakenumaAbandonedMireEffect(final TakenumaAbandonedMireEffect effect) { + super(effect); + } + + @Override + public TakenumaAbandonedMireEffect copy() { + return new TakenumaAbandonedMireEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + player.millCards(3, source, game); + if (player.getGraveyard().count(filter, game) < 1) { + return true; + } + TargetCard target = new TargetCardInGraveyard(filter); + target.setNotTarget(true); + player.choose(outcome, player.getGraveyard(), target, game); + return player.moveCards(game.getCard(target.getFirstTarget()), Zone.HAND, source, game); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TalarasBattalion.java b/Mage.Sets/src/mage/cards/t/TalarasBattalion.java index 6d11ea84c5f..16ef5fab6fd 100644 --- a/Mage.Sets/src/mage/cards/t/TalarasBattalion.java +++ b/Mage.Sets/src/mage/cards/t/TalarasBattalion.java @@ -1,38 +1,31 @@ - package mage.cards.t; -import java.util.UUID; import mage.MageInt; -import mage.ObjectColor; import mage.abilities.Ability; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.condition.Condition; -import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; +import mage.abilities.common.CastOnlyIfConditionIsTrueAbility; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Outcome; import mage.constants.WatcherScope; -import mage.constants.Zone; -import mage.filter.FilterSpell; -import mage.filter.predicate.mageobject.ColorPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; import mage.game.stack.Spell; import mage.watchers.Watcher; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + /** - * - * @author jeffwadsworth + * @author TheElk801 */ public final class TalarasBattalion extends CardImpl { public TalarasBattalion(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); this.subtype.add(SubType.ELF); this.subtype.add(SubType.WARRIOR); @@ -43,7 +36,10 @@ public final class TalarasBattalion extends CardImpl { this.addAbility(TrampleAbility.getInstance()); // Cast Talara's Battalion only if you've cast another green spell this turn. - this.addAbility(new SimpleStaticAbility(Zone.ALL, new TalarasBattalionEffect()), new TalarasBattalionWatcher(this.getId())); + this.addAbility(new CastOnlyIfConditionIsTrueAbility( + TalarasBattalionWatcher::checkSpell, + "cast {this} only if you've cast another green spell this turn" + ), new TalarasBattalionWatcher()); } @@ -57,82 +53,32 @@ public final class TalarasBattalion extends CardImpl { } } -class TalarasBattalionEffect extends ContinuousRuleModifyingEffectImpl { - - TalarasBattalionEffect() { - super(Duration.EndOfGame, Outcome.Detriment); - staticText = "Cast this spell only if you've cast another green spell this turn"; - } - - TalarasBattalionEffect(final TalarasBattalionEffect effect) { - super(effect); - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getType() == GameEvent.EventType.CAST_SPELL - && event.getSourceId().equals(source.getSourceId())) { - CastGreenSpellThisTurnCondition condition = new CastGreenSpellThisTurnCondition(); - return (!condition.apply(game, source)); - } - return false; - - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public TalarasBattalionEffect copy() { - return new TalarasBattalionEffect(this); - } -} - -class CastGreenSpellThisTurnCondition implements Condition { - - @Override - public boolean apply(Game game, Ability source) { - TalarasBattalionWatcher watcher = game.getState().getWatcher(TalarasBattalionWatcher.class, source.getControllerId()); - if (watcher != null) { - return watcher.conditionMet(); - } - return false; - } -} - class TalarasBattalionWatcher extends Watcher { - private static final FilterSpell filter = new FilterSpell(); + private final Set playerSet = new HashSet<>(); - static { - filter.add(new ColorPredicate(ObjectColor.GREEN)); - } - private final UUID cardId; - - public TalarasBattalionWatcher(UUID cardId) { - super(WatcherScope.PLAYER); - this.cardId = cardId; + TalarasBattalionWatcher() { + super(WatcherScope.GAME); } @Override public void watch(GameEvent event, Game game) { - if (condition == true) { //no need to check - condition has already occured + if (event.getType() != EventType.SPELL_CAST) { return; } - if (event.getType() == GameEvent.EventType.SPELL_CAST - && controllerId.equals(event.getPlayerId())) { - Spell spell = game.getStack().getSpell(event.getTargetId()); - if (!spell.getSourceId().equals(cardId) && filter.match(spell, game)) { - condition = true; - } + Spell spell = game.getSpell(event.getSourceId()); + if (spell != null && spell.getColor(game).isGreen()) { + playerSet.add(spell.getControllerId()); } } @Override public void reset() { super.reset(); - condition = false; + playerSet.clear(); + } + + static boolean checkSpell(Game game, Ability source) { + return game.getState().getWatcher(TalarasBattalionWatcher.class).playerSet.contains(source.getControllerId()); } } diff --git a/Mage.Sets/src/mage/cards/t/TalesOfMasterSeshiro.java b/Mage.Sets/src/mage/cards/t/TalesOfMasterSeshiro.java new file mode 100644 index 00000000000..04a1d542911 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TalesOfMasterSeshiro.java @@ -0,0 +1,72 @@ +package mage.cards.t; + +import mage.abilities.common.SagaAbility; +import mage.abilities.effects.Effects; +import mage.abilities.effects.common.ExileSagaAndReturnTransformedEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.TransformAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SagaChapter; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TalesOfMasterSeshiro extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledPermanent("creature or Vehicle you control"); + + static { + filter.add(Predicates.or( + CardType.CREATURE.getPredicate(), + SubType.VEHICLE.getPredicate() + )); + } + + public TalesOfMasterSeshiro(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{G}"); + + this.subtype.add(SubType.SAGA); + this.secondSideCardClazz = mage.cards.s.SeshirosLivingLegacy.class; + + // (As this Saga enters and after your draw step, add a lore counter.) + SagaAbility sagaAbility = new SagaAbility(this); + + // I, II — Put a +1/+1 counter on target creature or Vehicle you control. It gains vigilance until end of turn. + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_II, + new Effects( + new AddCountersTargetEffect(CounterType.P1P1.createInstance()), + new GainAbilityTargetEffect(VigilanceAbility.getInstance()) + .setText("It gains vigilance until end of turn") + ), new TargetPermanent(filter) + ); + + // III — Exile this Saga, then return it to the battlefield transformed under your control. + this.addAbility(new TransformAbility()); + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, new ExileSagaAndReturnTransformedEffect()); + + this.addAbility(sagaAbility); + } + + private TalesOfMasterSeshiro(final TalesOfMasterSeshiro card) { + super(card); + } + + @Override + public TalesOfMasterSeshiro copy() { + return new TalesOfMasterSeshiro(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/Tallowisp.java b/Mage.Sets/src/mage/cards/t/Tallowisp.java index 10afc2125ee..b513e1266ac 100644 --- a/Mage.Sets/src/mage/cards/t/Tallowisp.java +++ b/Mage.Sets/src/mage/cards/t/Tallowisp.java @@ -41,7 +41,7 @@ public final class Tallowisp extends CardImpl { this.toughness = new MageInt(3); // Whenever you cast a Spirit or Arcane spell, you may search your library for an Aura card with enchant creature, reveal it, and put it into your hand. If you do, shuffle your library. - this.addAbility(new SpellCastControllerTriggeredAbility(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filterAura), true, true), StaticFilters.SPIRIT_OR_ARCANE_CARD, true)); + this.addAbility(new SpellCastControllerTriggeredAbility(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filterAura), true, true), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true)); } private Tallowisp(final Tallowisp card) { diff --git a/Mage.Sets/src/mage/cards/t/TameshiRealityArchitect.java b/Mage.Sets/src/mage/cards/t/TameshiRealityArchitect.java new file mode 100644 index 00000000000..ae70ac99bd0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TameshiRealityArchitect.java @@ -0,0 +1,103 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.common.ZoneChangeTriggeredAbility; +import mage.abilities.costs.common.ReturnToHandChosenControlledPermanentCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.common.FilterArtifactOrEnchantmentCard; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.target.common.TargetCardInYourGraveyard; +import mage.target.common.TargetControlledPermanent; +import mage.target.targetadjustment.TargetAdjuster; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TameshiRealityArchitect extends CardImpl { + + public TameshiRealityArchitect(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.MOONFOLK); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Whenever one or more noncreature permanents are returned to hand, draw a card. This ability triggers only once each turn. + this.addAbility(new TameshiRealityArchitectTriggeredAbility()); + + // {X}{W}, Return a land you control to its owner's hand: Return target artifact or enchantment card with mana value X or less from your graveyard to the battlefield. Activate only as a sorcery. + Ability ability = new ActivateAsSorceryActivatedAbility( + new ReturnFromGraveyardToBattlefieldTargetEffect() + .setText("return target artifact or enchantment card with " + + "mana value X or less from your graveyard to the battlefield"), + new ManaCostsImpl<>("{X}{W}") + ); + ability.addCost(new ReturnToHandChosenControlledPermanentCost( + new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND) + )); + this.addAbility(ability.setTargetAdjuster(TameshiRealityArchitectAdjuster.instance)); + } + + private TameshiRealityArchitect(final TameshiRealityArchitect card) { + super(card); + } + + @Override + public TameshiRealityArchitect copy() { + return new TameshiRealityArchitect(this); + } +} + +enum TameshiRealityArchitectAdjuster implements TargetAdjuster { + instance; + + @Override + public void adjustTargets(Ability ability, Game game) { + int xValue = ability.getManaCostsToPay().getX(); + FilterCard filter = new FilterArtifactOrEnchantmentCard( + "artifact or enchantment card with mana value " + xValue + " or less from your graveyard" + ); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, xValue + 1)); + ability.getTargets().clear(); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + } +} + +class TameshiRealityArchitectTriggeredAbility extends ZoneChangeTriggeredAbility { + + TameshiRealityArchitectTriggeredAbility() { + super(Zone.BATTLEFIELD, Zone.BATTLEFIELD, Zone.HAND, new DrawCardSourceControllerEffect(1), + "Whenever one or more noncreature permanents are returned to hand, ", false); + this.setTriggersOnce(true); + } + + private TameshiRealityArchitectTriggeredAbility(final TameshiRealityArchitectTriggeredAbility ability) { + super(ability); + } + + @Override + public TameshiRealityArchitectTriggeredAbility copy() { + return new TameshiRealityArchitectTriggeredAbility(this); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return super.checkTrigger(event, game) && !((ZoneChangeEvent) event).getTarget().isCreature(game); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TamiyoCollectorOfTales.java b/Mage.Sets/src/mage/cards/t/TamiyoCollectorOfTales.java index 11e4b8bff87..cd7a1539c82 100644 --- a/Mage.Sets/src/mage/cards/t/TamiyoCollectorOfTales.java +++ b/Mage.Sets/src/mage/cards/t/TamiyoCollectorOfTales.java @@ -2,12 +2,11 @@ package mage.cards.t; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ChooseACardNameEffect; -import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; import mage.cards.*; import mage.constants.*; import mage.game.Game; @@ -31,7 +30,7 @@ public final class TamiyoCollectorOfTales extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.TAMIYO); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // Spells and abilities your opponents control can't cause you to discard cards or sacrifice permanents. this.addAbility(new SimpleStaticAbility(new TamiyoCollectorOfTalesRuleEffect())); @@ -40,7 +39,7 @@ public final class TamiyoCollectorOfTales extends CardImpl { this.addAbility(new LoyaltyAbility(new TamiyoCollectorOfTalesEffect(), 1)); // -3: Return target card from your graveyard to your hand. - Ability ability = new LoyaltyAbility(new ReturnToHandTargetEffect(), -3); + Ability ability = new LoyaltyAbility(new ReturnFromGraveyardToHandTargetEffect(), -3); ability.addTarget(new TargetCardInYourGraveyard()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/t/TamiyoCompleatedSage.java b/Mage.Sets/src/mage/cards/t/TamiyoCompleatedSage.java new file mode 100644 index 00000000000..36bc4cddf45 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TamiyoCompleatedSage.java @@ -0,0 +1,115 @@ +package mage.cards.t; + +import mage.abilities.Ability; +import mage.abilities.LoyaltyAbility; +import mage.abilities.dynamicvalue.common.GetXLoyaltyValue; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenCopyTargetEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DontUntapInControllersNextUntapStepTargetEffect; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.keyword.CompleatedAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.common.FilterPermanentCard; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.game.Game; +import mage.game.permanent.PermanentCard; +import mage.game.permanent.token.TamiyosNotebookToken; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.common.TargetCardInYourGraveyard; +import mage.target.targetadjustment.TargetAdjuster; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TamiyoCompleatedSage extends CardImpl { + + public TamiyoCompleatedSage(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{2}{G}{G/U/P}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.TAMIYO); + this.setStartingLoyalty(5); + + // Compleated + this.addAbility(CompleatedAbility.getInstance()); + + // +1: Tap up to one target artifact or creature. It doesn't untap during its controller's next untap step. + Ability ability = new LoyaltyAbility(new TapTargetEffect(), 1); + ability.addEffect(new DontUntapInControllersNextUntapStepTargetEffect("it")); + ability.addTarget(new TargetPermanent( + 0, 1, StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_CREATURE + )); + this.addAbility(ability); + + // −X: Exile target nonland permanent card with mana value X from your graveyard. Create a token that's a copy of that card. + this.addAbility(new LoyaltyAbility(new TamiyoCompleatedSageEffect()).setTargetAdjuster(TamiyoCompleatedSageAdjuster.instance)); + + // −7: Create Tamiyo's Notebook, a legendary colorless artifact token with "Spells you cast cost {2} less to cast" and "{T}: Draw a card." + this.addAbility(new LoyaltyAbility(new CreateTokenEffect(new TamiyosNotebookToken()), -7)); + } + + private TamiyoCompleatedSage(final TamiyoCompleatedSage card) { + super(card); + } + + @Override + public TamiyoCompleatedSage copy() { + return new TamiyoCompleatedSage(this); + } +} + +enum TamiyoCompleatedSageAdjuster implements TargetAdjuster { + instance; + + @Override + public void adjustTargets(Ability ability, Game game) { + int xValue = GetXLoyaltyValue.instance.calculate(game, ability, null); + FilterCard filter = new FilterPermanentCard("nonland permanent card with mana value " + xValue); + filter.add(Predicates.not(CardType.LAND.getPredicate())); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, xValue)); + ability.getTargets().clear(); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + } +} + +class TamiyoCompleatedSageEffect extends OneShotEffect { + + TamiyoCompleatedSageEffect() { + super(Outcome.Benefit); + staticText = "exile target nonland permanent card with mana value X " + + "from your graveyard. Create a token that's a copy of that card"; + } + + private TamiyoCompleatedSageEffect(final TamiyoCompleatedSageEffect effect) { + super(effect); + } + + @Override + public TamiyoCompleatedSageEffect copy() { + return new TamiyoCompleatedSageEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Card card = game.getCard(source.getFirstTarget()); + if (player == null || card == null) { + return false; + } + player.moveCards(card, Zone.EXILED, source, game); + return new CreateTokenCopyTargetEffect().setSavedPermanent( + new PermanentCard(card, source.getControllerId(), game) + ).apply(game, source); + } +} +// aqua got norted diff --git a/Mage.Sets/src/mage/cards/t/TamiyoFieldResearcher.java b/Mage.Sets/src/mage/cards/t/TamiyoFieldResearcher.java index 8c9b21198de..a3062ca82c3 100644 --- a/Mage.Sets/src/mage/cards/t/TamiyoFieldResearcher.java +++ b/Mage.Sets/src/mage/cards/t/TamiyoFieldResearcher.java @@ -5,7 +5,6 @@ import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DontUntapInControllersNextUntapStepTargetEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; @@ -51,7 +50,7 @@ public final class TamiyoFieldResearcher extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.TAMIYO); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Choose up to two target creatures. Until your next turn, whenever either of those creatures deals combat damage, you draw a card. Ability ability = new LoyaltyAbility(new TamiyoFieldResearcherEffect1(), 1); diff --git a/Mage.Sets/src/mage/cards/t/TamiyoTheMoonSage.java b/Mage.Sets/src/mage/cards/t/TamiyoTheMoonSage.java index 1b25f0ce93f..6b2f271d4fc 100644 --- a/Mage.Sets/src/mage/cards/t/TamiyoTheMoonSage.java +++ b/Mage.Sets/src/mage/cards/t/TamiyoTheMoonSage.java @@ -4,7 +4,6 @@ package mage.cards.t; import java.util.UUID; 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.common.DontUntapInControllersNextUntapStepTargetEffect; @@ -35,11 +34,11 @@ public final class TamiyoTheMoonSage extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.TAMIYO); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Tap target permanent. It doesn't untap during its controller's next untap step. LoyaltyAbility ability = new LoyaltyAbility(new TapTargetEffect(), 1); - ability.addEffect(new DontUntapInControllersNextUntapStepTargetEffect()); + ability.addEffect(new DontUntapInControllersNextUntapStepTargetEffect("it")); Target target = new TargetPermanent(); ability.addTarget(target); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/t/TamiyosCompleation.java b/Mage.Sets/src/mage/cards/t/TamiyosCompleation.java new file mode 100644 index 00000000000..3d5fe53db76 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TamiyosCompleation.java @@ -0,0 +1,132 @@ +package mage.cards.t; + +import java.util.UUID; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DontUntapInControllersUntapStepEnchantedEffect; +import mage.constants.*; +import mage.abilities.keyword.FlashAbility; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicates; +import mage.abilities.effects.common.AttachEffect; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; + +/** + * + * @author weirddan455 + */ +public final class TamiyosCompleation extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent("artifact, creature, or planeswalker"); + + static { + filter.add(Predicates.or(CardType.ARTIFACT.getPredicate(), CardType.CREATURE.getPredicate(), CardType.PLANESWALKER.getPredicate())); + } + + public TamiyosCompleation(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{U}"); + + this.subtype.add(SubType.AURA); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // Enchant artifact, creature, or planeswalker + TargetPermanent auraTarget = new TargetPermanent(filter); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.LoseAbility)); + this.addAbility(new EnchantAbility(auraTarget.getTargetName())); + + // When Tamiyo's Compleation enters the battlefield, tap enchanted permanent. If it's an Equipment, unattach it. + this.addAbility(new EntersBattlefieldTriggeredAbility(new TamiyosCompleationTapEffect())); + + // Enchanted permanent loses all abilities and doesn't untap during its controller's untap step. + Ability ability = new SimpleStaticAbility(new TamiyosCompleationLoseAbilitiesEffect()); + ability.addEffect(new DontUntapInControllersUntapStepEnchantedEffect().setText("and doesn't untap during its controller's untap step")); + this.addAbility(ability); + } + + private TamiyosCompleation(final TamiyosCompleation card) { + super(card); + } + + @Override + public TamiyosCompleation copy() { + return new TamiyosCompleation(this); + } +} + +class TamiyosCompleationTapEffect extends OneShotEffect { + + public TamiyosCompleationTapEffect() { + super(Outcome.Tap); + this.staticText = "tap enchanted permanent. If it's an Equipment, unattach it"; + } + + private TamiyosCompleationTapEffect(final TamiyosCompleationTapEffect effect) { + super(effect); + } + + @Override + public TamiyosCompleationTapEffect copy() { + return new TamiyosCompleationTapEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent enchantment = source.getSourcePermanentIfItStillExists(game); + if (enchantment != null) { + Permanent enchanted = game.getPermanent(enchantment.getAttachedTo()); + if (enchanted != null) { + enchanted.tap(source, game); + if (enchanted.hasSubtype(SubType.EQUIPMENT, game)) { + Permanent creature = game.getPermanent(enchanted.getAttachedTo()); + if (creature != null) { + creature.removeAttachment(enchanted.getId(), source, game); + } + } + return true; + } + } + return false; + } +} + +class TamiyosCompleationLoseAbilitiesEffect extends ContinuousEffectImpl { + + public TamiyosCompleationLoseAbilitiesEffect() { + super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.LoseAbility); + this.staticText = "Enchanted permanent loses all abilities"; + } + + private TamiyosCompleationLoseAbilitiesEffect(final TamiyosCompleationLoseAbilitiesEffect effect) { + super(effect); + } + + @Override + public TamiyosCompleationLoseAbilitiesEffect copy() { + return new TamiyosCompleationLoseAbilitiesEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent enchantment = source.getSourcePermanentIfItStillExists(game); + if (enchantment != null) { + Permanent enchanted = game.getPermanent(enchantment.getAttachedTo()); + if (enchanted != null) { + enchanted.removeAllAbilities(source.getSourceId(), game); + return true; + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/t/TamiyosSafekeeping.java b/Mage.Sets/src/mage/cards/t/TamiyosSafekeeping.java new file mode 100644 index 00000000000..b3295b763b1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TamiyosSafekeeping.java @@ -0,0 +1,39 @@ +package mage.cards.t; + +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.HexproofAbility; +import mage.abilities.keyword.IndestructibleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TamiyosSafekeeping extends CardImpl { + + public TamiyosSafekeeping(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{G}"); + + // Target permanent you control gains hexproof and indestructible until end of turn. You gain 2 life. + this.getSpellAbility().addEffect(new GainAbilityTargetEffect(HexproofAbility.getInstance()) + .setText("target permanent you control gains hexproof")); + this.getSpellAbility().addEffect(new GainAbilityTargetEffect(IndestructibleAbility.getInstance()) + .setText("and indestructible until end of turn")); + this.getSpellAbility().addTarget(new TargetControlledPermanent()); + this.getSpellAbility().addEffect(new GainLifeEffect(2)); + } + + private TamiyosSafekeeping(final TamiyosSafekeeping card) { + super(card); + } + + @Override + public TamiyosSafekeeping copy() { + return new TamiyosSafekeeping(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TangleclawWerewolf.java b/Mage.Sets/src/mage/cards/t/TangleclawWerewolf.java index a7133e5cb15..5ddfe0c12bc 100644 --- a/Mage.Sets/src/mage/cards/t/TangleclawWerewolf.java +++ b/Mage.Sets/src/mage/cards/t/TangleclawWerewolf.java @@ -29,7 +29,6 @@ public final class TangleclawWerewolf extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(4); - this.transformable = true; this.secondSideCardClazz = mage.cards.f.FibrousEntangler.class; // Tangleclaw Werewolf can block an additional creature each combat. @@ -37,7 +36,7 @@ public final class TangleclawWerewolf extends CardImpl { // {6}{G}: Transform Tangleclaw Werewolf. this.addAbility(new TransformAbility()); - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new TransformSourceEffect(true), new ManaCostsImpl<>("{6}{G}"))); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new TransformSourceEffect(), new ManaCostsImpl<>("{6}{G}"))); } private TangleclawWerewolf(final TangleclawWerewolf card) { diff --git a/Mage.Sets/src/mage/cards/t/Tanglewalker.java b/Mage.Sets/src/mage/cards/t/Tanglewalker.java index e898c42f745..665c54aeef7 100644 --- a/Mage.Sets/src/mage/cards/t/Tanglewalker.java +++ b/Mage.Sets/src/mage/cards/t/Tanglewalker.java @@ -15,7 +15,7 @@ import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Zone; import mage.filter.FilterPermanent; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; import mage.filter.common.FilterLandPermanent; /** @@ -39,7 +39,7 @@ public final class Tanglewalker extends CardImpl { // Each creature you control can't be blocked as long as defending player controls an artifact land. Effect effect = new ConditionalRestrictionEffect( - new CantBeBlockedAllEffect(new FilterControlledCreaturePermanent("Creatures you control"), Duration.WhileOnBattlefield), + new CantBeBlockedAllEffect(StaticFilters.FILTER_CONTROLLED_CREATURES, Duration.WhileOnBattlefield), new DefendingPlayerControlsCondition(filter)); effect.setText("Each creature you control can't be blocked as long as defending player controls an artifact land"); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); diff --git a/Mage.Sets/src/mage/cards/t/TanukiTransplanter.java b/Mage.Sets/src/mage/cards/t/TanukiTransplanter.java new file mode 100644 index 00000000000..5b76967040e --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TanukiTransplanter.java @@ -0,0 +1,125 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.ReconfigureAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TanukiTransplanter extends CardImpl { + + public TanukiTransplanter(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}{G}"); + + this.subtype.add(SubType.EQUIPMENT); + this.subtype.add(SubType.DOG); + this.power = new MageInt(2); + this.toughness = new MageInt(4); + + // Whenever Tanuki Transplanter or equipped creature attacks, add an amount of {G} equal to its power. Until end of turn, you don't lose this mana as steps and phases end. + this.addAbility(new TanukiTransplanterTriggeredAbility()); + + // Reconfigure {3} + this.addAbility(new ReconfigureAbility("{3}")); + } + + private TanukiTransplanter(final TanukiTransplanter card) { + super(card); + } + + @Override + public TanukiTransplanter copy() { + return new TanukiTransplanter(this); + } +} + +class TanukiTransplanterTriggeredAbility extends TriggeredAbilityImpl { + + TanukiTransplanterTriggeredAbility() { + super(Zone.BATTLEFIELD, new TanukiTransplanterEffect()); + } + + private TanukiTransplanterTriggeredAbility(final TanukiTransplanterTriggeredAbility ability) { + super(ability); + } + + @Override + public TanukiTransplanterTriggeredAbility copy() { + return new TanukiTransplanterTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DECLARED_ATTACKERS; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + UUID attacker; + if (!game.getCombat().getAttackers().contains(getSourceId())) { + Permanent permanent = getSourcePermanentOrLKI(game); + if (permanent != null && game.getCombat().getAttackers().contains(permanent.getAttachedTo())) { + attacker = permanent.getAttachedTo(); + } else { + attacker = null; + } + } else { + attacker = getSourceId(); + } + if (attacker == null) { + return false; + } + getEffects().setTargetPointer(new FixedTarget(attacker, game)); + return true; + } + + @Override + public String getRule() { + return "Whenever {this} or equipped creature attacks, add an amount of {G} equal to its power. " + + "Until end of turn, you don't lose this mana as steps and phases end."; + } +} + +class TanukiTransplanterEffect extends OneShotEffect { + + TanukiTransplanterEffect() { + super(Outcome.Benefit); + } + + private TanukiTransplanterEffect(final TanukiTransplanterEffect effect) { + super(effect); + } + + @Override + public TanukiTransplanterEffect copy() { + return new TanukiTransplanterEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (player == null || permanent == null || permanent.getPower().getValue() < 1) { + return false; + } + player.getManaPool().addMana(Mana.GreenMana(permanent.getPower().getValue()), game, source, true); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/t/TatsunariToadRider.java b/Mage.Sets/src/mage/cards/t/TatsunariToadRider.java new file mode 100644 index 00000000000..e536f6e3064 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TatsunariToadRider.java @@ -0,0 +1,90 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.combat.CantBeBlockedByAllTargetEffect; +import mage.abilities.effects.common.combat.CantBeBlockedByCreaturesSourceEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.ReachAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.filter.predicate.mageobject.NamePredicate; +import mage.game.permanent.token.KeimiToken; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TatsunariToadRider extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledCreaturePermanent(); + private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent(); + private static final FilterPermanent filter3 = new FilterControlledPermanent(SubType.FROG); + + static { + filter.add(new NamePredicate("Keimi")); + filter2.add(Predicates.or( + new AbilityPredicate(FlyingAbility.class), + new AbilityPredicate(ReachAbility.class) + )); + } + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter, ComparisonType.EQUAL_TO, 0); + + public TatsunariToadRider(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.NINJA); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Whenever you cast an enchantment spell, if you don't control a creature named Keimi, create Keimi, a legendary 3/3 black and green Frog creature token with "Whenever you cast an enchantment spell, each opponent loses 1 life and you gain 1 life." + this.addAbility(new ConditionalInterveningIfTriggeredAbility( + new SpellCastControllerTriggeredAbility( + new CreateTokenEffect(new KeimiToken()), + StaticFilters.FILTER_SPELL_AN_ENCHANTMENT, false + ), condition, "Whenever you cast an enchantment spell, if you don't control " + + "a creature named Keimi, create Keimi, a legendary 3/3 black and green Frog creature token with " + + "\"Whenever you cast an enchantment spell, each opponent loses 1 life and you gain 1 life.\"" + )); + + // {1}{G/U}: Tatsunari, Toad Rider and target Frog you control can't be blocked this turn except by creatures with flying or reach. + Ability ability = new SimpleActivatedAbility( + new CantBeBlockedByCreaturesSourceEffect( + filter2, Duration.EndOfTurn + ).setText("{this}"), new ManaCostsImpl<>("{1}{G/U}") + ); + ability.addEffect(new CantBeBlockedByAllTargetEffect(filter2, Duration.EndOfTurn) + .setText("and target Frog you control can't be blocked this turn except by creatures with flying or reach")); + ability.addTarget(new TargetPermanent(filter3)); + this.addAbility(ability); + } + + private TatsunariToadRider(final TatsunariToadRider card) { + super(card); + } + + @Override + public TatsunariToadRider copy() { + return new TatsunariToadRider(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TattooWard.java b/Mage.Sets/src/mage/cards/t/TattooWard.java index 481fec6fbf2..bd2d801d4e1 100644 --- a/Mage.Sets/src/mage/cards/t/TattooWard.java +++ b/Mage.Sets/src/mage/cards/t/TattooWard.java @@ -1,9 +1,5 @@ - package mage.cards.t; -import java.util.UUID; -import mage.constants.SubType; -import mage.target.common.TargetCreaturePermanent; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; @@ -12,31 +8,22 @@ import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.continuous.BoostEnchantedEffect; import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; -import mage.constants.Outcome; -import mage.target.TargetPermanent; import mage.abilities.keyword.EnchantAbility; import mage.abilities.keyword.ProtectionAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AttachmentType; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Zone; -import mage.filter.FilterCard; +import mage.constants.*; import mage.filter.StaticFilters; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; /** - * * @author jeffwadsworth */ public final class TattooWard extends CardImpl { - private static final FilterCard filter = new FilterCard("enchantments"); - - static { - filter.add(CardType.ENCHANTMENT.getPredicate()); - } - public TattooWard(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); @@ -46,20 +33,22 @@ public final class TattooWard extends CardImpl { TargetPermanent auraTarget = new TargetCreaturePermanent(); this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); - Ability ability = new EnchantAbility(auraTarget.getTargetName()); - this.addAbility(ability); + this.addAbility(new EnchantAbility(auraTarget.getTargetName())); // Enchanted creature gets +1/+1 and has protection from enchantments. This effect doesn't remove Tattoo Ward. - Ability ability2 = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(1, 1, Duration.WhileOnBattlefield)); - ProtectionAbility protectionAbility = new ProtectionAbility(filter); - protectionAbility.setAuraIdNotToBeRemoved(getId()); - ability2.addEffect(new GainAbilityAttachedEffect(protectionAbility, AttachmentType.AURA, Duration.WhileOnBattlefield)); - this.addAbility(ability2); + Ability ability = new SimpleStaticAbility(new BoostEnchantedEffect( + 1, 1, Duration.WhileOnBattlefield + )); + ability.addEffect(new GainAbilityAttachedEffect( + new ProtectionAbility(StaticFilters.FILTER_PERMANENT_ENCHANTMENTS), + AttachmentType.AURA, Duration.WhileOnBattlefield + ).setDoesntRemoveItself(true)); + this.addAbility(ability); // Sacrifice Tattoo Ward: Destroy target enchantment. - Ability ability3 = new SimpleActivatedAbility(new DestroyTargetEffect(), new SacrificeSourceCost()); - ability3.addTarget(new TargetPermanent(StaticFilters.FILTER_ENCHANTMENT_PERMANENT)); - this.addAbility(ability3); + ability = new SimpleActivatedAbility(new DestroyTargetEffect(), new SacrificeSourceCost()); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_ENCHANTMENT)); + this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/t/TavernRuffian.java b/Mage.Sets/src/mage/cards/t/TavernRuffian.java index b06f875bd57..804e7551083 100644 --- a/Mage.Sets/src/mage/cards/t/TavernRuffian.java +++ b/Mage.Sets/src/mage/cards/t/TavernRuffian.java @@ -2,7 +2,6 @@ package mage.cards.t; import mage.MageInt; import mage.abilities.keyword.DayboundAbility; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -24,11 +23,9 @@ public final class TavernRuffian extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(5); - this.transformable = true; this.secondSideCardClazz = mage.cards.t.TavernSmasher.class; // Daybound - this.addAbility(new TransformAbility()); this.addAbility(new DayboundAbility()); } diff --git a/Mage.Sets/src/mage/cards/t/TavernSmasher.java b/Mage.Sets/src/mage/cards/t/TavernSmasher.java index 8a3b1258e57..23e61cd013e 100644 --- a/Mage.Sets/src/mage/cards/t/TavernSmasher.java +++ b/Mage.Sets/src/mage/cards/t/TavernSmasher.java @@ -22,7 +22,6 @@ public final class TavernSmasher extends CardImpl { this.color.setRed(true); this.nightCard = true; - this.transformable = true; this.power = new MageInt(6); this.toughness = new MageInt(5); diff --git a/Mage.Sets/src/mage/cards/t/TawnosUrzasApprentice.java b/Mage.Sets/src/mage/cards/t/TawnosUrzasApprentice.java index df79e5c7d40..0e3fe8681a8 100644 --- a/Mage.Sets/src/mage/cards/t/TawnosUrzasApprentice.java +++ b/Mage.Sets/src/mage/cards/t/TawnosUrzasApprentice.java @@ -33,7 +33,7 @@ public final class TawnosUrzasApprentice extends CardImpl { private static final FilterStackObject filter = new FilterStackObject("activated or triggered ability you control from an artifact source"); static { - filter.add(new ArtifactSourcePredicate()); + filter.add(ArtifactSourcePredicate.instance); filter.add(TargetController.YOU.getControllerPredicate()); } diff --git a/Mage.Sets/src/mage/cards/t/TawnossWeaponry.java b/Mage.Sets/src/mage/cards/t/TawnossWeaponry.java index b8cfaa12433..bf91e993053 100644 --- a/Mage.Sets/src/mage/cards/t/TawnossWeaponry.java +++ b/Mage.Sets/src/mage/cards/t/TawnossWeaponry.java @@ -30,7 +30,7 @@ public final class TawnossWeaponry extends CardImpl { this.addAbility(new SkipUntapOptionalAbility()); // {2}, {tap}: Target creature gets +1/+1 for as long as Tawnos's Weaponry remains tapped. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect( - new BoostTargetEffect(1, 1, Duration.Custom), SourceTappedCondition.instance, + new BoostTargetEffect(1, 1, Duration.Custom), SourceTappedCondition.TAPPED, "target creature gets +1/+1 for as long as {this} remains tapped"), new ManaCostsImpl("{2}")); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent()); diff --git a/Mage.Sets/src/mage/cards/t/TeachingsOfTheKirin.java b/Mage.Sets/src/mage/cards/t/TeachingsOfTheKirin.java new file mode 100644 index 00000000000..7b0d51758cf --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TeachingsOfTheKirin.java @@ -0,0 +1,62 @@ +package mage.cards.t; + +import java.util.UUID; + +import mage.abilities.common.SagaAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.ExileSagaAndReturnTransformedEffect; +import mage.abilities.effects.common.MillCardsControllerEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.TransformAbility; +import mage.constants.SagaChapter; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.counters.CounterType; +import mage.game.permanent.token.SpiritToken; +import mage.target.common.TargetControlledCreaturePermanent; + +/** + * + * @author weirddan455 + */ +public final class TeachingsOfTheKirin extends CardImpl { + + public TeachingsOfTheKirin(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{G}"); + + this.subtype.add(SubType.SAGA); + this.secondSideCardClazz = mage.cards.k.KirinTouchedOrochi.class; + + // (As this Saga enters and after your draw step, add a lore counter.) + SagaAbility sagaAbility = new SagaAbility(this); + + // I - Mill three cards. Create a 1/1 colorless Spirit creature token. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, + new MillCardsControllerEffect(3), + new CreateTokenEffect(new SpiritToken()) + ); + + // II — Put a +1/+1 counter on target creature you control. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_II, SagaChapter.CHAPTER_II, + new AddCountersTargetEffect(CounterType.P1P1.createInstance()), + new TargetControlledCreaturePermanent() + ); + + // III — Exile this Saga, then return it to the battlefield transformed under your control. + this.addAbility(new TransformAbility()); + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, new ExileSagaAndReturnTransformedEffect()); + + this.addAbility(sagaAbility); + } + + private TeachingsOfTheKirin(final TeachingsOfTheKirin card) { + super(card); + } + + @Override + public TeachingsOfTheKirin copy() { + return new TeachingsOfTheKirin(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TearsOfRage.java b/Mage.Sets/src/mage/cards/t/TearsOfRage.java index f1792dfbffa..d2a09ac4ccd 100644 --- a/Mage.Sets/src/mage/cards/t/TearsOfRage.java +++ b/Mage.Sets/src/mage/cards/t/TearsOfRage.java @@ -1,4 +1,3 @@ - package mage.cards.t; import java.util.UUID; @@ -17,6 +16,7 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.PhaseStep; +import mage.filter.StaticFilters; import mage.filter.common.FilterAttackingCreature; import mage.game.Game; import mage.players.Player; @@ -36,8 +36,7 @@ public final class TearsOfRage extends CardImpl { // Attacking creatures you control get +X/+0 until end of turn, where X is the number of attacking creatures. Sacrifice those creatures at the beginning of the next end step. BoostControlledEffect effect = new BoostControlledEffect(new AttackingCreatureCount("the number of attacking creatures"), StaticValue.get(0), - Duration.EndOfTurn, new FilterAttackingCreature("Attacking creatures"), false); - effect.setLockedIn(true); + Duration.EndOfTurn, StaticFilters.FILTER_ATTACKING_CREATURES, false, true); getSpellAbility().addEffect(effect); getSpellAbility().addEffect(new TearsOfRageEffect()); } diff --git a/Mage.Sets/src/mage/cards/t/TeferiHeroOfDominaria.java b/Mage.Sets/src/mage/cards/t/TeferiHeroOfDominaria.java index 76918d6299d..73116eaebf4 100644 --- a/Mage.Sets/src/mage/cards/t/TeferiHeroOfDominaria.java +++ b/Mage.Sets/src/mage/cards/t/TeferiHeroOfDominaria.java @@ -4,7 +4,6 @@ package mage.cards.t; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; @@ -36,7 +35,7 @@ public final class TeferiHeroOfDominaria extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.TEFERI); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Draw a card. At the beginning of the next end step, untap up to two lands. LoyaltyAbility ability = new LoyaltyAbility(new DrawCardSourceControllerEffect(1), 1); diff --git a/Mage.Sets/src/mage/cards/t/TeferiMasterOfTime.java b/Mage.Sets/src/mage/cards/t/TeferiMasterOfTime.java index 3ba2279cb2e..1cef3aac957 100644 --- a/Mage.Sets/src/mage/cards/t/TeferiMasterOfTime.java +++ b/Mage.Sets/src/mage/cards/t/TeferiMasterOfTime.java @@ -2,7 +2,6 @@ package mage.cards.t; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.OneShotEffect; @@ -28,7 +27,7 @@ public final class TeferiMasterOfTime extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.TEFERI); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // You may activate loyalty abilities of Teferi, Master of Time on any player's turn any time you could cast an instant. this.addAbility(new SimpleStaticAbility(new TeferiMasterOfTimeActivationEffect())); @@ -58,7 +57,7 @@ public final class TeferiMasterOfTime extends CardImpl { class TeferiMasterOfTimeActivationEffect extends AsThoughEffectImpl { TeferiMasterOfTimeActivationEffect() { - super(AsThoughEffectType.ACTIVATE_AS_INSTANT, Duration.EndOfGame, Outcome.Benefit); + super(AsThoughEffectType.ACTIVATE_AS_INSTANT, Duration.WhileOnBattlefield, Outcome.Benefit); staticText = "You may activate loyalty abilities of {this} " + "on any player's turn any time you could cast an instant"; } diff --git a/Mage.Sets/src/mage/cards/t/TeferiTemporalArchmage.java b/Mage.Sets/src/mage/cards/t/TeferiTemporalArchmage.java index bb40bd0acac..d8b152d4d62 100644 --- a/Mage.Sets/src/mage/cards/t/TeferiTemporalArchmage.java +++ b/Mage.Sets/src/mage/cards/t/TeferiTemporalArchmage.java @@ -4,7 +4,6 @@ package mage.cards.t; import java.util.UUID; import mage.abilities.LoyaltyAbility; import mage.abilities.common.CanBeYourCommanderAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.common.GetEmblemEffect; import mage.abilities.effects.common.LookLibraryAndPickControllerEffect; @@ -31,7 +30,7 @@ public final class TeferiTemporalArchmage extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.TEFERI); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +1: Look at the top two cards of your library. Put one of them into your hand and the other on the bottom of your library. this.addAbility(new LoyaltyAbility(new LookLibraryAndPickControllerEffect( diff --git a/Mage.Sets/src/mage/cards/t/TeferiTimeRaveler.java b/Mage.Sets/src/mage/cards/t/TeferiTimeRaveler.java index 3cfed14c3fd..93559f22dfd 100644 --- a/Mage.Sets/src/mage/cards/t/TeferiTimeRaveler.java +++ b/Mage.Sets/src/mage/cards/t/TeferiTimeRaveler.java @@ -3,7 +3,6 @@ package mage.cards.t; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; import mage.abilities.effects.common.DrawCardSourceControllerEffect; @@ -44,7 +43,7 @@ public final class TeferiTimeRaveler extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.TEFERI); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // Each opponent can cast spells only any time they could cast a sorcery. this.addAbility(new SimpleStaticAbility(new TeferiTimeRavelerReplacementEffect())); diff --git a/Mage.Sets/src/mage/cards/t/TeferiTimebender.java b/Mage.Sets/src/mage/cards/t/TeferiTimebender.java index be09d8f4cf8..542925bb874 100644 --- a/Mage.Sets/src/mage/cards/t/TeferiTimebender.java +++ b/Mage.Sets/src/mage/cards/t/TeferiTimebender.java @@ -3,7 +3,6 @@ package mage.cards.t; import java.util.UUID; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.UntapTargetEffect; @@ -28,7 +27,7 @@ public final class TeferiTimebender extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.TEFERI); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +2: Untap up to one target artifact or creature. FilterPermanent filter = new FilterPermanent("artifact or creature"); diff --git a/Mage.Sets/src/mage/cards/t/TeferiTimelessVoyager.java b/Mage.Sets/src/mage/cards/t/TeferiTimelessVoyager.java index ec3ab59fc17..3e6d1e2e98a 100644 --- a/Mage.Sets/src/mage/cards/t/TeferiTimelessVoyager.java +++ b/Mage.Sets/src/mage/cards/t/TeferiTimelessVoyager.java @@ -3,7 +3,6 @@ package mage.cards.t; import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; @@ -30,7 +29,7 @@ public final class TeferiTimelessVoyager extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.TEFERI); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Draw a card. this.addAbility(new LoyaltyAbility(new DrawCardSourceControllerEffect(1), 1)); diff --git a/Mage.Sets/src/mage/cards/t/TeferiWhoSlowsTheSunset.java b/Mage.Sets/src/mage/cards/t/TeferiWhoSlowsTheSunset.java index 995228baba4..faa9180f894 100644 --- a/Mage.Sets/src/mage/cards/t/TeferiWhoSlowsTheSunset.java +++ b/Mage.Sets/src/mage/cards/t/TeferiWhoSlowsTheSunset.java @@ -2,7 +2,6 @@ package mage.cards.t; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.GainLifeEffect; @@ -35,14 +34,14 @@ public final class TeferiWhoSlowsTheSunset extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.TEFERI); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Choose up to one target artifact, up to one target creature, and up to one target land. Untap the chosen permanents you control. Tap the chosen permanents you don't control. You gain 2 life. Ability ability = new LoyaltyAbility(new TeferiWhoSlowsTheSunsetEffect(), 1); ability.addEffect(new GainLifeEffect(2)); - ability.addTarget(new TargetArtifactPermanent()); - ability.addTarget(new TargetCreaturePermanent()); - ability.addTarget(new TargetLandPermanent()); + ability.addTarget(new TargetArtifactPermanent(0, 1)); + ability.addTarget(new TargetCreaturePermanent(0, 1)); + ability.addTarget(new TargetLandPermanent(0, 1)); this.addAbility(ability); // −2: Look at the top three cards of your library. Put one of them into your hand and the rest on the bottom of your library in any order. diff --git a/Mage.Sets/src/mage/cards/t/TeferisPuzzleBox.java b/Mage.Sets/src/mage/cards/t/TeferisPuzzleBox.java index 0031ef90fdf..e22774bea9e 100644 --- a/Mage.Sets/src/mage/cards/t/TeferisPuzzleBox.java +++ b/Mage.Sets/src/mage/cards/t/TeferisPuzzleBox.java @@ -42,7 +42,7 @@ class TeferisPuzzleBoxEffect extends OneShotEffect { public TeferisPuzzleBoxEffect() { super(Outcome.Neutral); - staticText = "At the beginning of each player's draw step, that player puts the cards in their hand on the bottom of their library in any order, then draws that many cards"; + staticText = "that player puts the cards in their hand on the bottom of their library in any order, then draws that many cards"; } public TeferisPuzzleBoxEffect(final TeferisPuzzleBoxEffect effect) { diff --git a/Mage.Sets/src/mage/cards/t/Teleportal.java b/Mage.Sets/src/mage/cards/t/Teleportal.java index 8e7baf24825..1392bd60a63 100644 --- a/Mage.Sets/src/mage/cards/t/Teleportal.java +++ b/Mage.Sets/src/mage/cards/t/Teleportal.java @@ -21,7 +21,6 @@ import mage.target.targetpointer.FixedTarget; import java.util.UUID; - /** * @author LevelX2 */ @@ -36,7 +35,6 @@ public final class Teleportal extends CardImpl { public Teleportal(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{U}{R}"); - // Target creature you control gets +1/+0 until end of turn and can't be blocked this turn. this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); this.getSpellAbility().addEffect(new BoostTargetEffect(1, 0, Duration.EndOfTurn)); @@ -77,7 +75,7 @@ class TeleportalEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { for (Permanent creature : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { CantBeBlockedTargetEffect effect = new CantBeBlockedTargetEffect(); - effect.setTargetPointer(new FixedTarget(creature.getId())); + effect.setTargetPointer(new FixedTarget(creature.getId(), game)); game.addEffect(effect, source); } return true; diff --git a/Mage.Sets/src/mage/cards/t/TellerOfTales.java b/Mage.Sets/src/mage/cards/t/TellerOfTales.java index fa7214a0b60..52705de22de 100644 --- a/Mage.Sets/src/mage/cards/t/TellerOfTales.java +++ b/Mage.Sets/src/mage/cards/t/TellerOfTales.java @@ -59,7 +59,7 @@ public final class TellerOfTales extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); // Whenever you cast a Spirit or Arcane spell, you may tap or untap target creature. - Ability ability = new SpellCastControllerTriggeredAbility(new MayTapOrUntapTargetEffect(), StaticFilters.SPIRIT_OR_ARCANE_CARD, true); + Ability ability = new SpellCastControllerTriggeredAbility(new MayTapOrUntapTargetEffect(), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/t/TemperedInSolitude.java b/Mage.Sets/src/mage/cards/t/TemperedInSolitude.java new file mode 100644 index 00000000000..513b20524df --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TemperedInSolitude.java @@ -0,0 +1,31 @@ +package mage.cards.t; + +import mage.abilities.common.AttacksAloneControlledTriggeredAbility; +import mage.abilities.effects.common.ExileTopXMayPlayUntilEndOfTurnEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TemperedInSolitude extends CardImpl { + + public TemperedInSolitude(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{R}"); + + // Whenever a creature you control attacks alone, exile the top card of your library. You may play that card this turn. + this.addAbility(new AttacksAloneControlledTriggeredAbility(new ExileTopXMayPlayUntilEndOfTurnEffect(1))); + } + + private TemperedInSolitude(final TemperedInSolitude card) { + super(card); + } + + @Override + public TemperedInSolitude copy() { + return new TemperedInSolitude(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TemperedSliver.java b/Mage.Sets/src/mage/cards/t/TemperedSliver.java index 6de875e1a7f..e7a8f3e7ea1 100644 --- a/Mage.Sets/src/mage/cards/t/TemperedSliver.java +++ b/Mage.Sets/src/mage/cards/t/TemperedSliver.java @@ -31,7 +31,7 @@ public final class TemperedSliver extends CardImpl { this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( new DealsCombatDamageToAPlayerTriggeredAbility( new AddCountersSourceEffect(CounterType.P1P1.createInstance(1)), false,"Whenever this creature deals combat damage to a player, put a +1/+1 counter on it.",false - ), Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS + ), Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_SLIVERS ))); } diff --git a/Mage.Sets/src/mage/cards/t/TemperedVeteran.java b/Mage.Sets/src/mage/cards/t/TemperedVeteran.java index da1dada4b38..f4b6b68f812 100644 --- a/Mage.Sets/src/mage/cards/t/TemperedVeteran.java +++ b/Mage.Sets/src/mage/cards/t/TemperedVeteran.java @@ -11,8 +11,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.counters.CounterType; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; @@ -23,13 +22,6 @@ import java.util.UUID; */ public final class TemperedVeteran extends CardImpl { - private static final FilterPermanent filter - = new FilterCreaturePermanent("creature with a +1/+1 counter on it"); - - static { - filter.add(CounterType.P1P1.getPredicate()); - } - public TemperedVeteran(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); @@ -43,7 +35,7 @@ public final class TemperedVeteran extends CardImpl { new AddCountersTargetEffect(CounterType.P1P1.createInstance()), new ManaCostsImpl("{W}") ); ability.addCost(new TapSourceCost()); - ability.addTarget(new TargetPermanent(filter)); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_P1P1)); this.addAbility(ability); // {4}{W}{W}, {T}: Put a +1/+1 counter on target creature. diff --git a/Mage.Sets/src/mage/cards/t/TempleOfAbandon.java b/Mage.Sets/src/mage/cards/t/TempleOfAbandon.java index e8dcac3beac..d42db246a13 100644 --- a/Mage.Sets/src/mage/cards/t/TempleOfAbandon.java +++ b/Mage.Sets/src/mage/cards/t/TempleOfAbandon.java @@ -23,7 +23,7 @@ public final class TempleOfAbandon extends CardImpl { // Temple of Abandon enters the battlefield tapped. this.addAbility(new EntersBattlefieldTappedAbility()); // When Temple of Abandon enters the battlefield, scry 1.
- this.addAbility(new EntersBattlefieldTriggeredAbility(new ScryEffect(1))); + this.addAbility(new EntersBattlefieldTriggeredAbility(new ScryEffect(1, false))); // {T}: Add {R} or {G}. this.addAbility(new RedManaAbility()); this.addAbility(new GreenManaAbility()); diff --git a/Mage.Sets/src/mage/cards/t/TempleOfDeceit.java b/Mage.Sets/src/mage/cards/t/TempleOfDeceit.java index 0f13379c6d4..efdb5969d6d 100644 --- a/Mage.Sets/src/mage/cards/t/TempleOfDeceit.java +++ b/Mage.Sets/src/mage/cards/t/TempleOfDeceit.java @@ -23,7 +23,7 @@ public final class TempleOfDeceit extends CardImpl { // Temple of Deceit enters the battlefield tapped. this.addAbility(new EntersBattlefieldTappedAbility()); // When Temple of Deceit enters the battlefield, scry 1. - this.addAbility(new EntersBattlefieldTriggeredAbility(new ScryEffect(1))); + this.addAbility(new EntersBattlefieldTriggeredAbility(new ScryEffect(1, false))); // {T}: Add {U} or {B}. this.addAbility(new BlueManaAbility()); this.addAbility(new BlackManaAbility()); diff --git a/Mage.Sets/src/mage/cards/t/TempleOfEnlightenment.java b/Mage.Sets/src/mage/cards/t/TempleOfEnlightenment.java index 5ba69637856..5dc279be1c0 100644 --- a/Mage.Sets/src/mage/cards/t/TempleOfEnlightenment.java +++ b/Mage.Sets/src/mage/cards/t/TempleOfEnlightenment.java @@ -23,7 +23,7 @@ public final class TempleOfEnlightenment extends CardImpl { // Temple of Enlightenment enters the battlefield tapped. this.addAbility(new EntersBattlefieldTappedAbility()); // When Temple of Enlightenment enters the battlefield, scry 1. - this.addAbility(new EntersBattlefieldTriggeredAbility(new ScryEffect(1))); + this.addAbility(new EntersBattlefieldTriggeredAbility(new ScryEffect(1, false))); // {T}: Add {W} or {U}. this.addAbility(new WhiteManaAbility()); this.addAbility(new BlueManaAbility()); diff --git a/Mage.Sets/src/mage/cards/t/TempleOfEpiphany.java b/Mage.Sets/src/mage/cards/t/TempleOfEpiphany.java index 313d83dd87a..f157a9314a2 100644 --- a/Mage.Sets/src/mage/cards/t/TempleOfEpiphany.java +++ b/Mage.Sets/src/mage/cards/t/TempleOfEpiphany.java @@ -23,7 +23,7 @@ public final class TempleOfEpiphany extends CardImpl { // Temple of Epiphany enters the battlefield tapped. this.addAbility(new EntersBattlefieldTappedAbility()); // When Temple of Epiphany enters the battlefield, scry 1. - this.addAbility(new EntersBattlefieldTriggeredAbility(new ScryEffect(1))); + this.addAbility(new EntersBattlefieldTriggeredAbility(new ScryEffect(1, false))); // {T}: Add {U} or {R}. this.addAbility(new BlueManaAbility()); this.addAbility(new RedManaAbility()); diff --git a/Mage.Sets/src/mage/cards/t/TempleOfMalady.java b/Mage.Sets/src/mage/cards/t/TempleOfMalady.java index 5d3ce42cafa..7800a9deafe 100644 --- a/Mage.Sets/src/mage/cards/t/TempleOfMalady.java +++ b/Mage.Sets/src/mage/cards/t/TempleOfMalady.java @@ -23,7 +23,7 @@ public final class TempleOfMalady extends CardImpl { // Temple of Malady enters the battlefield tapped. this.addAbility(new EntersBattlefieldTappedAbility()); // When Temple of Malady enters the battlefield, scry 1. - this.addAbility(new EntersBattlefieldTriggeredAbility(new ScryEffect(1))); + this.addAbility(new EntersBattlefieldTriggeredAbility(new ScryEffect(1, false))); // {T}: Add {B} or {G}. this.addAbility(new BlackManaAbility()); this.addAbility(new GreenManaAbility()); diff --git a/Mage.Sets/src/mage/cards/t/TempleOfMalice.java b/Mage.Sets/src/mage/cards/t/TempleOfMalice.java index db0a31ec18a..30c316218d6 100644 --- a/Mage.Sets/src/mage/cards/t/TempleOfMalice.java +++ b/Mage.Sets/src/mage/cards/t/TempleOfMalice.java @@ -23,7 +23,7 @@ public final class TempleOfMalice extends CardImpl { // Temple of Malice enters the battlefield tapped. this.addAbility(new EntersBattlefieldTappedAbility()); // When Temple of Malice enters the battlefield, scry 1. - this.addAbility(new EntersBattlefieldTriggeredAbility(new ScryEffect(1))); + this.addAbility(new EntersBattlefieldTriggeredAbility(new ScryEffect(1, false))); // {T}: Add {B} or {R}. this.addAbility(new BlackManaAbility()); this.addAbility(new RedManaAbility()); diff --git a/Mage.Sets/src/mage/cards/t/TempleOfMystery.java b/Mage.Sets/src/mage/cards/t/TempleOfMystery.java index 4ce7771992a..e89cdef5b54 100644 --- a/Mage.Sets/src/mage/cards/t/TempleOfMystery.java +++ b/Mage.Sets/src/mage/cards/t/TempleOfMystery.java @@ -23,7 +23,7 @@ public final class TempleOfMystery extends CardImpl { // Temple of Mystery enters the battlefield tapped. this.addAbility(new EntersBattlefieldTappedAbility()); // When Temple of Mystery enters the battlefield, scry 1. - this.addAbility(new EntersBattlefieldTriggeredAbility(new ScryEffect(1))); + this.addAbility(new EntersBattlefieldTriggeredAbility(new ScryEffect(1, false))); // {T}: Add {G} or {U}. this.addAbility(new GreenManaAbility()); this.addAbility(new BlueManaAbility()); diff --git a/Mage.Sets/src/mage/cards/t/TempleOfPlenty.java b/Mage.Sets/src/mage/cards/t/TempleOfPlenty.java index 404c5138133..13a15efcb20 100644 --- a/Mage.Sets/src/mage/cards/t/TempleOfPlenty.java +++ b/Mage.Sets/src/mage/cards/t/TempleOfPlenty.java @@ -23,7 +23,7 @@ public final class TempleOfPlenty extends CardImpl { // Temple of Plenty enters the battlefield tapped. this.addAbility(new EntersBattlefieldTappedAbility()); // When Temple of Plenty enters the battlefield, scry 1. - this.addAbility(new EntersBattlefieldTriggeredAbility(new ScryEffect(1))); + this.addAbility(new EntersBattlefieldTriggeredAbility(new ScryEffect(1, false))); // {T}: Add {G} or {W}. this.addAbility(new GreenManaAbility()); this.addAbility(new WhiteManaAbility()); diff --git a/Mage.Sets/src/mage/cards/t/TempleOfSilence.java b/Mage.Sets/src/mage/cards/t/TempleOfSilence.java index dc3c981652f..ee4b7cee7d3 100644 --- a/Mage.Sets/src/mage/cards/t/TempleOfSilence.java +++ b/Mage.Sets/src/mage/cards/t/TempleOfSilence.java @@ -23,7 +23,7 @@ public final class TempleOfSilence extends CardImpl { // Temple of Silence enters the battlefield tapped. this.addAbility(new EntersBattlefieldTappedAbility()); // When Temple of Silence enters the battlefield, scry 1. - this.addAbility(new EntersBattlefieldTriggeredAbility(new ScryEffect(1))); + this.addAbility(new EntersBattlefieldTriggeredAbility(new ScryEffect(1, false))); // {T}: Add {W} or {B}. this.addAbility(new WhiteManaAbility()); this.addAbility(new BlackManaAbility()); diff --git a/Mage.Sets/src/mage/cards/t/TempleOfTriumph.java b/Mage.Sets/src/mage/cards/t/TempleOfTriumph.java index cfe86608ed4..aac691cb4ff 100644 --- a/Mage.Sets/src/mage/cards/t/TempleOfTriumph.java +++ b/Mage.Sets/src/mage/cards/t/TempleOfTriumph.java @@ -23,7 +23,7 @@ public final class TempleOfTriumph extends CardImpl { // Temple of Triumph enters the battlefield tapped. this.addAbility(new EntersBattlefieldTappedAbility()); // When Temple of Triumph enters the battlefield, scry 1. - this.addAbility(new EntersBattlefieldTriggeredAbility(new ScryEffect(1))); + this.addAbility(new EntersBattlefieldTriggeredAbility(new ScryEffect(1, false))); // {T}: Add {R} or {W}. this.addAbility(new RedManaAbility()); this.addAbility(new WhiteManaAbility()); diff --git a/Mage.Sets/src/mage/cards/t/TenaciousHunter.java b/Mage.Sets/src/mage/cards/t/TenaciousHunter.java index b6aec6dc1df..bf1e7677678 100644 --- a/Mage.Sets/src/mage/cards/t/TenaciousHunter.java +++ b/Mage.Sets/src/mage/cards/t/TenaciousHunter.java @@ -41,11 +41,11 @@ public final class TenaciousHunter extends CardImpl { // As long as a creature has a -1/-1 counter on it, Tenacious Hunter has vigilance and deathtouch. Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(new GainAbilitySourceEffect(VigilanceAbility.getInstance()), - new PermanentsOnTheBattlefieldCondition(filter, ComparisonType.MORE_THAN, 0, false), + new PermanentsOnTheBattlefieldCondition(filter, false), "As long as a creature has a -1/-1 counter on it, {this} has vigilance")); ability.addEffect(new ConditionalContinuousEffect(new GainAbilitySourceEffect(DeathtouchAbility.getInstance()), - new PermanentsOnTheBattlefieldCondition(filter, ComparisonType.MORE_THAN, 0, false), + new PermanentsOnTheBattlefieldCondition(filter, false), "and deathtouch")); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/t/TenthDistrictLegionnaire.java b/Mage.Sets/src/mage/cards/t/TenthDistrictLegionnaire.java index b4e52204603..22ac220bb93 100644 --- a/Mage.Sets/src/mage/cards/t/TenthDistrictLegionnaire.java +++ b/Mage.Sets/src/mage/cards/t/TenthDistrictLegionnaire.java @@ -34,7 +34,7 @@ public final class TenthDistrictLegionnaire extends CardImpl { Ability ability = new HeroicAbility(new AddCountersSourceEffect( CounterType.P1P1.createInstance() ), false, false); - ability.addEffect(new ScryEffect(1).concatBy(", then")); + ability.addEffect(new ScryEffect(1, false).concatBy(", then")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/t/Tephraderm.java b/Mage.Sets/src/mage/cards/t/Tephraderm.java index bfd2a109efd..f9b06856ca3 100644 --- a/Mage.Sets/src/mage/cards/t/Tephraderm.java +++ b/Mage.Sets/src/mage/cards/t/Tephraderm.java @@ -1,4 +1,3 @@ - package mage.cards.t; import java.util.UUID; @@ -28,7 +27,7 @@ import mage.target.targetpointer.FixedTarget; public final class Tephraderm extends CardImpl { public Tephraderm(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}"); this.subtype.add(SubType.BEAST); this.power = new MageInt(4); this.toughness = new MageInt(5); @@ -74,10 +73,11 @@ class TephradermCreatureDamageTriggeredAbility extends TriggeredAbilityImpl { } Permanent sourcePermanent = game.getPermanent(event.getSourceId()); - if (sourcePermanent != null && FILTER_CREATURE.match(sourcePermanent, getSourceId(), getControllerId(), game)) { + if (sourcePermanent != null + && FILTER_CREATURE.match(sourcePermanent, getSourceId(), getControllerId(), game)) { for (Effect effect : getEffects()) { if (effect instanceof DamageTargetEffect) { - effect.setTargetPointer(new FixedTarget(sourcePermanent.getId())); + effect.setTargetPointer(new FixedTarget(sourcePermanent.getId(), game)); ((DamageTargetEffect) effect).setAmount(StaticValue.get(event.getAmount())); } } diff --git a/Mage.Sets/src/mage/cards/t/TergridGodOfFright.java b/Mage.Sets/src/mage/cards/t/TergridGodOfFright.java index 9a8c1d07d51..c6f5de8ffc7 100644 --- a/Mage.Sets/src/mage/cards/t/TergridGodOfFright.java +++ b/Mage.Sets/src/mage/cards/t/TergridGodOfFright.java @@ -46,7 +46,7 @@ public final class TergridGodOfFright extends ModalDoubleFacesCard { this.getLeftHalfCard().setPT(4, 5); // Menace - this.getLeftHalfCard().addAbility(new MenaceAbility()); + this.getLeftHalfCard().addAbility(new MenaceAbility(false)); // Whenever an opponent sacrifices a nontoken permanent or discards a permanent card, you may put that card onto the battlefield under your control from their graveyard. this.getLeftHalfCard().addAbility(new TergridGodOfFrightTriggeredAbility()); diff --git a/Mage.Sets/src/mage/cards/t/TerritorialHammerskull.java b/Mage.Sets/src/mage/cards/t/TerritorialHammerskull.java index a01c90d49fa..f03aa39f640 100644 --- a/Mage.Sets/src/mage/cards/t/TerritorialHammerskull.java +++ b/Mage.Sets/src/mage/cards/t/TerritorialHammerskull.java @@ -10,8 +10,7 @@ import mage.constants.SubType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; /** @@ -20,12 +19,6 @@ import mage.target.common.TargetCreaturePermanent; */ public final class TerritorialHammerskull extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public TerritorialHammerskull(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); @@ -35,7 +28,7 @@ public final class TerritorialHammerskull extends CardImpl { // Whenever Territorial Hammerskull attacks, tap target creature an opponent controls. Ability ability = new AttacksTriggeredAbility(new TapTargetEffect(), false); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/t/TerrorOfKruinPass.java b/Mage.Sets/src/mage/cards/t/TerrorOfKruinPass.java index 7e3cb3b8a88..987091f32de 100644 --- a/Mage.Sets/src/mage/cards/t/TerrorOfKruinPass.java +++ b/Mage.Sets/src/mage/cards/t/TerrorOfKruinPass.java @@ -28,7 +28,6 @@ public final class TerrorOfKruinPass extends CardImpl { // this card is the second face of double-faced card this.nightCard = true; - this.transformable = true; this.color.setRed(true); this.power = new MageInt(3); @@ -38,7 +37,7 @@ public final class TerrorOfKruinPass extends CardImpl { // Werewolves you control have menace. (They can't be blocked except by two or more creatures.) this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( - new MenaceAbility(false), Duration.WhileOnBattlefield, filter, false + new MenaceAbility(), Duration.WhileOnBattlefield, filter, false ))); // At the beginning of each upkeep, if a player cast two or more spells last turn, transform Terror of Kruin Pass. diff --git a/Mage.Sets/src/mage/cards/t/TetheredGriffin.java b/Mage.Sets/src/mage/cards/t/TetheredGriffin.java index 5e20b667172..d00fb61b4d8 100644 --- a/Mage.Sets/src/mage/cards/t/TetheredGriffin.java +++ b/Mage.Sets/src/mage/cards/t/TetheredGriffin.java @@ -31,7 +31,7 @@ public final class TetheredGriffin extends CardImpl { // When you control no enchantments, sacrifice Tethered Griffin. this.addAbility(new ControlsPermanentsControllerTriggeredAbility( - StaticFilters.FILTER_ENCHANTMENT_PERMANENT, ComparisonType.EQUAL_TO, 0, + StaticFilters.FILTER_PERMANENT_ENCHANTMENT, ComparisonType.EQUAL_TO, 0, new SacrificeSourceEffect())); } diff --git a/Mage.Sets/src/mage/cards/t/TeveshSzatDoomOfFools.java b/Mage.Sets/src/mage/cards/t/TeveshSzatDoomOfFools.java index 67a9ae5eb04..6069af8cf4a 100644 --- a/Mage.Sets/src/mage/cards/t/TeveshSzatDoomOfFools.java +++ b/Mage.Sets/src/mage/cards/t/TeveshSzatDoomOfFools.java @@ -3,7 +3,6 @@ package mage.cards.t; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.common.CanBeYourCommanderAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.continuous.GainControlTargetEffect; @@ -41,7 +40,7 @@ public final class TeveshSzatDoomOfFools extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.SZAT); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +2: Create two 0/1 black Thrull creature tokens. this.addAbility(new LoyaltyAbility(new CreateTokenEffect(new BreedingPitThrullToken(), 2), 2)); diff --git a/Mage.Sets/src/mage/cards/t/TeyoTheShieldmage.java b/Mage.Sets/src/mage/cards/t/TeyoTheShieldmage.java index 6a862561612..88173d0b7f6 100644 --- a/Mage.Sets/src/mage/cards/t/TeyoTheShieldmage.java +++ b/Mage.Sets/src/mage/cards/t/TeyoTheShieldmage.java @@ -1,7 +1,6 @@ package mage.cards.t; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.continuous.GainAbilityControllerEffect; @@ -25,7 +24,7 @@ public final class TeyoTheShieldmage extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.TEYO); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // You have hexproof. this.addAbility(new SimpleStaticAbility(new GainAbilityControllerEffect(HexproofAbility.getInstance()))); diff --git a/Mage.Sets/src/mage/cards/t/TezzeretAgentOfBolas.java b/Mage.Sets/src/mage/cards/t/TezzeretAgentOfBolas.java index 76c27ebc5e1..3efbb5d90a3 100644 --- a/Mage.Sets/src/mage/cards/t/TezzeretAgentOfBolas.java +++ b/Mage.Sets/src/mage/cards/t/TezzeretAgentOfBolas.java @@ -4,7 +4,6 @@ package mage.cards.t; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; @@ -42,7 +41,7 @@ public final class TezzeretAgentOfBolas extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.TEZZERET); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +1: Look at the top five cards of your library. You may reveal an artifact card from among them and put it into your hand. Put the rest on the bottom of your library in any order. this.addAbility(new LoyaltyAbility(new LookLibraryAndPickControllerEffect(5, 1, filter, true), 1)); diff --git a/Mage.Sets/src/mage/cards/t/TezzeretArtificeMaster.java b/Mage.Sets/src/mage/cards/t/TezzeretArtificeMaster.java index 722a9103fd7..d84a8477d72 100644 --- a/Mage.Sets/src/mage/cards/t/TezzeretArtificeMaster.java +++ b/Mage.Sets/src/mage/cards/t/TezzeretArtificeMaster.java @@ -1,7 +1,6 @@ package mage.cards.t; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.condition.common.MetalcraftCondition; import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; @@ -29,7 +28,7 @@ public final class TezzeretArtificeMaster extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.TEZZERET); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +1: Create a colorless Thopter artifact creature token with flying. this.addAbility(new LoyaltyAbility(new CreateTokenEffect(new ThopterColorlessToken()), 1)); diff --git a/Mage.Sets/src/mage/cards/t/TezzeretBetrayerOfFlesh.java b/Mage.Sets/src/mage/cards/t/TezzeretBetrayerOfFlesh.java new file mode 100644 index 00000000000..6279e949f5e --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TezzeretBetrayerOfFlesh.java @@ -0,0 +1,196 @@ +package mage.cards.t; + +import mage.abilities.Ability; +import mage.abilities.ActivatedAbility; +import mage.abilities.LoyaltyAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.GetEmblemEffect; +import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.effects.common.discard.DiscardControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.command.emblems.TezzeretBetrayerOfFleshEmblem; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.common.TargetArtifactPermanent; +import mage.util.CardUtil; +import mage.watchers.Watcher; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TezzeretBetrayerOfFlesh extends CardImpl { + + public TezzeretBetrayerOfFlesh(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{2}{U}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.TEZZERET); + this.setStartingLoyalty(4); + + // The first activated ability of an artifact you activate each turn costs {2} less to activate. + this.addAbility(new SimpleStaticAbility( + new TezzeretBetrayerOfFleshReductionEffect() + ), new TezzeretBetrayerOfFleshWatcher()); + + // +1: Draw two cards. Then discard two cards unless you discard an artifact card. + Ability ability = new LoyaltyAbility(new DrawCardSourceControllerEffect(2), 1); + ability.addEffect(new DoIfCostPaid( + null, new DiscardControllerEffect(2), + new DiscardCardCost(StaticFilters.FILTER_CARD_ARTIFACT_AN) + .setText("discard an artifact card instead of discarding two cards") + ).setText("Then discard two cards unless you discard an artifact card")); + this.addAbility(ability); + + // −2: Target artifact becomes an artifact creature. If it isn't a Vehicle, it has base power and toughness 4/4. + ability = new LoyaltyAbility(new TezzeretBetrayerOfFleshTypeEffect(), -2); + ability.addTarget(new TargetArtifactPermanent()); + this.addAbility(ability); + + // −6: You get an emblem with "Whenever an artifact you control becomes tapped, draw a card." + this.addAbility(new LoyaltyAbility(new GetEmblemEffect(new TezzeretBetrayerOfFleshEmblem()), -6)); + } + + private TezzeretBetrayerOfFlesh(final TezzeretBetrayerOfFlesh card) { + super(card); + } + + @Override + public TezzeretBetrayerOfFlesh copy() { + return new TezzeretBetrayerOfFlesh(this); + } +} + +class TezzeretBetrayerOfFleshReductionEffect extends CostModificationEffectImpl { + + TezzeretBetrayerOfFleshReductionEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.REDUCE_COST); + staticText = "the first activated ability of an artifact you activate each turn costs {2} less to activate"; + } + + private TezzeretBetrayerOfFleshReductionEffect(final TezzeretBetrayerOfFleshReductionEffect effect) { + super(effect); + } + + @Override + public TezzeretBetrayerOfFleshReductionEffect copy() { + return new TezzeretBetrayerOfFleshReductionEffect(this); + } + + @Override + public boolean apply(Game game, Ability source, Ability abilityToModify) { + CardUtil.reduceCost(abilityToModify, 2); + return true; + } + + @Override + public boolean applies(Ability abilityToModify, Ability source, Game game) { + return abilityToModify.isControlledBy(source.getControllerId()) + && abilityToModify instanceof ActivatedAbility + && TezzeretBetrayerOfFleshWatcher.checkPlayer(game, abilityToModify); + } +} + +class TezzeretBetrayerOfFleshWatcher extends Watcher { + + private final Set playerSet = new HashSet<>(); + + TezzeretBetrayerOfFleshWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() != GameEvent.EventType.ACTIVATED_ABILITY) { + return; + } + Permanent permanent = game.getPermanent(event.getSourceId()); + if (permanent != null && permanent.isArtifact(game)) { + playerSet.add(event.getPlayerId()); + } + } + + @Override + public void reset() { + super.reset(); + playerSet.clear(); + } + + public static boolean checkPlayer(Game game, Ability ability) { + if (game.getState() + .getWatcher(TezzeretBetrayerOfFleshWatcher.class) + .playerSet + .contains(ability.getControllerId())) { + return false; + } + Permanent permanent = ability.getSourcePermanentOrLKI(game); + return permanent != null && permanent.isArtifact(game); + } +} + +class TezzeretBetrayerOfFleshTypeEffect extends ContinuousEffectImpl { + + TezzeretBetrayerOfFleshTypeEffect() { + super(Duration.Custom, Outcome.BecomeCreature); + staticText = "target artifact becomes an artifact creature. " + + "If it isn't a Vehicle, it has base power and toughness 4/4"; + } + + private TezzeretBetrayerOfFleshTypeEffect(final TezzeretBetrayerOfFleshTypeEffect effect) { + super(effect); + } + + @Override + public TezzeretBetrayerOfFleshTypeEffect copy() { + return new TezzeretBetrayerOfFleshTypeEffect(this); + } + + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (permanent == null) { + discard(); + return false; + } + switch (layer) { + case TypeChangingEffects_4: + permanent.addCardType(game, CardType.ARTIFACT, CardType.CREATURE); + return true; + case PTChangingEffects_7: + if (sublayer != SubLayer.SetPT_7b + || permanent.hasSubtype(SubType.VEHICLE, game)) { + return false; + } + permanent.getPower().setValue(4); + permanent.getToughness().setValue(4); + return true; + } + return false; + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean hasLayer(Layer layer) { + switch (layer) { + case TypeChangingEffects_4: + case PTChangingEffects_7: + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/t/TezzeretCruelMachinist.java b/Mage.Sets/src/mage/cards/t/TezzeretCruelMachinist.java index a8ffc254347..2bd0f763736 100644 --- a/Mage.Sets/src/mage/cards/t/TezzeretCruelMachinist.java +++ b/Mage.Sets/src/mage/cards/t/TezzeretCruelMachinist.java @@ -3,7 +3,6 @@ package mage.cards.t; import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; @@ -36,7 +35,7 @@ public final class TezzeretCruelMachinist extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.TEZZERET); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Draw a card. this.addAbility(new LoyaltyAbility(new DrawCardSourceControllerEffect(1), 1)); diff --git a/Mage.Sets/src/mage/cards/t/TezzeretMasterOfMetal.java b/Mage.Sets/src/mage/cards/t/TezzeretMasterOfMetal.java index a08d1dfaaf6..c9a4fd6ecb8 100644 --- a/Mage.Sets/src/mage/cards/t/TezzeretMasterOfMetal.java +++ b/Mage.Sets/src/mage/cards/t/TezzeretMasterOfMetal.java @@ -2,21 +2,18 @@ package mage.cards.t; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.dynamicvalue.common.ArtifactYouControlCount; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.LoseLifeTargetEffect; import mage.abilities.effects.common.RevealCardsFromLibraryUntilEffect; -import mage.abilities.hint.ValueHint; +import mage.abilities.hint.common.ArtifactYouControlHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.FilterPermanent; -import mage.filter.common.FilterArtifactCard; -import mage.filter.common.FilterControlledArtifactPermanent; +import mage.filter.StaticFilters; import mage.filter.predicate.Predicates; import mage.game.Game; import mage.game.permanent.Permanent; @@ -36,16 +33,15 @@ public final class TezzeretMasterOfMetal extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.TEZZERET); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +1: Reveal cards from the top of your library until you reveal an artifact card. Put that card into your hand and the rest on the bottom of your library in a random order. - this.addAbility(new LoyaltyAbility(new RevealCardsFromLibraryUntilEffect(new FilterArtifactCard(), Zone.HAND, Zone.LIBRARY), 1)); + this.addAbility(new LoyaltyAbility(new RevealCardsFromLibraryUntilEffect(StaticFilters.FILTER_CARD_ARTIFACT, Zone.HAND, Zone.LIBRARY), 1)); // -3: Target opponent loses life equal to the number of artifacts you control. - DynamicValue xValue = new PermanentsOnBattlefieldCount(new FilterControlledArtifactPermanent()); - Ability ability = new LoyaltyAbility(new LoseLifeTargetEffect(xValue), -3); + Ability ability = new LoyaltyAbility(new LoseLifeTargetEffect(ArtifactYouControlCount.instance).setText("target opponent loses life equal to the number of artifacts you control"), -3); ability.addTarget(new TargetOpponent()); - ability.addHint(new ValueHint("Artifacts you control", xValue)); + ability.addHint(ArtifactYouControlHint.instance); this.addAbility(ability); // -8: Gain control of all artifacts and creatures target opponent controls. diff --git a/Mage.Sets/src/mage/cards/t/TezzeretMasterOfTheBridge.java b/Mage.Sets/src/mage/cards/t/TezzeretMasterOfTheBridge.java index 90e4661e197..31b421e3b02 100644 --- a/Mage.Sets/src/mage/cards/t/TezzeretMasterOfTheBridge.java +++ b/Mage.Sets/src/mage/cards/t/TezzeretMasterOfTheBridge.java @@ -2,7 +2,6 @@ package mage.cards.t; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.OneShotEffect; @@ -43,7 +42,7 @@ public final class TezzeretMasterOfTheBridge extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.TEZZERET); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // Creature and planeswalker spells you cast have affinity for artifacts. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledSpellsEffect( diff --git a/Mage.Sets/src/mage/cards/t/TezzeretTheSchemer.java b/Mage.Sets/src/mage/cards/t/TezzeretTheSchemer.java index 8244bb7318f..9aec7c26321 100644 --- a/Mage.Sets/src/mage/cards/t/TezzeretTheSchemer.java +++ b/Mage.Sets/src/mage/cards/t/TezzeretTheSchemer.java @@ -4,7 +4,6 @@ package mage.cards.t; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.dynamicvalue.common.SignInversionDynamicValue; @@ -33,7 +32,7 @@ public final class TezzeretTheSchemer extends CardImpl { this.subtype.add(SubType.TEZZERET); //Starting Loyalty - 5 - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +1: Create a colorless artifact token named Etherium Cell which has "{T}, Sacrifice this artifact: Add one mana of any color." this.addAbility(new LoyaltyAbility(new CreateTokenEffect(new EtheriumCellToken()), 1)); diff --git a/Mage.Sets/src/mage/cards/t/TezzeretTheSeeker.java b/Mage.Sets/src/mage/cards/t/TezzeretTheSeeker.java index 581046db955..852cc6630a5 100644 --- a/Mage.Sets/src/mage/cards/t/TezzeretTheSeeker.java +++ b/Mage.Sets/src/mage/cards/t/TezzeretTheSeeker.java @@ -5,7 +5,6 @@ import java.util.List; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.costs.Cost; import mage.abilities.costs.common.PayVariableLoyaltyCost; import mage.abilities.effects.ContinuousEffectImpl; @@ -35,7 +34,7 @@ public final class TezzeretTheSeeker extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.TEZZERET); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Untap up to two target artifacts. LoyaltyAbility ability = new LoyaltyAbility(new UntapTargetEffect(), 1); diff --git a/Mage.Sets/src/mage/cards/t/TezzeretsBetrayal.java b/Mage.Sets/src/mage/cards/t/TezzeretsBetrayal.java index 02aa259eb37..da6499ea4f2 100644 --- a/Mage.Sets/src/mage/cards/t/TezzeretsBetrayal.java +++ b/Mage.Sets/src/mage/cards/t/TezzeretsBetrayal.java @@ -1,7 +1,6 @@ package mage.cards.t; -import java.util.UUID; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.search.SearchLibraryGraveyardPutInHandEffect; import mage.cards.CardImpl; @@ -11,8 +10,9 @@ import mage.filter.FilterCard; import mage.filter.predicate.mageobject.NamePredicate; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author fireshoes */ public final class TezzeretsBetrayal extends CardImpl { @@ -32,7 +32,7 @@ public final class TezzeretsBetrayal extends CardImpl { // You may search your library and/or graveyard for a card named Tezzeret, Master of Metal, reveal it, and put it into your hand. // If you search your library this way, shuffle it. - getSpellAbility().addEffect(new SearchLibraryGraveyardPutInHandEffect(filter)); + getSpellAbility().addEffect(new SearchLibraryGraveyardPutInHandEffect(filter, false, true)); } private TezzeretsBetrayal(final TezzeretsBetrayal card) { diff --git a/Mage.Sets/src/mage/cards/t/TezzeretsStrider.java b/Mage.Sets/src/mage/cards/t/TezzeretsStrider.java index f6ee5a04243..528ca79b88a 100644 --- a/Mage.Sets/src/mage/cards/t/TezzeretsStrider.java +++ b/Mage.Sets/src/mage/cards/t/TezzeretsStrider.java @@ -39,11 +39,12 @@ public final class TezzeretsStrider extends CardImpl { Zone.BATTLEFIELD, new ConditionalContinuousEffect( new GainAbilitySourceEffect( - new MenaceAbility(), + new MenaceAbility(true), Duration.WhileOnBattlefield ), new PermanentsOnTheBattlefieldCondition(filter), - "As long as you control a Tezzeret planeswalker, {this} has menace" + "As long as you control a Tezzeret planeswalker, {this} has menace. " + + "(It can't be blocked except by two or more creatures.)" ) )); } diff --git a/Mage.Sets/src/mage/cards/t/ThaliaHereticCathar.java b/Mage.Sets/src/mage/cards/t/ThaliaHereticCathar.java index 75d5482e116..352008cc914 100644 --- a/Mage.Sets/src/mage/cards/t/ThaliaHereticCathar.java +++ b/Mage.Sets/src/mage/cards/t/ThaliaHereticCathar.java @@ -1,28 +1,41 @@ - package mage.cards.t; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.PermanentsEnterBattlefieldTappedEffect; import mage.abilities.keyword.FirstStrikeAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.game.Game; -import mage.game.events.EntersTheBattlefieldEvent; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicates; + +import java.util.UUID; /** - * * @author fireshoes */ public final class ThaliaHereticCathar extends CardImpl { + private static final FilterPermanent filter + = new FilterPermanent("creatures and nonbasic lands your opponents control"); + + static { + filter.add(TargetController.OPPONENT.getControllerPredicate()); + filter.add(Predicates.or( + CardType.CREATURE.getPredicate(), + Predicates.and( + Predicates.not(SuperType.BASIC.getPredicate()), + CardType.LAND.getPredicate() + ) + )); + } + public ThaliaHereticCathar(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.SOLDIER); @@ -33,7 +46,7 @@ public final class ThaliaHereticCathar extends CardImpl { this.addAbility(FirstStrikeAbility.getInstance()); // Creatures and nonbasic lands your opponents control enter the battlefield tapped. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ThaliaTapEffect())); + this.addAbility(new SimpleStaticAbility(new PermanentsEnterBattlefieldTappedEffect(filter))); } private ThaliaHereticCathar(final ThaliaHereticCathar card) { @@ -45,46 +58,3 @@ public final class ThaliaHereticCathar extends CardImpl { return new ThaliaHereticCathar(this); } } - -class ThaliaTapEffect extends ReplacementEffectImpl { - - ThaliaTapEffect() { - super(Duration.WhileOnBattlefield, Outcome.Tap); - staticText = "Creatures and nonbasic lands your opponents control enter the battlefield tapped"; - } - - ThaliaTapEffect(final ThaliaTapEffect effect) { - super(effect); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Permanent target = ((EntersTheBattlefieldEvent) event).getTarget(); - if (target != null) { - target.setTapped(true); - } - return false; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (game.getOpponents(source.getControllerId()).contains(event.getPlayerId())) { - Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget(); - if (permanent != null && (permanent.isCreature(game) || - (permanent.isLand(game) && !permanent.isBasic()))) { - return true; - } - } - return false; - } - - @Override - public ThaliaTapEffect copy() { - return new ThaliaTapEffect(this); - } -} diff --git a/Mage.Sets/src/mage/cards/t/ThalisseReverentMedium.java b/Mage.Sets/src/mage/cards/t/ThalisseReverentMedium.java index 8b984e57349..b45ef79bbb3 100644 --- a/Mage.Sets/src/mage/cards/t/ThalisseReverentMedium.java +++ b/Mage.Sets/src/mage/cards/t/ThalisseReverentMedium.java @@ -14,6 +14,7 @@ import mage.constants.*; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.token.SpiritWhiteToken; +import mage.util.CardUtil; import mage.watchers.Watcher; import java.util.HashMap; @@ -93,7 +94,7 @@ class ThalisseReverentMediumWatcher extends Watcher { if (event.getType() != GameEvent.EventType.CREATED_TOKEN) { return; } - tokenMap.compute(event.getPlayerId(), (u, i) -> i == null ? 1 : Integer.sum(i, 1)); + tokenMap.compute(event.getPlayerId(), CardUtil::setOrIncrementValue); } @Override diff --git a/Mage.Sets/src/mage/cards/t/ThassaGodOfTheSea.java b/Mage.Sets/src/mage/cards/t/ThassaGodOfTheSea.java index 0b774119ffe..d9b66b2fc69 100644 --- a/Mage.Sets/src/mage/cards/t/ThassaGodOfTheSea.java +++ b/Mage.Sets/src/mage/cards/t/ThassaGodOfTheSea.java @@ -41,7 +41,7 @@ public final class ThassaGodOfTheSea extends CardImpl { // At the beginning of your upkeep, scry 1. this.addAbility(new BeginningOfUpkeepTriggeredAbility( - new ScryEffect(1), TargetController.YOU, false + new ScryEffect(1, false), TargetController.YOU, false )); // {1}{U}: Target creature you control can't be blocked this turn. diff --git a/Mage.Sets/src/mage/cards/t/ThaumaticCompass.java b/Mage.Sets/src/mage/cards/t/ThaumaticCompass.java index a3d446cfae2..f8ca94370ef 100644 --- a/Mage.Sets/src/mage/cards/t/ThaumaticCompass.java +++ b/Mage.Sets/src/mage/cards/t/ThaumaticCompass.java @@ -31,7 +31,6 @@ public final class ThaumaticCompass extends CardImpl { public ThaumaticCompass(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); - this.transformable = true; this.secondSideCardClazz = mage.cards.s.SpiresOfOrazca.class; // {3}, {T}: Search your library for a basic land card, reveal it, put it into your hand, then shuffle your library. @@ -43,7 +42,7 @@ public final class ThaumaticCompass extends CardImpl { // At the beginning of your end step, if you control seven or more lands, transform Thaumatic Compass. this.addAbility(new TransformAbility()); - TriggeredAbility ability2 = new BeginningOfEndStepTriggeredAbility(new TransformSourceEffect(true), TargetController.YOU, false); + TriggeredAbility ability2 = new BeginningOfEndStepTriggeredAbility(new TransformSourceEffect(), TargetController.YOU, false); this.addAbility(new ConditionalInterveningIfTriggeredAbility( ability2, new PermanentsOnTheBattlefieldCondition(new FilterLandPermanent(), ComparisonType.MORE_THAN, 6, true), diff --git a/Mage.Sets/src/mage/cards/t/ThaumaturgesFamiliar.java b/Mage.Sets/src/mage/cards/t/ThaumaturgesFamiliar.java index 2b27b0feb0a..c3ba51397c2 100644 --- a/Mage.Sets/src/mage/cards/t/ThaumaturgesFamiliar.java +++ b/Mage.Sets/src/mage/cards/t/ThaumaturgesFamiliar.java @@ -27,7 +27,7 @@ public final class ThaumaturgesFamiliar extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Thaumaturge's Familiar enters the battlefield, scry 1. - this.addAbility(new EntersBattlefieldTriggeredAbility(new ScryEffect(1))); + this.addAbility(new EntersBattlefieldTriggeredAbility(new ScryEffect(1, false))); } private ThaumaturgesFamiliar(final ThaumaturgesFamiliar card) { diff --git a/Mage.Sets/src/mage/cards/t/TheAkroanWar.java b/Mage.Sets/src/mage/cards/t/TheAkroanWar.java index de9a2af57b8..8ee54a17572 100644 --- a/Mage.Sets/src/mage/cards/t/TheAkroanWar.java +++ b/Mage.Sets/src/mage/cards/t/TheAkroanWar.java @@ -33,7 +33,7 @@ public final class TheAkroanWar extends CardImpl { this.subtype.add(SubType.SAGA); // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) - SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); + SagaAbility sagaAbility = new SagaAbility(this); // I — Gain control of target creature for as long as The Akroan War remains on the battlefield. sagaAbility.addChapterEffect( @@ -53,7 +53,7 @@ public final class TheAkroanWar extends CardImpl { SagaChapter.CHAPTER_II, new AttacksIfAbleAllEffect( filter, Duration.UntilYourNextTurn, true - ) + ).setText("until your next turn, creatures your opponents control attack each combat if able") ); // III — Each tapped creature deals damage to itself equal to its power. diff --git a/Mage.Sets/src/mage/cards/t/TheAntiquitiesWar.java b/Mage.Sets/src/mage/cards/t/TheAntiquitiesWar.java index 8e239c44d1d..77f6b4b972f 100644 --- a/Mage.Sets/src/mage/cards/t/TheAntiquitiesWar.java +++ b/Mage.Sets/src/mage/cards/t/TheAntiquitiesWar.java @@ -34,7 +34,7 @@ public final class TheAntiquitiesWar extends CardImpl { this.subtype.add(SubType.SAGA); // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) - SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); + SagaAbility sagaAbility = new SagaAbility(this); // I, II — Look at the top five cards of your library. You may reveal an artifact card from among them and put it into your hand. Put the rest on the bottom of your library in a random order. sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_II, diff --git a/Mage.Sets/src/mage/cards/t/TheBearsOfLittjara.java b/Mage.Sets/src/mage/cards/t/TheBearsOfLittjara.java index 4d5c962f5e5..497dd590202 100644 --- a/Mage.Sets/src/mage/cards/t/TheBearsOfLittjara.java +++ b/Mage.Sets/src/mage/cards/t/TheBearsOfLittjara.java @@ -34,7 +34,7 @@ public final class TheBearsOfLittjara extends CardImpl { this.subtype.add(SubType.SAGA); // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) - SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); + SagaAbility sagaAbility = new SagaAbility(this); // I — Create a 2/2 blue Shapeshifter creature token with changeling. sagaAbility.addChapterEffect( diff --git a/Mage.Sets/src/mage/cards/t/TheBindingOfTheTitans.java b/Mage.Sets/src/mage/cards/t/TheBindingOfTheTitans.java index e76f919bbd8..85346d89214 100644 --- a/Mage.Sets/src/mage/cards/t/TheBindingOfTheTitans.java +++ b/Mage.Sets/src/mage/cards/t/TheBindingOfTheTitans.java @@ -40,7 +40,7 @@ public final class TheBindingOfTheTitans extends CardImpl { this.subtype.add(SubType.SAGA); // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) - SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); + SagaAbility sagaAbility = new SagaAbility(this); // I — Each player puts the top three cards of their library into their graveyard. sagaAbility.addChapterEffect( diff --git a/Mage.Sets/src/mage/cards/t/TheBirthOfMeletis.java b/Mage.Sets/src/mage/cards/t/TheBirthOfMeletis.java index ed138ab380c..ec8f4c058d3 100644 --- a/Mage.Sets/src/mage/cards/t/TheBirthOfMeletis.java +++ b/Mage.Sets/src/mage/cards/t/TheBirthOfMeletis.java @@ -34,7 +34,7 @@ public final class TheBirthOfMeletis extends CardImpl { this.subtype.add(SubType.SAGA); // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) - SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); + SagaAbility sagaAbility = new SagaAbility(this); // I — Search your library for a basic Plains card, reveal it, put it into your hand, then shuffle your library. sagaAbility.addChapterEffect( diff --git a/Mage.Sets/src/mage/cards/t/TheBloodskyMassacre.java b/Mage.Sets/src/mage/cards/t/TheBloodskyMassacre.java index f362604daf8..78c4dc81945 100644 --- a/Mage.Sets/src/mage/cards/t/TheBloodskyMassacre.java +++ b/Mage.Sets/src/mage/cards/t/TheBloodskyMassacre.java @@ -33,7 +33,7 @@ public final class TheBloodskyMassacre extends CardImpl { this.subtype.add(SubType.SAGA); // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) - SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); + SagaAbility sagaAbility = new SagaAbility(this); // I — Create a 2/3 red Demon Berserker creature token with menace. sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, new CreateTokenEffect(new DemonBerserkerToken())); diff --git a/Mage.Sets/src/mage/cards/t/TheCelestus.java b/Mage.Sets/src/mage/cards/t/TheCelestus.java new file mode 100644 index 00000000000..24bc5c1f851 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TheCelestus.java @@ -0,0 +1,114 @@ +package mage.cards.t; + +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.common.BecomeDayAsEntersAbility; +import mage.abilities.common.BecomesDayOrNightTriggeredAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.mana.AnyColorManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SuperType; +import mage.game.Game; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TheCelestus extends CardImpl { + + public TheCelestus(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); + + this.addSuperType(SuperType.LEGENDARY); + + // If it's neither day nor night, it becomes day as The Celestus enters the battlefield. + this.addAbility(new BecomeDayAsEntersAbility()); + + // {T}: Add one mana of any color. + this.addAbility(new AnyColorManaAbility()); + + // {3}, {T}: If it's night, it becomes day. Otherwise, it becomes night. Activate only as a sorcery. + Ability ability = new ActivateAsSorceryActivatedAbility( + new TheCelestusDayNightEffect(), new GenericManaCost(3) + ); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + + // Whenever day becomes night or night becomes day, you gain 1 life. You may draw a card. If you do, discard a card. + this.addAbility(new BecomesDayOrNightTriggeredAbility(new TheCelestusLootEffect())); + } + + private TheCelestus(final TheCelestus card) { + super(card); + } + + @Override + public TheCelestus copy() { + return new TheCelestus(this); + } +} + +class TheCelestusDayNightEffect extends OneShotEffect { + + TheCelestusDayNightEffect() { + super(Outcome.Benefit); + staticText = "if it's night, it becomes day. Otherwise, it becomes night"; + } + + private TheCelestusDayNightEffect(final TheCelestusDayNightEffect effect) { + super(effect); + } + + @Override + public TheCelestusDayNightEffect copy() { + return new TheCelestusDayNightEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + if (game.checkDayNight(false)) { + game.setDaytime(true); + } else { + game.setDaytime(false); + } + return true; + } +} + +class TheCelestusLootEffect extends OneShotEffect { + + TheCelestusLootEffect() { + super(Outcome.DrawCard); + staticText = "you gain 1 life. You may draw a card. If you do, discard a card"; + } + + private TheCelestusLootEffect(final TheCelestusLootEffect effect) { + super(effect); + } + + @Override + public TheCelestusLootEffect copy() { + return new TheCelestusLootEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + player.gainLife(1, game, source); + if (player.chooseUse(outcome, "Draw a card?", source, game)) { + player.drawCards(1, source, game); + player.discard(1, false, false, source, game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/t/TheEldestReborn.java b/Mage.Sets/src/mage/cards/t/TheEldestReborn.java index 9b9895b7451..a7011578b29 100644 --- a/Mage.Sets/src/mage/cards/t/TheEldestReborn.java +++ b/Mage.Sets/src/mage/cards/t/TheEldestReborn.java @@ -48,7 +48,7 @@ public final class TheEldestReborn extends CardImpl { this.subtype.add(SubType.SAGA); // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) - SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); + SagaAbility sagaAbility = new SagaAbility(this); // I — Each opponent sacrifices a creature or planeswalker. sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, new SacrificeOpponentsEffect(filterSacrifice) diff --git a/Mage.Sets/src/mage/cards/t/TheFallOfLordKonda.java b/Mage.Sets/src/mage/cards/t/TheFallOfLordKonda.java new file mode 100644 index 00000000000..450b8d2312e --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TheFallOfLordKonda.java @@ -0,0 +1,73 @@ +package mage.cards.t; + +import mage.abilities.common.SagaAbility; +import mage.abilities.effects.common.ExileSagaAndReturnTransformedEffect; +import mage.abilities.effects.common.ExileTargetEffect; +import mage.abilities.effects.common.continuous.GainControlAllOwnedEffect; +import mage.abilities.keyword.TransformAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.SagaChapter; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterOpponentsCreaturePermanent; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TheFallOfLordKonda extends CardImpl { + + private static final FilterPermanent filter + = new FilterOpponentsCreaturePermanent("creature an opponent controls with mana value 4 or greater"); + + static { + filter.add(new ManaValuePredicate(ComparisonType.MORE_THAN, 3)); + } + + public TheFallOfLordKonda(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); + + this.subtype.add(SubType.SAGA); + this.secondSideCardClazz = mage.cards.f.FragmentOfKonda.class; + + // (As this Saga enters and after your draw step, add a lore counter.) + SagaAbility sagaAbility = new SagaAbility(this); + + // I — Exile target creature an opponent controls with mana value 4 or greater. + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_I, + new ExileTargetEffect(), new TargetPermanent(filter) + ); + + // II — Each player gains control of all permanents they own. + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_II, SagaChapter.CHAPTER_II, + new GainControlAllOwnedEffect(StaticFilters.FILTER_PERMANENTS) + ); + + // III — Exile this Saga, then return it to the battlefield transformed under your control. + this.addAbility(new TransformAbility()); + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_III, + new ExileSagaAndReturnTransformedEffect() + ); + + this.addAbility(sagaAbility); + } + + private TheFallOfLordKonda(final TheFallOfLordKonda card) { + super(card); + } + + @Override + public TheFallOfLordKonda copy() { + return new TheFallOfLordKonda(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TheFirstEruption.java b/Mage.Sets/src/mage/cards/t/TheFirstEruption.java index 3a8d8054169..2af887c778a 100644 --- a/Mage.Sets/src/mage/cards/t/TheFirstEruption.java +++ b/Mage.Sets/src/mage/cards/t/TheFirstEruption.java @@ -44,7 +44,7 @@ public final class TheFirstEruption extends CardImpl { this.subtype.add(SubType.SAGA); // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) - SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); + SagaAbility sagaAbility = new SagaAbility(this); // I — The First Eruption deals 1 damage to each creature without flying. sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, new DamageAllEffect(1, filter)); diff --git a/Mage.Sets/src/mage/cards/t/TheFlameOfKeld.java b/Mage.Sets/src/mage/cards/t/TheFlameOfKeld.java index 951bc53e6b2..762def5eb96 100644 --- a/Mage.Sets/src/mage/cards/t/TheFlameOfKeld.java +++ b/Mage.Sets/src/mage/cards/t/TheFlameOfKeld.java @@ -28,7 +28,7 @@ public final class TheFlameOfKeld extends CardImpl { this.subtype.add(SubType.SAGA); // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) - SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); + SagaAbility sagaAbility = new SagaAbility(this); // I — Discard your hand. sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, new DiscardHandControllerEffect()); diff --git a/Mage.Sets/src/mage/cards/t/TheKamiWar.java b/Mage.Sets/src/mage/cards/t/TheKamiWar.java new file mode 100644 index 00000000000..62364fcf76d --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TheKamiWar.java @@ -0,0 +1,78 @@ +package mage.cards.t; + +import mage.abilities.common.SagaAbility; +import mage.abilities.effects.Effects; +import mage.abilities.effects.common.ExileSagaAndReturnTransformedEffect; +import mage.abilities.effects.common.ExileTargetEffect; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.abilities.effects.common.discard.DiscardEachPlayerEffect; +import mage.abilities.keyword.TransformAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SagaChapter; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterNonlandPermanent; +import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TheKamiWar extends CardImpl { + + private static final FilterPermanent filter + = new FilterNonlandPermanent("nonland permanent an opponent controls"); + private static final FilterPermanent filter2 + = new FilterNonlandPermanent("other target nonland permanent"); + + static { + filter.add(TargetController.OPPONENT.getControllerPredicate()); + filter2.add(AnotherPredicate.instance); + } + + public TheKamiWar(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}{U}{B}{R}{G}"); + + this.subtype.add(SubType.SAGA); + this.secondSideCardClazz = mage.cards.o.OKagachiMadeManifest.class; + + // (As this Saga enters and after your draw step, add a lore counter.) + SagaAbility sagaAbility = new SagaAbility(this); + + // I — Exile target nonland permanent an opponent controls. + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_I, + new ExileTargetEffect(), new TargetPermanent(filter) + ); + + // II — Return up to one other target nonland permanent to its owner's hand. Then each opponent discards a card. + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_II, SagaChapter.CHAPTER_II, + new Effects( + new ReturnToHandTargetEffect(), + new DiscardEachPlayerEffect(TargetController.OPPONENT) + .concatBy("Then") + ), new TargetPermanent(0, 1, filter2) + ); + + // III — Exile this Saga, then return it to the battlefield transformed under your control. + this.addAbility(new TransformAbility()); + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, new ExileSagaAndReturnTransformedEffect()); + + this.addAbility(sagaAbility); + } + + private TheKamiWar(final TheKamiWar card) { + super(card); + } + + @Override + public TheKamiWar copy() { + return new TheKamiWar(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TheLongReachOfNight.java b/Mage.Sets/src/mage/cards/t/TheLongReachOfNight.java new file mode 100644 index 00000000000..3606b77d03c --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TheLongReachOfNight.java @@ -0,0 +1,56 @@ +package mage.cards.t; + +import mage.abilities.common.SagaAbility; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.effects.common.ExileSagaAndReturnTransformedEffect; +import mage.abilities.effects.common.SacrificeOpponentsUnlessPayEffect; +import mage.abilities.keyword.TransformAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SagaChapter; +import mage.constants.SubType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TheLongReachOfNight extends CardImpl { + + public TheLongReachOfNight(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{B}"); + + this.subtype.add(SubType.SAGA); + this.secondSideCardClazz = mage.cards.a.AnimusOfNightsReach.class; + + // (As this Saga enters and after your draw step, add a lore counter.) + SagaAbility sagaAbility = new SagaAbility(this); + + // I, II — Each opponent sacrifices a creature unless they discard a card. + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_II, + new SacrificeOpponentsUnlessPayEffect( + new DiscardCardCost(), StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT + ) + ); + + // III — Exile this Saga, then return it to the battlefield transformed under your control. + this.addAbility(new TransformAbility()); + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_III, new ExileSagaAndReturnTransformedEffect() + ); + + this.addAbility(sagaAbility); + } + + private TheLongReachOfNight(final TheLongReachOfNight card) { + super(card); + } + + @Override + public TheLongReachOfNight copy() { + return new TheLongReachOfNight(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TheMendingOfDominaria.java b/Mage.Sets/src/mage/cards/t/TheMendingOfDominaria.java index 70de4698294..549b19c38d2 100644 --- a/Mage.Sets/src/mage/cards/t/TheMendingOfDominaria.java +++ b/Mage.Sets/src/mage/cards/t/TheMendingOfDominaria.java @@ -32,7 +32,7 @@ public final class TheMendingOfDominaria extends CardImpl { this.subtype.add(SubType.SAGA); // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) - SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); + SagaAbility sagaAbility = new SagaAbility(this); // I, II — Put the top two cards of your library into your graveyard, then you may return a creature card from your graveyard to your hand. sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_II, new TheMendingOfDominariaFirstEffect()); diff --git a/Mage.Sets/src/mage/cards/t/TheMirariConjecture.java b/Mage.Sets/src/mage/cards/t/TheMirariConjecture.java index b73f2a0f1cd..684676b2ca0 100644 --- a/Mage.Sets/src/mage/cards/t/TheMirariConjecture.java +++ b/Mage.Sets/src/mage/cards/t/TheMirariConjecture.java @@ -40,7 +40,7 @@ public final class TheMirariConjecture extends CardImpl { this.subtype.add(SubType.SAGA); // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) - SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); + SagaAbility sagaAbility = new SagaAbility(this); // I — Return target instant card from your graveyard to your hand. sagaAbility.addChapterEffect( this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_I, diff --git a/Mage.Sets/src/mage/cards/t/TheModernAge.java b/Mage.Sets/src/mage/cards/t/TheModernAge.java new file mode 100644 index 00000000000..8377efec220 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TheModernAge.java @@ -0,0 +1,50 @@ +package mage.cards.t; + +import mage.abilities.common.SagaAbility; +import mage.abilities.effects.common.DrawDiscardControllerEffect; +import mage.abilities.effects.common.ExileSagaAndReturnTransformedEffect; +import mage.abilities.keyword.TransformAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SagaChapter; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TheModernAge extends CardImpl { + + public TheModernAge(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}"); + + this.subtype.add(SubType.SAGA); + this.secondSideCardClazz = mage.cards.v.VectorGlider.class; + + // (As this Saga enters and after your draw step, add a lore counter.) + SagaAbility sagaAbility = new SagaAbility(this); + + // I, II — Draw a card, then discard a card. + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_II, + new DrawDiscardControllerEffect(1, 1) + ); + + // III — Exile this Saga, then return it to the battlefield transformed under your control. + this.addAbility(new TransformAbility()); + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, new ExileSagaAndReturnTransformedEffect()); + + this.addAbility(sagaAbility); + } + + private TheModernAge(final TheModernAge card) { + super(card); + } + + @Override + public TheModernAge copy() { + return new TheModernAge(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TheRavensWarning.java b/Mage.Sets/src/mage/cards/t/TheRavensWarning.java index f6aeef0abb0..e977abfd22f 100644 --- a/Mage.Sets/src/mage/cards/t/TheRavensWarning.java +++ b/Mage.Sets/src/mage/cards/t/TheRavensWarning.java @@ -15,7 +15,6 @@ import mage.constants.SubType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.DamagedPlayerEvent; import mage.game.events.GameEvent; @@ -35,7 +34,7 @@ public final class TheRavensWarning extends CardImpl { this.subtype.add(SubType.SAGA); // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) - SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); + SagaAbility sagaAbility = new SagaAbility(this); // I — Create a 1/1 blue Bird creature token with flying. You gain 2 life. sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, @@ -50,7 +49,7 @@ public final class TheRavensWarning extends CardImpl { // III — You may put a card you own from outside the game on top of your library. sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, - new WishEffect(StaticFilters.FILTER_CARD_A, false, false, true) + new WishEffect(true) ); sagaAbility.addHint(OpenSideboardHint.instance); this.addAbility(sagaAbility); diff --git a/Mage.Sets/src/mage/cards/t/TheRealityChip.java b/Mage.Sets/src/mage/cards/t/TheRealityChip.java new file mode 100644 index 00000000000..f84d9d13b69 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TheRealityChip.java @@ -0,0 +1,55 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.AttachedToMatchesFilterCondition; +import mage.abilities.decorator.ConditionalAsThoughEffect; +import mage.abilities.effects.common.continuous.LookAtTopCardOfLibraryAnyTimeEffect; +import mage.abilities.effects.common.continuous.PlayTheTopCardEffect; +import mage.abilities.keyword.ReconfigureAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TheRealityChip extends CardImpl { + + private static final Condition condition = new AttachedToMatchesFilterCondition(StaticFilters.FILTER_PERMANENT_CREATURE); + + public TheRealityChip(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{1}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.EQUIPMENT); + this.subtype.add(SubType.JELLYFISH); + this.power = new MageInt(0); + this.toughness = new MageInt(4); + + // You may look at the top card of your library any time. + this.addAbility(new SimpleStaticAbility(new LookAtTopCardOfLibraryAnyTimeEffect())); + + // As long as The Reality Chip is attached to a creature, you may play lands and cast spells from the top of your library. + this.addAbility(new SimpleStaticAbility(new ConditionalAsThoughEffect(new PlayTheTopCardEffect(), condition) + .setText("as long as {this} is attached to a creature, you may play lands and cast spells from the top of your library"))); + + // Reconfigure {2}{U} + this.addAbility(new ReconfigureAbility("{2}{U}")); + } + + private TheRealityChip(final TheRealityChip card) { + super(card); + } + + @Override + public TheRealityChip copy() { + return new TheRealityChip(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TheRestorationOfEiganjo.java b/Mage.Sets/src/mage/cards/t/TheRestorationOfEiganjo.java new file mode 100644 index 00000000000..06c81e03c48 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TheRestorationOfEiganjo.java @@ -0,0 +1,80 @@ +package mage.cards.t; + +import mage.abilities.common.SagaAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.effects.common.DoWhenCostPaid; +import mage.abilities.effects.common.ExileSagaAndReturnTransformedEffect; +import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; +import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; +import mage.abilities.keyword.TransformAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.common.FilterPermanentCard; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.target.common.TargetCardInLibrary; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TheRestorationOfEiganjo extends CardImpl { + + private static final FilterCard filter + = new FilterCard("a basic Plains card"); + private static final FilterCard filter2 + = new FilterPermanentCard("permanent card with mana value 2 or less from your graveyard"); + + static { + filter.add(SubType.PLAINS.getPredicate()); + filter.add(SuperType.BASIC.getPredicate()); + filter2.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 3)); + } + + public TheRestorationOfEiganjo(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); + + this.subtype.add(SubType.SAGA); + this.secondSideCardClazz = mage.cards.a.ArchitectOfRestoration.class; + + // (As this Saga enters and after your draw step, add a lore counter.) + SagaAbility sagaAbility = new SagaAbility(this); + + // I - Search your library for a basic Plains card, reveal it, put it into your hand, then shuffle. + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_I, new SearchLibraryPutInHandEffect( + new TargetCardInLibrary(filter), true, true + ) + ); + + // II — You may discard a card. When you do, return target permanent card with mana value 2 or less from your graveyard to the battlefield tapped + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new ReturnFromGraveyardToBattlefieldTargetEffect(true), false + ); + ability.addTarget(new TargetCardInYourGraveyard(filter2)); + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_II, new DoWhenCostPaid( + ability, new DiscardCardCost(), "Discard a card?" + ) + ); + + // III — Exile this Saga, then return it to the battlefield transformed under your control. + this.addAbility(new TransformAbility()); + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, new ExileSagaAndReturnTransformedEffect()); + + this.addAbility(sagaAbility); + } + + private TheRestorationOfEiganjo(final TheRestorationOfEiganjo card) { + super(card); + } + + @Override + public TheRestorationOfEiganjo copy() { + return new TheRestorationOfEiganjo(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TheRoyalScions.java b/Mage.Sets/src/mage/cards/t/TheRoyalScions.java index 291bbe9b25f..e5c59b1db9a 100644 --- a/Mage.Sets/src/mage/cards/t/TheRoyalScions.java +++ b/Mage.Sets/src/mage/cards/t/TheRoyalScions.java @@ -2,7 +2,6 @@ package mage.cards.t; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.dynamicvalue.common.CardsInControllerHandCount; import mage.abilities.effects.Effect; @@ -34,7 +33,7 @@ public final class TheRoyalScions extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.WILL); this.subtype.add(SubType.ROWAN); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +1: Draw a card, then discard a card. this.addAbility(new LoyaltyAbility(new DrawDiscardControllerEffect(1, 1), 1)); diff --git a/Mage.Sets/src/mage/cards/t/TheShatteredStatesEra.java b/Mage.Sets/src/mage/cards/t/TheShatteredStatesEra.java new file mode 100644 index 00000000000..69b38aa04b7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TheShatteredStatesEra.java @@ -0,0 +1,73 @@ +package mage.cards.t; + +import mage.abilities.common.SagaAbility; +import mage.abilities.effects.Effects; +import mage.abilities.effects.common.ExileSagaAndReturnTransformedEffect; +import mage.abilities.effects.common.UntapTargetEffect; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.continuous.GainControlTargetEffect; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.TransformAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SagaChapter; +import mage.constants.SubType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TheShatteredStatesEra extends CardImpl { + + public TheShatteredStatesEra(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{R}"); + + this.subtype.add(SubType.SAGA); + this.secondSideCardClazz = mage.cards.n.NamelessConqueror.class; + + // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) + SagaAbility sagaAbility = new SagaAbility(this); + + // I — Gain control of target creature until end of turn. Untap it. It gains haste until end of turn. + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_I, + new Effects( + new GainControlTargetEffect(Duration.EndOfTurn), + new UntapTargetEffect().setText("Untap it."), + new GainAbilityTargetEffect( + HasteAbility.getInstance(), Duration.EndOfTurn + ).setText("It gains haste until end of turn.") + ), new TargetCreaturePermanent() + ); + + // II — Creatures you control get +1/+0 until end of turn. + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_II, new BoostControlledEffect( + 1, 0, Duration.EndOfTurn + ) + ); + + // III — Exile this Saga, then return it to the battlefield transformed under your control. + this.addAbility(new TransformAbility()); + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_III, + new ExileSagaAndReturnTransformedEffect() + ); + + this.addAbility(sagaAbility); + } + + private TheShatteredStatesEra(final TheShatteredStatesEra card) { + super(card); + } + + @Override + public TheShatteredStatesEra copy() { + return new TheShatteredStatesEra(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TheSpaceFamilyGoblinson.java b/Mage.Sets/src/mage/cards/t/TheSpaceFamilyGoblinson.java new file mode 100644 index 00000000000..dc9b65d9aaa --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TheSpaceFamilyGoblinson.java @@ -0,0 +1,155 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.util.CardUtil; +import mage.watchers.Watcher; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TheSpaceFamilyGoblinson extends CardImpl { + + private static final Hint hint = new ValueHint( + "Dice you've rolled this turn", TheSpaceFamilyGoblinsonValue.instance + ); + + public TheSpaceFamilyGoblinson(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.GOBLIN); + this.subtype.add(SubType.GUEST); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // The Space Family Goblinson has trample as long as you've rolled three or more dice this turn. + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new GainAbilitySourceEffect(TrampleAbility.getInstance(), Duration.EndOfTurn), + TheSpaceFamilyGoblinsonCondition.instance, "{this} has trample " + + "as long as you've rolled three or more dice this turn" + )).addHint(hint), new TheSpaceFamilyGoblinsonWatcher()); + + // Whenever you roll a die, put a +1/+1 counter on The Space Family Goblinson. + this.addAbility(new TheSpaceFamilyGoblinsonTriggeredAbility()); + } + + private TheSpaceFamilyGoblinson(final TheSpaceFamilyGoblinson card) { + super(card); + } + + @Override + public TheSpaceFamilyGoblinson copy() { + return new TheSpaceFamilyGoblinson(this); + } +} + +enum TheSpaceFamilyGoblinsonCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + return TheSpaceFamilyGoblinsonWatcher.getCount(source.getControllerId(), game) >= 3; + } +} + +enum TheSpaceFamilyGoblinsonValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + return TheSpaceFamilyGoblinsonWatcher.getCount(sourceAbility.getControllerId(), game); + } + + @Override + public TheSpaceFamilyGoblinsonValue copy() { + return this; + } + + @Override + public String getMessage() { + return ""; + } +} + +class TheSpaceFamilyGoblinsonWatcher extends Watcher { + + private final Map map = new HashMap<>(); + + TheSpaceFamilyGoblinsonWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.DIE_ROLLED) { + map.compute(event.getPlayerId(), CardUtil::setOrIncrementValue); + } + } + + @Override + public void reset() { + super.reset(); + map.clear(); + } + + static int getCount(UUID playerId, Game game) { + return game + .getState() + .getWatcher(TheSpaceFamilyGoblinsonWatcher.class) + .map + .getOrDefault(playerId, 0); + } +} + +class TheSpaceFamilyGoblinsonTriggeredAbility extends TriggeredAbilityImpl { + + TheSpaceFamilyGoblinsonTriggeredAbility() { + super(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.P1P1.createInstance())); + } + + private TheSpaceFamilyGoblinsonTriggeredAbility(final TheSpaceFamilyGoblinsonTriggeredAbility ability) { + super(ability); + } + + @Override + public TheSpaceFamilyGoblinsonTriggeredAbility copy() { + return new TheSpaceFamilyGoblinsonTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DIE_ROLLED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return isControlledBy(event.getPlayerId()); + } + + @Override + public String getRule() { + return "Whenever you roll a die, put a +1/+1 counter on {this}"; + } +} diff --git a/Mage.Sets/src/mage/cards/t/TheThreeSeasons.java b/Mage.Sets/src/mage/cards/t/TheThreeSeasons.java index 855c213076c..338b3670ad5 100644 --- a/Mage.Sets/src/mage/cards/t/TheThreeSeasons.java +++ b/Mage.Sets/src/mage/cards/t/TheThreeSeasons.java @@ -40,7 +40,7 @@ public final class TheThreeSeasons extends CardImpl { this.subtype.add(SubType.SAGA); // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) - SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); + SagaAbility sagaAbility = new SagaAbility(this); // I — Mill three cards. sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, new MillCardsControllerEffect(3)); diff --git a/Mage.Sets/src/mage/cards/t/TheTricksterGodsHeist.java b/Mage.Sets/src/mage/cards/t/TheTricksterGodsHeist.java index 8806563e78d..39a247322de 100644 --- a/Mage.Sets/src/mage/cards/t/TheTricksterGodsHeist.java +++ b/Mage.Sets/src/mage/cards/t/TheTricksterGodsHeist.java @@ -33,7 +33,7 @@ public final class TheTricksterGodsHeist extends CardImpl { this.subtype.add(SubType.SAGA); // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) - SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); + SagaAbility sagaAbility = new SagaAbility(this); // I — You may exchange control of two target creatures. sagaAbility.addChapterEffect( diff --git a/Mage.Sets/src/mage/cards/t/TheTriumphOfAnax.java b/Mage.Sets/src/mage/cards/t/TheTriumphOfAnax.java index 3ccf85307b0..d24f163925a 100644 --- a/Mage.Sets/src/mage/cards/t/TheTriumphOfAnax.java +++ b/Mage.Sets/src/mage/cards/t/TheTriumphOfAnax.java @@ -54,12 +54,13 @@ public final class TheTriumphOfAnax extends CardImpl { // IV — Target creature you control fights up to one target creature you don't control. sagaAbility.addChapterEffect( this, SagaChapter.CHAPTER_IV, SagaChapter.CHAPTER_IV, - new Effects(new FightTargetsEffect( - "Target creature you control fights up to one target creature you don't control" - )), new Targets( + new Effects(new FightTargetsEffect().setText( + "Target creature you control fights up to one target creature you don't control. " + + "(Each deals damage equal to its power to the other.)")), + new Targets( new TargetControlledCreaturePermanent(), - new TargetPermanent(0, 1, StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL, false) - ) + new TargetPermanent(0, 1, + StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL, false)) ); this.addAbility(sagaAbility); } diff --git a/Mage.Sets/src/mage/cards/t/TheWanderer.java b/Mage.Sets/src/mage/cards/t/TheWanderer.java index 1b4a1bc002a..7191a319d54 100644 --- a/Mage.Sets/src/mage/cards/t/TheWanderer.java +++ b/Mage.Sets/src/mage/cards/t/TheWanderer.java @@ -2,7 +2,6 @@ package mage.cards.t; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.ExileTargetEffect; import mage.abilities.effects.common.PreventAllNonCombatDamageToAllEffect; @@ -40,7 +39,7 @@ public final class TheWanderer extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{3}{W}"); this.addSuperType(SuperType.LEGENDARY); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // Prevent all noncombat damage that would be dealt to you and other permanents you control. this.addAbility(new SimpleStaticAbility(new PreventAllNonCombatDamageToAllEffect( diff --git a/Mage.Sets/src/mage/cards/t/TheWanderingEmperor.java b/Mage.Sets/src/mage/cards/t/TheWanderingEmperor.java new file mode 100644 index 00000000000..5b8bfa900a3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TheWanderingEmperor.java @@ -0,0 +1,118 @@ +package mage.cards.t; + +import mage.abilities.Ability; +import mage.abilities.LoyaltyAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.ExileTargetEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.abilities.keyword.FlashAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.TappedPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.SamuraiToken; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TheWanderingEmperor extends CardImpl { + + private static final FilterPermanent filter = new FilterCreaturePermanent("tapped creature"); + + static { + filter.add(TappedPredicate.TAPPED); + } + + public TheWanderingEmperor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{2}{W}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.setStartingLoyalty(3); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // As long as The Wandering Emperor entered the battlefield this turn, you may activate her loyalty abilities any time you could cast an instant. + this.addAbility(new SimpleStaticAbility(new TheWanderingEmperorEffect())); + + // +1: Put a +1/+1 counter on up to one target creature. It gains first strike until end of turn. + Ability ability = new LoyaltyAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance()), 1 + ); + ability.addEffect(new GainAbilityTargetEffect( + FirstStrikeAbility.getInstance(), Duration.EndOfTurn + ).setText("It gains first strike until end of turn")); + ability.addTarget(new TargetCreaturePermanent(0, 1)); + this.addAbility(ability); + + // −1: Create a 2/2 white Samurai creature token with vigilance. + this.addAbility(new LoyaltyAbility(new CreateTokenEffect(new SamuraiToken()), -1)); + + // −2: Exile target tapped creature. You gain 2 life. + ability = new LoyaltyAbility(new ExileTargetEffect(), -2); + ability.addEffect(new GainLifeEffect(2)); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + } + + private TheWanderingEmperor(final TheWanderingEmperor card) { + super(card); + } + + @Override + public TheWanderingEmperor copy() { + return new TheWanderingEmperor(this); + } +} + +class TheWanderingEmperorEffect extends AsThoughEffectImpl { + + TheWanderingEmperorEffect() { + super(AsThoughEffectType.ACTIVATE_AS_INSTANT, Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "as long as {this} entered the battlefield this turn, " + + "you may activate her loyalty abilities any time you could cast an instant"; + } + + private TheWanderingEmperorEffect(final TheWanderingEmperorEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public TheWanderingEmperorEffect copy() { + return new TheWanderingEmperorEffect(this); + } + + @Override + public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) { + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + return permanent != null + && permanent.getTurnsOnBattlefield() == 0 + && affectedAbility.isControlledBy(source.getControllerId()) + && affectedAbility.getSourceId().equals(source.getSourceId()) + && affectedAbility instanceof LoyaltyAbility; + } + + @Override + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/t/TheWretched.java b/Mage.Sets/src/mage/cards/t/TheWretched.java index 0a3d7d08175..77190199ea7 100644 --- a/Mage.Sets/src/mage/cards/t/TheWretched.java +++ b/Mage.Sets/src/mage/cards/t/TheWretched.java @@ -1,12 +1,8 @@ package mage.cards.t; -import java.util.Objects; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EndOfCombatTriggeredAbility; -import mage.abilities.condition.common.SourceOnBattlefieldControlUnchangedCondition; -import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.GainControlTargetEffect; @@ -21,37 +17,37 @@ import mage.game.combat.CombatGroup; import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; import mage.watchers.common.BlockedAttackerWatcher; -import mage.watchers.common.LostControlWatcher; + +import java.util.UUID; /** - * * @author jeffwadsworth - * + *

* 5/1/2009 The ability grants you control of all creatures that are blocking it * as the ability resolves. This will include any creatures that were put onto * the battlefield blocking it. - * + *

* 5/1/2009 Any blocking creatures that regenerated during combat will have been * removed from combat. Since such creatures are no longer in combat, they * cannot be blocking The Wretched, which means you won't be able to gain * control of them. - * + *

* 5/1/2009 If The Wretched itself regenerated during combat, then it will have * been removed from combat. Since it is no longer in combat, there cannot be * any creatures blocking it, which means you won't be able to gain control of * any creatures. - * + *

* 10/1/2009 The Wretched's ability triggers only if it's still on the * battlefield when the end of combat step begins (after the combat damage * step). For example, if it's blocked by a 7/7 creature and is destroyed, its * ability won't trigger at all. - * + *

* 10/1/2009 If The Wretched leaves the battlefield, you no longer control it, * so the duration of its control-change effect ends. - * + *

* 10/1/2009 If you lose control of The Wretched before its ability resolves, * you won't gain control of the creatures blocking it at all. - * + *

* 10/1/2009 Once the ability resolves, it doesn't care whether the permanents * you gained control of remain creatures, only that they remain on the * battlefield. @@ -65,9 +61,7 @@ public final class TheWretched extends CardImpl { this.toughness = new MageInt(5); // At end of combat, gain control of all creatures blocking The Wretched for as long as you control The Wretched. - Ability ability = new EndOfCombatTriggeredAbility(new TheWretchedEffect(), false); - this.addAbility(ability, new BlockedAttackerWatcher()); - ability.addWatcher(new LostControlWatcher()); + this.addAbility(new EndOfCombatTriggeredAbility(new TheWretchedEffect(), false), new BlockedAttackerWatcher()); } private TheWretched(final TheWretched card) { @@ -93,32 +87,27 @@ class TheWretchedEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent theWretched = (Permanent) source.getSourceObjectIfItStillExists(game); - if (theWretched == null) { - return false; - } - if (theWretched.isRemovedFromCombat() || !theWretched.isAttacking()) { + Permanent theWretched = source.getSourcePermanentIfItStillExists(game); + if (theWretched == null + || theWretched.isRemovedFromCombat() + || !theWretched.isAttacking() + || !source.isControlledBy(theWretched.getControllerId())) { return false; } // Check if control of source has changed since ability triggered????? (does it work is it neccessary???) - Permanent permanent = game.getBattlefield().getPermanent(source.getSourceId()); - if (permanent != null && !Objects.equals(permanent.getControllerId(), source.getControllerId())) { - return false; - } - for (CombatGroup combatGroup : game.getCombat().getGroups()) { - if (combatGroup.getAttackers().contains(source.getSourceId())) { - for (UUID creatureId : combatGroup.getBlockers()) { - Permanent blocker = game.getPermanent(creatureId); - if (blocker != null && blocker.getBlocking() > 0) { - ContinuousEffect effect = new ConditionalContinuousEffect( - new GainControlTargetEffect(Duration.Custom, source.getControllerId()), - new SourceOnBattlefieldControlUnchangedCondition(), ""); - effect.setTargetPointer(new FixedTarget(blocker.getId())); - game.addEffect(effect, source); - - } + if (!combatGroup.getAttackers().contains(source.getSourceId())) { + continue; + } + for (UUID creatureId : combatGroup.getBlockers()) { + Permanent blocker = game.getPermanent(creatureId); + if (blocker == null + || blocker.getBlocking() <= 0) { + continue; } + ContinuousEffect effect = new GainControlTargetEffect(Duration.WhileControlled, source.getControllerId()); + effect.setTargetPointer(new FixedTarget(blocker.getId(), game)); + game.addEffect(effect, source); } } return true; diff --git a/Mage.Sets/src/mage/cards/t/TheaterOfHorrors.java b/Mage.Sets/src/mage/cards/t/TheaterOfHorrors.java index 73608f08a99..6fb986eb58e 100644 --- a/Mage.Sets/src/mage/cards/t/TheaterOfHorrors.java +++ b/Mage.Sets/src/mage/cards/t/TheaterOfHorrors.java @@ -95,8 +95,8 @@ class TheaterOfHorrorsCastEffect extends AsThoughEffectImpl { TheaterOfHorrorsCastEffect() { super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfGame, Outcome.Benefit); - staticText = "during your turn, if an opponent lost life this turn, " + - "you may play lands and cast spells from among cards exiled with {this}"; + staticText = "during your turn, if an opponent lost life this turn, " + + "you may play lands and cast spells from among cards exiled with {this}"; } private TheaterOfHorrorsCastEffect(final TheaterOfHorrorsCastEffect effect) { @@ -115,13 +115,20 @@ class TheaterOfHorrorsCastEffect extends AsThoughEffectImpl { @Override public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + Card theCard = game.getCard(objectId); + if (theCard == null) { + return false; + } + objectId = theCard.getMainCard().getId(); // for split cards and mdfc PlayerLostLifeWatcher watcher = game.getState().getWatcher(PlayerLostLifeWatcher.class); - if (watcher != null && game.isActivePlayer(source.getControllerId()) + if (watcher != null + && game.isActivePlayer(source.getControllerId()) && watcher.getAllOppLifeLost(source.getControllerId(), game) > 0 && affectedControllerId.equals(source.getControllerId()) && game.getState().getZone(objectId) == Zone.EXILED) { ExileZone zone = game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source)); - return zone != null && zone.contains(objectId); + return zone != null + && zone.contains(objectId); } return false; } diff --git a/Mage.Sets/src/mage/cards/t/ThiefOfHope.java b/Mage.Sets/src/mage/cards/t/ThiefOfHope.java index c86a9916e49..9f1eda77e5a 100644 --- a/Mage.Sets/src/mage/cards/t/ThiefOfHope.java +++ b/Mage.Sets/src/mage/cards/t/ThiefOfHope.java @@ -27,7 +27,7 @@ public final class ThiefOfHope extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); - Ability ability = new SpellCastControllerTriggeredAbility(new LoseLifeTargetEffect(1), StaticFilters.SPIRIT_OR_ARCANE_CARD, false); + Ability ability = new SpellCastControllerTriggeredAbility(new LoseLifeTargetEffect(1), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false); ability.addEffect(new GainLifeEffect(1).concatBy("and")); ability.addTarget(new TargetOpponent()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/t/ThievingSkydiver.java b/Mage.Sets/src/mage/cards/t/ThievingSkydiver.java index e67dc43906f..2bfaea2e86f 100644 --- a/Mage.Sets/src/mage/cards/t/ThievingSkydiver.java +++ b/Mage.Sets/src/mage/cards/t/ThievingSkydiver.java @@ -40,7 +40,7 @@ public final class ThievingSkydiver extends CardImpl { KickerAbility kickerAbility = new KickerAbility("{X}"); kickerAbility.getKickerCosts().forEach(cost -> { cost.setMinimumCost(1); - cost.setReminderText(". X can't be 0."); + cost.setReminderText(". X can't be 0. (You may pay an additional {X} as you cast this spell.)"); }); this.addAbility(kickerAbility); diff --git a/Mage.Sets/src/mage/cards/t/ThievingSprite.java b/Mage.Sets/src/mage/cards/t/ThievingSprite.java index 74ae8435b29..f10d8706951 100644 --- a/Mage.Sets/src/mage/cards/t/ThievingSprite.java +++ b/Mage.Sets/src/mage/cards/t/ThievingSprite.java @@ -1,24 +1,20 @@ - package mage.cards.t; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.discard.DiscardCardYouChooseTargetEffect; import mage.abilities.keyword.FlyingAbility; -import mage.cards.*; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.FilterCard; +import mage.constants.TargetController; import mage.filter.common.FilterControlledPermanent; -import mage.game.Game; -import mage.players.Player; -import mage.target.TargetCard; import mage.target.TargetPlayer; -import java.util.List; import java.util.UUID; /** @@ -26,6 +22,11 @@ import java.util.UUID; */ public final class ThievingSprite extends CardImpl { + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(new FilterControlledPermanent(SubType.FAERIE, "Faeries you control"), null); + + private static final String rule = "target player reveals X cards from their hand, where X is " + + xValue.getMessage() + ". You choose one of those cards. That player discards that card"; + public ThievingSprite(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); this.subtype.add(SubType.FAERIE); @@ -37,11 +38,11 @@ public final class ThievingSprite extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); - // When Thieving Sprite enters the battlefield, target player reveals X cards from their hand, where X is the number of Faeries you control. - // You choose one of those cards. That player discards that card. - Ability ability = new EntersBattlefieldTriggeredAbility(new ThievingSpriteEffect(), false); - TargetPlayer target = new TargetPlayer(); - ability.addTarget(target); + // When Thieving Sprite enters the battlefield, target player reveals X cards from their hand, + // where X is the number of Faeries you control. You choose one of those cards. + // That player discards that card. + Ability ability = new EntersBattlefieldTriggeredAbility(new DiscardCardYouChooseTargetEffect(TargetController.ANY, xValue).setText(rule)); + ability.addTarget(new TargetPlayer()); this.addAbility(ability); } @@ -55,69 +56,3 @@ public final class ThievingSprite extends CardImpl { return new ThievingSprite(this); } } - -class ThievingSpriteEffect extends OneShotEffect { - - public ThievingSpriteEffect() { - super(Outcome.Discard); - this.staticText = "target player reveals X cards from their hand, where X is the number of Faeries you control. You choose one of those cards. " - + "That player discards that card"; - } - - public ThievingSpriteEffect(final ThievingSpriteEffect effect) { - super(effect); - } - - @Override - public ThievingSpriteEffect copy() { - return new ThievingSpriteEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player targetPlayer = game.getPlayer(source.getFirstTarget()); - Player controller = game.getPlayer(source.getControllerId()); - if (targetPlayer == null || controller == null) { - return false; - } - - FilterControlledPermanent filter = new FilterControlledPermanent(); - filter.add(SubType.FAERIE.getPredicate()); - int numberOfFaeries = game.getBattlefield().countAll(filter, controller.getId(), game); - - Cards revealedCards = new CardsImpl(); - if (numberOfFaeries > 0 && targetPlayer.getHand().size() > numberOfFaeries) { - Cards cardsInHand = new CardsImpl(); - cardsInHand.addAll(targetPlayer.getHand()); - - TargetCard target = new TargetCard(numberOfFaeries, Zone.HAND, new FilterCard()); - - if (targetPlayer.choose(Outcome.Discard, cardsInHand, target, game)) { - List targets = target.getTargets(); - for (UUID targetId : targets) { - Card card = game.getCard(targetId); - if (card != null) { - revealedCards.add(card); - } - } - } - } else { - revealedCards.addAll(targetPlayer.getHand()); - } - - TargetCard targetInHand = new TargetCard(Zone.HAND, new FilterCard("card to discard")); - - if (!revealedCards.isEmpty()) { - targetPlayer.revealCards("Thieving Sprite", revealedCards, game); - Card card = null; - if (revealedCards.size() > 1) { - controller.choose(Outcome.Discard, revealedCards, targetInHand, game); - card = revealedCards.get(targetInHand.getFirstTarget(), game); - } else { - card = revealedCards.getRandom(game); - } - targetPlayer.discard(card, false, source, game); - } - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/t/ThingInTheIce.java b/Mage.Sets/src/mage/cards/t/ThingInTheIce.java index 5274ac890ea..20a03323497 100644 --- a/Mage.Sets/src/mage/cards/t/ThingInTheIce.java +++ b/Mage.Sets/src/mage/cards/t/ThingInTheIce.java @@ -42,7 +42,6 @@ public final class ThingInTheIce extends CardImpl { this.power = new MageInt(0); this.toughness = new MageInt(4); - this.transformable = true; this.secondSideCardClazz = mage.cards.a.AwokenHorror.class; // Defender @@ -58,7 +57,7 @@ public final class ThingInTheIce extends CardImpl { effect = new RemoveCounterSourceEffect(CounterType.ICE.createInstance(1)); effect.setText("remove an ice counter from {this}"); Ability ability = new SpellCastControllerTriggeredAbility(effect, filter, false); - effect = new ConditionalOneShotEffect(new TransformSourceEffect(true), new SourceHasCounterCondition(CounterType.ICE, 0, 0), + effect = new ConditionalOneShotEffect(new TransformSourceEffect(), new SourceHasCounterCondition(CounterType.ICE, 0, 0), "if there are no ice counters on it, transform it"); ability.addEffect(effect); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/t/ThirstForDiscovery.java b/Mage.Sets/src/mage/cards/t/ThirstForDiscovery.java new file mode 100644 index 00000000000..2711a2ae365 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/ThirstForDiscovery.java @@ -0,0 +1,39 @@ +package mage.cards.t; + +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.discard.DiscardControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ThirstForDiscovery extends CardImpl { + + public ThirstForDiscovery(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{U}"); + + // Draw three cards. Then discard two cards unless you discard a basic land card. + DiscardCardCost cost = new DiscardCardCost(StaticFilters.FILTER_CARD_BASIC_LAND_A); + cost.setText("discard a basic land card instead of discarding two cards"); + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(3)); + this.getSpellAbility().addEffect(new DoIfCostPaid( + null, new DiscardControllerEffect(2), cost + ).setText("Then discard two cards unless you discard a basic land card")); + } + + private ThirstForDiscovery(final ThirstForDiscovery card) { + super(card); + } + + @Override + public ThirstForDiscovery copy() { + return new ThirstForDiscovery(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/ThopterSpyNetwork.java b/Mage.Sets/src/mage/cards/t/ThopterSpyNetwork.java index 7f3f104d419..feb59d0f8e1 100644 --- a/Mage.Sets/src/mage/cards/t/ThopterSpyNetwork.java +++ b/Mage.Sets/src/mage/cards/t/ThopterSpyNetwork.java @@ -76,7 +76,7 @@ class ThopterSpyNetworkUpkeepTriggeredAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "At the beginning of your upkeep, if you control an artifact, create a 1/1 colorless Thopter artifact creature token with flying"; + return "At the beginning of your upkeep, if you control an artifact, create a 1/1 colorless Thopter artifact creature token with flying."; } } @@ -123,6 +123,6 @@ class ThopterSpyNetworkDamageTriggeredAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "Whenever one or more artifact creatures you control deal combat damage to a player, draw a card"; + return "Whenever one or more artifact creatures you control deal combat damage to a player, draw a card."; } } diff --git a/Mage.Sets/src/mage/cards/t/ThornbiteStaff.java b/Mage.Sets/src/mage/cards/t/ThornbiteStaff.java index 375babcf553..c77b8d348bc 100644 --- a/Mage.Sets/src/mage/cards/t/ThornbiteStaff.java +++ b/Mage.Sets/src/mage/cards/t/ThornbiteStaff.java @@ -1,4 +1,3 @@ - package mage.cards.t; import java.util.UUID; @@ -28,10 +27,7 @@ import mage.target.common.TargetAnyTarget; */ public final class ThornbiteStaff extends CardImpl { - private static final FilterPermanent filter = new FilterCreaturePermanent("a Shaman creature"); - static { - filter.add(SubType.SHAMAN.getPredicate()); - } + private static final FilterPermanent filter = new FilterCreaturePermanent(SubType.SHAMAN, "a Shaman creature"); public ThornbiteStaff(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.TRIBAL,CardType.ARTIFACT},"{2}"); diff --git a/Mage.Sets/src/mage/cards/t/ThorncasterSliver.java b/Mage.Sets/src/mage/cards/t/ThorncasterSliver.java index e3c75dd4a0e..569908848be 100644 --- a/Mage.Sets/src/mage/cards/t/ThorncasterSliver.java +++ b/Mage.Sets/src/mage/cards/t/ThorncasterSliver.java @@ -35,7 +35,7 @@ public final class ThorncasterSliver extends CardImpl { ability.addTarget(new TargetAnyTarget()); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledEffect(ability, - Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS) + Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_ALL_SLIVERS) .setText("Sliver creatures you control have \"Whenever this creature attacks, it deals 1 damage to any target.\""))); } diff --git a/Mage.Sets/src/mage/cards/t/ThornmantleStriker.java b/Mage.Sets/src/mage/cards/t/ThornmantleStriker.java index 5dc657323e8..6379c57fd03 100644 --- a/Mage.Sets/src/mage/cards/t/ThornmantleStriker.java +++ b/Mage.Sets/src/mage/cards/t/ThornmantleStriker.java @@ -1,5 +1,6 @@ package mage.cards.t; +import java.util.Map; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; @@ -16,6 +17,8 @@ import mage.constants.SubType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.counters.Counter; +import mage.counters.Counters; import mage.filter.common.FilterControlledPermanent; import mage.game.Game; import mage.game.permanent.Permanent; @@ -90,29 +93,51 @@ class ThornmantleStrikerEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); Permanent permanent = game.getPermanent(source.getFirstTarget()); - if (controller != null && permanent != null) { - int toRemove = xValue.calculate(game, source, this); - int removed = 0; - String[] counterNames = permanent.getCounters(game).keySet().toArray(new String[0]); - for (String counterName : counterNames) { - if (controller.chooseUse(Outcome.Neutral, "Do you want to remove " + counterName + " counters?", source, game)) { - if (permanent.getCounters(game).get(counterName).getCount() == 1 || (toRemove - removed == 1)) { - permanent.removeCounters(counterName, 1, source, game); - removed++; - } else { - int amount = controller.getAmount(1, Math.min(permanent.getCounters(game).get(counterName).getCount(), toRemove - removed), "How many?", game); - if (amount > 0) { - removed += amount; - permanent.removeCounters(counterName, amount, source, game); - } - } - } - if (removed >= toRemove) { - break; - } + if (controller == null || permanent == null) { + return false; + } + int elves = xValue.calculate(game, source, this); + if (elves < 1) { + return false; + } + // Make copy of counters to avoid concurrent modification exception + Counters counters = permanent.getCounters(game).copy(); + int totalCounters = 0; + for (Counter counter : counters.values()) { + totalCounters += counter.getCount(); + } + if (totalCounters == 0) { + return false; + } + if (totalCounters <= elves) { + for (Map.Entry entry : counters.entrySet()) { + permanent.removeCounters(entry.getKey(), entry.getValue().getCount(), source, game); } return true; } - return false; + if (counters.size() == 1) { + String counterName = counters.keySet().iterator().next(); + permanent.removeCounters(counterName, elves, source, game); + return true; + } + int remainingCounters = totalCounters; + int countersLeftToRemove = elves; + for (Map.Entry entry : counters.entrySet()) { + String counterName = entry.getKey(); + int numCounters = entry.getValue().getCount(); + remainingCounters -= numCounters; + int min = Math.max(0, countersLeftToRemove - remainingCounters); + int max = Math.min(countersLeftToRemove, numCounters); + int toRemove = controller.getAmount(min, max, counterName + " counters to remove", game); + // Sanity check in case of GUI bugs/disconnects + toRemove = Math.max(toRemove, min); + toRemove = Math.min(toRemove, max); + permanent.removeCounters(counterName, toRemove, source, game); + countersLeftToRemove -= toRemove; + if (countersLeftToRemove <= 0) { + break; + } + } + return true; } } diff --git a/Mage.Sets/src/mage/cards/t/ThoughtErasure.java b/Mage.Sets/src/mage/cards/t/ThoughtErasure.java index d2f7f570703..8553da54639 100644 --- a/Mage.Sets/src/mage/cards/t/ThoughtErasure.java +++ b/Mage.Sets/src/mage/cards/t/ThoughtErasure.java @@ -1,35 +1,29 @@ package mage.cards.t; -import java.util.UUID; import mage.abilities.effects.common.discard.DiscardCardYouChooseTargetEffect; import mage.abilities.effects.keyword.SurveilEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.TargetController; -import mage.filter.FilterCard; -import mage.filter.common.FilterNonlandCard; +import mage.filter.StaticFilters; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class ThoughtErasure extends CardImpl { - private static final FilterCard filter = new FilterNonlandCard(); - public ThoughtErasure(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{U}{B}"); // Target opponent reveals their hand. You choose a nonland card from it. That player discards that card. - this.getSpellAbility().addEffect(new DiscardCardYouChooseTargetEffect( - filter, TargetController.ANY - )); + this.getSpellAbility().addEffect(new DiscardCardYouChooseTargetEffect(StaticFilters.FILTER_CARD_NON_LAND)); this.getSpellAbility().addTarget(new TargetOpponent()); // Surveil 1. - this.getSpellAbility().addEffect(new SurveilEffect(1)); + this.getSpellAbility().addEffect(new SurveilEffect(1).concatBy("
")); } private ThoughtErasure(final ThoughtErasure card) { diff --git a/Mage.Sets/src/mage/cards/t/ThousandFacedShadow.java b/Mage.Sets/src/mage/cards/t/ThousandFacedShadow.java new file mode 100644 index 00000000000..0a1d59b6f67 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/ThousandFacedShadow.java @@ -0,0 +1,106 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.common.CreateTokenCopyTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.NinjutsuAbility; +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.common.FilterAttackingCreature; +import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.game.Game; +import mage.game.events.EntersTheBattlefieldEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ThousandFacedShadow extends CardImpl { + + public ThousandFacedShadow(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{U}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.NINJA); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Ninjutsu {2}{U}{U} + this.addAbility(new NinjutsuAbility("{2}{U}{U}")); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When Thousand-Faced Shadow enters the battlefield from your hand, if it's attacking, create a token that's a copy of another target attacking creature. The token enters the battlefield tapped and attacking. + this.addAbility(new ThousandFacedShadowTriggeredAbility()); + } + + private ThousandFacedShadow(final ThousandFacedShadow card) { + super(card); + } + + @Override + public ThousandFacedShadow copy() { + return new ThousandFacedShadow(this); + } +} + +class ThousandFacedShadowTriggeredAbility extends TriggeredAbilityImpl { + + private static final FilterPermanent filter = new FilterAttackingCreature("another attacking creature"); + + static { + filter.add(AnotherPredicate.instance); + } + + ThousandFacedShadowTriggeredAbility() { + super(Zone.BATTLEFIELD, new CreateTokenCopyTargetEffect( + null, null, false, 1, true, true + )); + this.addTarget(new TargetPermanent(filter)); + } + + private ThousandFacedShadowTriggeredAbility(final ThousandFacedShadowTriggeredAbility ability) { + super(ability); + } + + @Override + public ThousandFacedShadowTriggeredAbility copy() { + return new ThousandFacedShadowTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent permanent = game.getPermanent(event.getTargetId()); + return permanent != null + && ((EntersTheBattlefieldEvent) event).getFromZone() == Zone.HAND + && permanent.getId().equals(getSourceId()); + } + + @Override + public boolean checkInterveningIfClause(Game game) { + Permanent permanent = getSourcePermanentIfItStillExists(game); + return permanent != null && permanent.isAttacking(); + } + + @Override + public String getRule() { + return "When {this} enters the battlefield from your hand, if it's attacking, " + + "create a token that's a copy of another target attacking creature. " + + "The token enters the battlefield tapped and attacking."; + } +} diff --git a/Mage.Sets/src/mage/cards/t/ThrabenGargoyle.java b/Mage.Sets/src/mage/cards/t/ThrabenGargoyle.java index 0aa7323da1e..d2861c441bc 100644 --- a/Mage.Sets/src/mage/cards/t/ThrabenGargoyle.java +++ b/Mage.Sets/src/mage/cards/t/ThrabenGargoyle.java @@ -26,7 +26,6 @@ public final class ThrabenGargoyle extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); - this.transformable = true; this.secondSideCardClazz = mage.cards.s.StonewingAntagonizer.class; // Defender @@ -34,7 +33,7 @@ public final class ThrabenGargoyle extends CardImpl { // {6}: Transform Thraben Gargoyle. this.addAbility(new TransformAbility()); - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new TransformSourceEffect(true), new GenericManaCost(6))); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new TransformSourceEffect(), new GenericManaCost(6))); } private ThrabenGargoyle(final ThrabenGargoyle card) { diff --git a/Mage.Sets/src/mage/cards/t/ThrabenMilitia.java b/Mage.Sets/src/mage/cards/t/ThrabenMilitia.java index 65bad5e707e..d9998dc5920 100644 --- a/Mage.Sets/src/mage/cards/t/ThrabenMilitia.java +++ b/Mage.Sets/src/mage/cards/t/ThrabenMilitia.java @@ -22,7 +22,6 @@ public final class ThrabenMilitia extends CardImpl { // this card is the second face of double-faced card this.nightCard = true; - this.transformable = true; this.power = new MageInt(5); this.toughness = new MageInt(4); diff --git a/Mage.Sets/src/mage/cards/t/ThrabenSentry.java b/Mage.Sets/src/mage/cards/t/ThrabenSentry.java index 5b6ceba84f0..1269ff1b844 100644 --- a/Mage.Sets/src/mage/cards/t/ThrabenSentry.java +++ b/Mage.Sets/src/mage/cards/t/ThrabenSentry.java @@ -23,7 +23,6 @@ public final class ThrabenSentry extends CardImpl { this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.SOLDIER); - this.transformable = true; this.secondSideCardClazz = ThrabenMilitia.class; this.power = new MageInt(2); @@ -33,7 +32,7 @@ public final class ThrabenSentry extends CardImpl { // Whenever another creature you control dies, you may transform Thraben Sentry. this.addAbility(new TransformAbility()); - this.addAbility(new DiesCreatureTriggeredAbility(new TransformSourceEffect(true), true, new FilterControlledCreaturePermanent())); + this.addAbility(new DiesCreatureTriggeredAbility(new TransformSourceEffect(), true, new FilterControlledCreaturePermanent())); } private ThrabenSentry(final ThrabenSentry card) { diff --git a/Mage.Sets/src/mage/cards/t/ThrashingMudspawn.java b/Mage.Sets/src/mage/cards/t/ThrashingMudspawn.java index 7e1becc18ee..3f614b1a97e 100644 --- a/Mage.Sets/src/mage/cards/t/ThrashingMudspawn.java +++ b/Mage.Sets/src/mage/cards/t/ThrashingMudspawn.java @@ -30,7 +30,7 @@ public final class ThrashingMudspawn extends CardImpl { // Whenever Thrashing Mudspawn is dealt damage, you lose that much life. Ability ability = new DealtDamageToSourceTriggeredAbility( - new ThrashingMudspawnEffect(), false, false, true + new ThrashingMudspawnEffect(), false, false ); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/t/Thraximundar.java b/Mage.Sets/src/mage/cards/t/Thraximundar.java index 06e0485b5db..c8c4c15d9ed 100644 --- a/Mage.Sets/src/mage/cards/t/Thraximundar.java +++ b/Mage.Sets/src/mage/cards/t/Thraximundar.java @@ -96,7 +96,7 @@ class ThraximundarTriggeredAbility extends TriggeredAbilityImpl { } @Override - public String getTriggerPhrase() { + public String getRule() { return "Whenever {this} attacks, defending player sacrifices a creature."; } } @@ -123,7 +123,7 @@ class PlayerSacrificesCreatureTriggeredAbility extends TriggeredAbilityImpl { } @Override - public String getRule() { + public String getTriggerPhrase() { return "Whenever a player sacrifices a creature, " ; } diff --git a/Mage.Sets/src/mage/cards/t/ThresherBeast.java b/Mage.Sets/src/mage/cards/t/ThresherBeast.java index fea559dca3a..ae70f9a8cb6 100644 --- a/Mage.Sets/src/mage/cards/t/ThresherBeast.java +++ b/Mage.Sets/src/mage/cards/t/ThresherBeast.java @@ -1,23 +1,14 @@ - package mage.cards.t; import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.common.BecomesBlockedByCreatureTriggeredAbility; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.effects.common.SacrificeEffect; 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.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.targetpointer.FixedTarget; /** * @@ -32,7 +23,9 @@ public final class ThresherBeast extends CardImpl { this.toughness = new MageInt(4); // Whenever Thresher Beast becomes blocked, defending player sacrifices a land. - this.addAbility(new BecomesBlockedByCreatureTriggeredAbility(new ThresherBeastEffect(), false)); + this.addAbility(new BecomesBlockedSourceTriggeredAbility( + new SacrificeEffect(StaticFilters.FILTER_LAND, 1, "defending player"), + false, true)); } private ThresherBeast(final ThresherBeast card) { @@ -44,34 +37,3 @@ public final class ThresherBeast extends CardImpl { return new ThresherBeast(this); } } - -class ThresherBeastEffect extends OneShotEffect { - - public ThresherBeastEffect() { - super(Outcome.Discard); - this.staticText = "defending player sacrifices a land"; - } - - public ThresherBeastEffect(final ThresherBeastEffect effect) { - super(effect); - } - - @Override - public ThresherBeastEffect copy() { - return new ThresherBeastEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent blockingCreature = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (blockingCreature != null) { - Player opponent = game.getPlayer(blockingCreature.getControllerId()); - if (opponent != null) { - Effect effect = new SacrificeEffect(StaticFilters.FILTER_LAND, 1, ""); - effect.setTargetPointer(new FixedTarget(opponent.getId())); - return effect.apply(game, source); - } - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/t/ThrillingEncore.java b/Mage.Sets/src/mage/cards/t/ThrillingEncore.java index 865b21b9ea5..81e837ea137 100644 --- a/Mage.Sets/src/mage/cards/t/ThrillingEncore.java +++ b/Mage.Sets/src/mage/cards/t/ThrillingEncore.java @@ -67,17 +67,12 @@ class ThrillingEncoreEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller == null) { - return false; - } - Cards cards = new CardsImpl(); - for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { - Player player = game.getPlayer(playerId); - if (player == null) { - continue; - } - cards.addAll(player.getGraveyard().getCards(filter, source.getSourceId(), playerId, game)); - } + CardsPutIntoGraveyardWatcher watcher = game.getState().getWatcher(CardsPutIntoGraveyardWatcher.class); + if (controller == null || watcher == null) { return false; } + + Cards cards = new CardsImpl(watcher.getCardsPutIntoGraveyardFromBattlefield(game)); + cards.removeIf(uuid -> !game.getCard(uuid).isCreature(game)); + return controller.moveCards(cards, Zone.BATTLEFIELD, source, game); } } diff --git a/Mage.Sets/src/mage/cards/t/ThrivingGrubs.java b/Mage.Sets/src/mage/cards/t/ThrivingGrubs.java index 4c8bc6f4044..d77d4dcc4a8 100644 --- a/Mage.Sets/src/mage/cards/t/ThrivingGrubs.java +++ b/Mage.Sets/src/mage/cards/t/ThrivingGrubs.java @@ -1,7 +1,6 @@ package mage.cards.t; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -15,24 +14,30 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.counters.CounterType; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class ThrivingGrubs extends CardImpl { public ThrivingGrubs(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); this.subtype.add(SubType.GREMLIN); this.power = new MageInt(2); this.toughness = new MageInt(1); // When Thriving Grubs enters the battlefield, you get {E}{E}. - this.addAbility(new EntersBattlefieldTriggeredAbility(new GetEnergyCountersControllerEffect(2), false)); + this.addAbility(new EntersBattlefieldTriggeredAbility( + new GetEnergyCountersControllerEffect(2), false + )); // Whenever Thriving Grubs attacks, you may pay {E}{E}. If you do, put a +1/+1 counter on it. - this.addAbility(new AttacksTriggeredAbility(new DoIfCostPaid(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), new PayEnergyCost(2)), false, - "Whenever {this} attacks you may pay {E}{E}. If you do, put a +1/+1 counter on it.")); + this.addAbility(new AttacksTriggeredAbility(new DoIfCostPaid( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()) + .setText("put a +1/+1 counter on it"), + new PayEnergyCost(2) + ), false)); } private ThrivingGrubs(final ThrivingGrubs card) { diff --git a/Mage.Sets/src/mage/cards/t/ThrivingIbex.java b/Mage.Sets/src/mage/cards/t/ThrivingIbex.java index 253f91b0409..e8c11693f86 100644 --- a/Mage.Sets/src/mage/cards/t/ThrivingIbex.java +++ b/Mage.Sets/src/mage/cards/t/ThrivingIbex.java @@ -1,7 +1,5 @@ - package mage.cards.t; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -15,14 +13,15 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.counters.CounterType; +import java.util.UUID; + /** - * * @author fireshoes */ public final class ThrivingIbex extends CardImpl { public ThrivingIbex(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); this.subtype.add(SubType.GOAT); this.power = new MageInt(2); this.toughness = new MageInt(4); @@ -31,8 +30,11 @@ public final class ThrivingIbex extends CardImpl { this.addAbility(new EntersBattlefieldTriggeredAbility(new GetEnergyCountersControllerEffect(2))); // Whenever Thriving Ibex attacks, you may pay {E}{E}. If you do, put a +1/+1 counter on it. - this.addAbility(new AttacksTriggeredAbility(new DoIfCostPaid(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), new PayEnergyCost(2)), false, - "Whenever {this} attacks you may pay {E}{E}. If you do, put a +1/+1 counter on it.")); + this.addAbility(new AttacksTriggeredAbility(new DoIfCostPaid( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()) + .setText("put a +1/+1 counter on it"), + new PayEnergyCost(2) + ), false)); } private ThrivingIbex(final ThrivingIbex card) { diff --git a/Mage.Sets/src/mage/cards/t/ThrivingRats.java b/Mage.Sets/src/mage/cards/t/ThrivingRats.java index eb93516f88a..ad91484a1c5 100644 --- a/Mage.Sets/src/mage/cards/t/ThrivingRats.java +++ b/Mage.Sets/src/mage/cards/t/ThrivingRats.java @@ -1,7 +1,5 @@ - package mage.cards.t; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -15,14 +13,15 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.counters.CounterType; +import java.util.UUID; + /** - * * @author fireshoes */ public final class ThrivingRats extends CardImpl { public ThrivingRats(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}"); this.subtype.add(SubType.RAT); this.power = new MageInt(1); this.toughness = new MageInt(2); @@ -31,8 +30,11 @@ public final class ThrivingRats extends CardImpl { this.addAbility(new EntersBattlefieldTriggeredAbility(new GetEnergyCountersControllerEffect(2))); // Whenever Thriving Rats attacks, you may pay {E}{E}. If you do, put a +1/+1 counter on it. - this.addAbility(new AttacksTriggeredAbility(new DoIfCostPaid(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), new PayEnergyCost(2)), false, - "Whenever {this} attacks you may pay {E}{E}. If you do, put a +1/+1 counter on it.")); + this.addAbility(new AttacksTriggeredAbility(new DoIfCostPaid( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()) + .setText("put a +1/+1 counter on it"), + new PayEnergyCost(2) + ), false)); } private ThrivingRats(final ThrivingRats card) { diff --git a/Mage.Sets/src/mage/cards/t/ThrivingRhino.java b/Mage.Sets/src/mage/cards/t/ThrivingRhino.java index 07497f26d93..bde7b650ed1 100644 --- a/Mage.Sets/src/mage/cards/t/ThrivingRhino.java +++ b/Mage.Sets/src/mage/cards/t/ThrivingRhino.java @@ -1,7 +1,5 @@ - package mage.cards.t; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -15,14 +13,15 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.counters.CounterType; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class ThrivingRhino extends CardImpl { public ThrivingRhino(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); this.subtype.add(SubType.RHINO); this.power = new MageInt(2); this.toughness = new MageInt(3); @@ -31,8 +30,11 @@ public final class ThrivingRhino extends CardImpl { this.addAbility(new EntersBattlefieldTriggeredAbility(new GetEnergyCountersControllerEffect(2))); // Whenever Thriving Rhino attacks, you may pay {E}{E}. If you do, put a +1/+1 counter on it. - this.addAbility(new AttacksTriggeredAbility(new DoIfCostPaid(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), new PayEnergyCost(2)), false)); - + this.addAbility(new AttacksTriggeredAbility(new DoIfCostPaid( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()) + .setText("put a +1/+1 counter on it"), + new PayEnergyCost(2) + ), false)); } private ThrivingRhino(final ThrivingRhino card) { diff --git a/Mage.Sets/src/mage/cards/t/ThrivingTurtle.java b/Mage.Sets/src/mage/cards/t/ThrivingTurtle.java index d523b374754..654d8ce82cd 100644 --- a/Mage.Sets/src/mage/cards/t/ThrivingTurtle.java +++ b/Mage.Sets/src/mage/cards/t/ThrivingTurtle.java @@ -1,7 +1,5 @@ - package mage.cards.t; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -15,14 +13,15 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.counters.CounterType; +import java.util.UUID; + /** - * * @author fireshoes */ public final class ThrivingTurtle extends CardImpl { public ThrivingTurtle(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{U}"); this.subtype.add(SubType.TURTLE); this.power = new MageInt(0); this.toughness = new MageInt(3); @@ -31,7 +30,11 @@ public final class ThrivingTurtle extends CardImpl { this.addAbility(new EntersBattlefieldTriggeredAbility(new GetEnergyCountersControllerEffect(2))); // Whenever Thriving Turtle attacks, you may pay {E}{E}. If you do, put a +1/+1 counter on it. - this.addAbility(new AttacksTriggeredAbility(new DoIfCostPaid(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), new PayEnergyCost(2)), false)); + this.addAbility(new AttacksTriggeredAbility(new DoIfCostPaid( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()) + .setText("put a +1/+1 counter on it"), + new PayEnergyCost(2) + ), false)); } private ThrivingTurtle(final ThrivingTurtle card) { diff --git a/Mage.Sets/src/mage/cards/t/ThroatSlitter.java b/Mage.Sets/src/mage/cards/t/ThroatSlitter.java index 36e1aade566..377510142dd 100644 --- a/Mage.Sets/src/mage/cards/t/ThroatSlitter.java +++ b/Mage.Sets/src/mage/cards/t/ThroatSlitter.java @@ -38,7 +38,7 @@ public final class ThroatSlitter extends CardImpl { this.toughness = new MageInt(2); // Ninjutsu {2}{B} ({2}{B}, Return an unblocked attacker you control to hand: Put this card onto the battlefield from your hand tapped and attacking.) - this.addAbility(new NinjutsuAbility(new ManaCostsImpl("{2}{B}"))); + this.addAbility(new NinjutsuAbility("{2}{B}")); // Whenever Throat Slitter deals combat damage to a player, destroy target nonblack creature that player controls. this.addAbility(new ThroatSlitterTriggeredAbility()); diff --git a/Mage.Sets/src/mage/cards/t/ThrullChampion.java b/Mage.Sets/src/mage/cards/t/ThrullChampion.java index 86771434f84..f715edf9914 100644 --- a/Mage.Sets/src/mage/cards/t/ThrullChampion.java +++ b/Mage.Sets/src/mage/cards/t/ThrullChampion.java @@ -1,13 +1,10 @@ package mage.cards.t; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.condition.common.SourceOnBattlefieldControlUnchangedCondition; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.common.continuous.BoostAllEffect; import mage.abilities.effects.common.continuous.GainControlTargetEffect; import mage.cards.CardImpl; @@ -18,19 +15,15 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.target.TargetPermanent; -import mage.watchers.common.LostControlWatcher; + +import java.util.UUID; /** - * * @author MarcoMarin */ public final class ThrullChampion extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Thrull creatures"); - - static { - filter.add(SubType.THRULL.getPredicate()); - } + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(SubType.THRULL, "Thrull creatures"); public ThrullChampion(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}"); @@ -40,14 +33,11 @@ public final class ThrullChampion extends CardImpl { // Thrull creatures get +1/+1. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostAllEffect(1, 1, Duration.WhileOnBattlefield, filter, false))); + // {tap}: Gain control of target Thrull for as long as you control Thrull Champion. - ConditionalContinuousEffect ThrullChampionGainControlEffect = new ConditionalContinuousEffect( - new GainControlTargetEffect(Duration.Custom), - new SourceOnBattlefieldControlUnchangedCondition(), - "Gain control of target Thrull for as long as you control {this}"); - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, ThrullChampionGainControlEffect, new TapSourceCost()); + Ability ability = new SimpleActivatedAbility(new GainControlTargetEffect(Duration.WhileControlled) + .setText("gain control of target Thrull for as long as you control {this}"), new TapSourceCost()); ability.addTarget(new TargetPermanent(filter)); - ability.addWatcher(new LostControlWatcher()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/t/ThunderingMightmare.java b/Mage.Sets/src/mage/cards/t/ThunderingMightmare.java new file mode 100644 index 00000000000..fa48a7b0c4a --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/ThunderingMightmare.java @@ -0,0 +1,50 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.common.SpellCastOpponentTriggeredAbility; +import mage.abilities.effects.common.continuous.GainAbilityPairedEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.SoulbondAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ThunderingMightmare extends CardImpl { + + public ThunderingMightmare(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}"); + + this.subtype.add(SubType.HORSE); + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Soulbond + this.addAbility(new SoulbondAbility()); + + // As long as Thundering Mightmare is paired with another creature, each of those creatures has "Whenever an opponent casts a spell, put a +1/+1 counter on this creature." + this.addAbility(new SimpleStaticAbility(new GainAbilityPairedEffect(new SpellCastOpponentTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()) + .setText("put a +1/+1 counter on this creature"), + false + ), "As long as {this} is paired with another creature, each of those creatures " + + "has \"Whenever an opponent casts a spell, put a +1/+1 counter on this creature.\""))); + } + + private ThunderingMightmare(final ThunderingMightmare card) { + super(card); + } + + @Override + public ThunderingMightmare copy() { + return new ThunderingMightmare(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/ThunderingRaiju.java b/Mage.Sets/src/mage/cards/t/ThunderingRaiju.java new file mode 100644 index 00000000000..688e79149d0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/ThunderingRaiju.java @@ -0,0 +1,74 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.DamagePlayersEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.filter.predicate.permanent.ModifiedPredicate; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ThunderingRaiju extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledCreaturePermanent(); + + static { + filter.add(ModifiedPredicate.instance); + filter.add(AnotherPredicate.instance); + } + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter); + private static final Hint hint = new ValueHint("Other modified creatures you control", xValue); + + public ThunderingRaiju(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{R}"); + + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // Whenever Thundering Raiju attacks, put a +1/+1 counter on target creature you control. Then Thundering Raiju deals X damage to each opponent, where X is the number of modified creatures you control other than Thundering Raiju. + Ability ability = new AttacksTriggeredAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance()) + ); + ability.addTarget(new TargetControlledCreaturePermanent()); + ability.addEffect(new DamagePlayersEffect( + Outcome.Damage, xValue, TargetController.OPPONENT + ).setText("Then {this} deals X damage to each opponent, " + + "where X is the number of modified creatures you control other than {this}")); + this.addAbility(ability.addHint(hint)); + } + + private ThunderingRaiju(final ThunderingRaiju card) { + super(card); + } + + @Override + public ThunderingRaiju copy() { + return new ThunderingRaiju(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/Thunderstaff.java b/Mage.Sets/src/mage/cards/t/Thunderstaff.java index 522641eaf24..1ec5b9b9085 100644 --- a/Mage.Sets/src/mage/cards/t/Thunderstaff.java +++ b/Mage.Sets/src/mage/cards/t/Thunderstaff.java @@ -1,4 +1,3 @@ - package mage.cards.t; import java.util.UUID; @@ -14,8 +13,8 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Zone; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AttackingPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -26,21 +25,15 @@ import mage.game.permanent.Permanent; */ public final class Thunderstaff extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Attacking creatures"); - static { - filter.add(AttackingPredicate.instance); - } - public Thunderstaff(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}"); // As long as Thunderstaff is untapped, if a creature would deal combat damage to you, prevent 1 of that damage. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ThunderstaffPreventionEffect())); // {2}, {tap}: Attacking creatures get +1/+0 until end of turn. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostAllEffect(1,0,Duration.EndOfTurn, filter, false), new GenericManaCost(2)); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostAllEffect(1,0,Duration.EndOfTurn, StaticFilters.FILTER_ATTACKING_CREATURES, false), new GenericManaCost(2)); ability.addCost(new TapSourceCost()); this.addAbility(ability); - } private Thunderstaff(final Thunderstaff card) { diff --git a/Mage.Sets/src/mage/cards/t/ThundersteelColossus.java b/Mage.Sets/src/mage/cards/t/ThundersteelColossus.java new file mode 100644 index 00000000000..9ac828bf682 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/ThundersteelColossus.java @@ -0,0 +1,44 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.keyword.CrewAbility; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ThundersteelColossus extends CardImpl { + + public ThundersteelColossus(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{7}"); + + this.subtype.add(SubType.VEHICLE); + this.power = new MageInt(7); + this.toughness = new MageInt(7); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // Crew 2 + this.addAbility(new CrewAbility(2)); + } + + private ThundersteelColossus(final ThundersteelColossus card) { + super(card); + } + + @Override + public ThundersteelColossus copy() { + return new ThundersteelColossus(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TibaltRakishInstigator.java b/Mage.Sets/src/mage/cards/t/TibaltRakishInstigator.java index a7b685b0daa..4eefa1ffa80 100644 --- a/Mage.Sets/src/mage/cards/t/TibaltRakishInstigator.java +++ b/Mage.Sets/src/mage/cards/t/TibaltRakishInstigator.java @@ -1,7 +1,6 @@ package mage.cards.t; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.continuous.CantGainLifeAllEffect; @@ -22,7 +21,7 @@ public final class TibaltRakishInstigator extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.TIBALT); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // Your opponents can't gain life. this.addAbility(new SimpleStaticAbility( diff --git a/Mage.Sets/src/mage/cards/t/TibaltTheFiendBlooded.java b/Mage.Sets/src/mage/cards/t/TibaltTheFiendBlooded.java index 75c95923b0f..641ed0497d8 100644 --- a/Mage.Sets/src/mage/cards/t/TibaltTheFiendBlooded.java +++ b/Mage.Sets/src/mage/cards/t/TibaltTheFiendBlooded.java @@ -3,7 +3,6 @@ package mage.cards.t; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.dynamicvalue.common.CardsInTargetHandCount; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.ContinuousEffectImpl; @@ -38,7 +37,7 @@ public final class TibaltTheFiendBlooded extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.TIBALT); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(2)); + this.setStartingLoyalty(2); // +1: Draw a card, then discard a card at random. LoyaltyAbility ability = new LoyaltyAbility(new DrawCardSourceControllerEffect(1), 1); diff --git a/Mage.Sets/src/mage/cards/t/TidalFlats.java b/Mage.Sets/src/mage/cards/t/TidalFlats.java index 1dd7cec6ee1..b282176c8f9 100644 --- a/Mage.Sets/src/mage/cards/t/TidalFlats.java +++ b/Mage.Sets/src/mage/cards/t/TidalFlats.java @@ -108,7 +108,7 @@ class TidalFlatsEffect extends OneShotEffect { Permanent blocker = game.getPermanent(blockerId); if (blocker != null && Objects.equals(blocker.getControllerId(), controller.getId())) { ContinuousEffect effect = new GainAbilityTargetEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(blocker.getId())); + effect.setTargetPointer(new FixedTarget(blocker.getId(), game)); game.addEffect(effect, source); } } diff --git a/Mage.Sets/src/mage/cards/t/TideShaper.java b/Mage.Sets/src/mage/cards/t/TideShaper.java index 2c42a2c3154..a3a9b830944 100644 --- a/Mage.Sets/src/mage/cards/t/TideShaper.java +++ b/Mage.Sets/src/mage/cards/t/TideShaper.java @@ -35,7 +35,7 @@ public final class TideShaper extends CardImpl { filter.add(TargetController.OPPONENT.getControllerPredicate()); } - private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter, ComparisonType.MORE_THAN, 0, false); + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter, false); private static final Hint hint = new ConditionHint(condition); public TideShaper(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/t/TidebinderMage.java b/Mage.Sets/src/mage/cards/t/TidebinderMage.java index 39aa56875fa..ebf8ae0c759 100644 --- a/Mage.Sets/src/mage/cards/t/TidebinderMage.java +++ b/Mage.Sets/src/mage/cards/t/TidebinderMage.java @@ -1,48 +1,43 @@ package mage.cards.t; -import java.util.UUID; import mage.MageInt; import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; +import mage.abilities.effects.common.DontUntapInControllersUntapStepTargetEffect; import mage.abilities.effects.common.TapTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.PhaseStep; +import mage.constants.SubType; import mage.constants.TargetController; -import mage.constants.WatcherScope; -import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.ZoneChangeEvent; -import mage.game.permanent.Permanent; -import mage.target.Target; import mage.target.common.TargetCreaturePermanent; -import mage.watchers.Watcher; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class TidebinderMage extends CardImpl { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("red or green creature an opponent controls"); + + private static final FilterCreaturePermanent filter + = new FilterCreaturePermanent("red or green creature an opponent controls"); + static { filter.add(TargetController.OPPONENT.getControllerPredicate()); - filter.add(Predicates.or(new ColorPredicate(ObjectColor.RED), new ColorPredicate(ObjectColor.GREEN))); + filter.add(Predicates.or( + new ColorPredicate(ObjectColor.RED), + new ColorPredicate(ObjectColor.GREEN) + )); } public TidebinderMage(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{U}{U}"); this.subtype.add(SubType.MERFOLK); this.subtype.add(SubType.WIZARD); @@ -52,11 +47,9 @@ public final class TidebinderMage extends CardImpl { // When Tidebinder Mage enters the battlefield, tap target red or green creature an opponent controls. // That creature doesn't untap during its controller's untap step for as long as you control Tidebinder Mage. Ability ability = new EntersBattlefieldTriggeredAbility(new TapTargetEffect(), false); - ability.addEffect(new TidebinderMageEffect()); - Target target = new TargetCreaturePermanent(filter); - ability.addTarget(target); - this.addAbility(ability, new TidebinderMageWatcher()); - + ability.addEffect(new DontUntapInControllersUntapStepTargetEffect(Duration.WhileControlled)); + ability.addTarget(new TargetCreaturePermanent(filter)); + this.addAbility(ability); } private TidebinderMage(final TidebinderMage card) { @@ -68,90 +61,3 @@ public final class TidebinderMage extends CardImpl { return new TidebinderMage(this); } } - -class TidebinderMageEffect extends ContinuousRuleModifyingEffectImpl { - - public TidebinderMageEffect() { - super(Duration.OneUse, Outcome.Detriment, false, false); - this.staticText = "That creature doesn't untap during its controller's untap step for as long as you control {this}"; - } - - public TidebinderMageEffect(final TidebinderMageEffect effect) { - super(effect); - } - - @Override - public TidebinderMageEffect copy() { - return new TidebinderMageEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - return false; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - // Source must be on the battlefield (it's neccessary to check here because if as response to the enter - // the battlefield triggered ability the source dies (or will be exiled), then the ZONE_CHANGE or LOST_CONTROL - // event will happen before this effect is applied ever) - Permanent sourcePermanent = game.getPermanent(source.getSourceId()); - if (sourcePermanent == null || !sourcePermanent.isControlledBy(source.getControllerId())) { - discard(); - return false; - } - if (event.getType() == GameEvent.EventType.LOST_CONTROL) { - if (event.getTargetId().equals(source.getSourceId())) { - discard(); - return false; - } - } - if (event.getType() == GameEvent.EventType.ZONE_CHANGE && event.getTargetId().equals(source.getSourceId())) { - ZoneChangeEvent zEvent = (ZoneChangeEvent)event; - if (zEvent.getFromZone() == Zone.BATTLEFIELD) { - discard(); - return false; - } - } - - if (game.getTurn().getStepType() == PhaseStep.UNTAP && event.getType() == GameEvent.EventType.UNTAP) { - if (event.getTargetId().equals(targetPointer.getFirst(game, source))) { - Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null && game.isActivePlayer(permanent.getControllerId())) { - return true; - } - } - } - return false; - } -} - -class TidebinderMageWatcher extends Watcher { - - TidebinderMageWatcher () { - super(WatcherScope.CARD); - } - - @Override - public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.LOST_CONTROL - && event.getPlayerId().equals(controllerId) - && event.getTargetId().equals(sourceId)) { - condition = true; - game.replaceEvent(event); - return; - } - if (event.getType() == GameEvent.EventType.ZONE_CHANGE && event.getTargetId().equals(sourceId)) { - ZoneChangeEvent zEvent = (ZoneChangeEvent)event; - if (zEvent.getFromZone() == Zone.BATTLEFIELD) { - condition = true; - game.replaceEvent(event); - } - } - } - - @Override - public void reset() { - //don't reset condition each turn - only when this leaves the battlefield - } -} diff --git a/Mage.Sets/src/mage/cards/t/TilonallisSummoner.java b/Mage.Sets/src/mage/cards/t/TilonallisSummoner.java index ac0e317720e..acb8437025c 100644 --- a/Mage.Sets/src/mage/cards/t/TilonallisSummoner.java +++ b/Mage.Sets/src/mage/cards/t/TilonallisSummoner.java @@ -88,7 +88,7 @@ class TilonallisSummonerEffect extends OneShotEffect { .setText("exile those tokens unless you have the city's blessing"); exileEffect.setTargetPointer(new FixedTargets(new CardsImpl(effect.getLastAddedTokenIds()), game)); game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility( - Zone.ALL, exileEffect, TargetController.ANY, new InvertCondition(CitysBlessingCondition.instance)), source); + exileEffect, TargetController.ANY, new InvertCondition(CitysBlessingCondition.instance)), source); } } return true; diff --git a/Mage.Sets/src/mage/cards/t/TimberShredder.java b/Mage.Sets/src/mage/cards/t/TimberShredder.java index 186ba31618f..6fe22125f0b 100644 --- a/Mage.Sets/src/mage/cards/t/TimberShredder.java +++ b/Mage.Sets/src/mage/cards/t/TimberShredder.java @@ -24,7 +24,6 @@ public final class TimberShredder extends CardImpl { // this card is the second face of double-faced card this.nightCard = true; - this.transformable = true; // Trample this.addAbility(TrampleAbility.getInstance()); diff --git a/Mage.Sets/src/mage/cards/t/TimeOfIce.java b/Mage.Sets/src/mage/cards/t/TimeOfIce.java index 2cbbd58b6fc..4b66573aafb 100644 --- a/Mage.Sets/src/mage/cards/t/TimeOfIce.java +++ b/Mage.Sets/src/mage/cards/t/TimeOfIce.java @@ -1,35 +1,25 @@ package mage.cards.t; -import java.util.UUID; -import mage.abilities.Ability; import mage.abilities.common.SagaAbility; -import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; import mage.abilities.effects.Effects; +import mage.abilities.effects.common.DontUntapInControllersUntapStepTargetEffect; import mage.abilities.effects.common.ReturnToHandFromBattlefieldAllEffect; import mage.abilities.effects.common.TapTargetEffect; -import mage.constants.SubType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.PhaseStep; import mage.constants.SagaChapter; -import mage.constants.WatcherScope; -import mage.constants.Zone; +import mage.constants.SubType; import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.permanent.TappedPredicate; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.ZoneChangeEvent; -import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; -import mage.watchers.Watcher; + +import java.util.UUID; /** - * * @author TheElk801 */ public final class TimeOfIce extends CardImpl { @@ -46,12 +36,12 @@ public final class TimeOfIce extends CardImpl { this.subtype.add(SubType.SAGA); // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) - SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); + SagaAbility sagaAbility = new SagaAbility(this); // I, II — Tap target creature an opponent controls. It doesn't untap during its controller's untap step for as long as you control Time of Ice. Effects effects = new Effects(); effects.add(new TapTargetEffect()); - effects.add(new TimeOfIceEffect()); + effects.add(new DontUntapInControllersUntapStepTargetEffect(Duration.WhileControlled, "It")); sagaAbility.addChapterEffect( this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_II, effects, new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE) @@ -59,7 +49,7 @@ public final class TimeOfIce extends CardImpl { // III — Return all tapped creatures to their owners' hands. sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, new ReturnToHandFromBattlefieldAllEffect(filter)); - this.addAbility(sagaAbility, new TimeOfIceWatcher()); + this.addAbility(sagaAbility); } private TimeOfIce(final TimeOfIce card) { @@ -71,101 +61,3 @@ public final class TimeOfIce extends CardImpl { return new TimeOfIce(this); } } - -class TimeOfIceEffect extends ContinuousRuleModifyingEffectImpl { - - public TimeOfIceEffect() { - super(Duration.Custom, Outcome.Detriment, false, false); - this.staticText = "That creature doesn't untap during its controller's untap step for as long as you control {this}"; - } - - public TimeOfIceEffect(final TimeOfIceEffect effect) { - super(effect); - } - - @Override - public TimeOfIceEffect copy() { - return new TimeOfIceEffect(this); - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.UNTAP - || event.getType() == GameEvent.EventType.ZONE_CHANGE - || event.getType() == GameEvent.EventType.LOST_CONTROL; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - // Source must be on the battlefield (it's neccessary to check here because if as response to the enter - // the battlefield triggered ability the source dies (or will be exiled), then the ZONE_CHANGE or LOST_CONTROL - // event will happen before this effect is applied ever) - Permanent sourceObject = game.getPermanent(source.getSourceId()); - if (sourceObject == null || sourceObject.getZoneChangeCounter(game) > source.getSourceObjectZoneChangeCounter() + 1) { - discard(); - return false; - } - switch (event.getType()) { - case ZONE_CHANGE: - // end effect if source does a zone move - if (event.getTargetId().equals(source.getSourceId())) { - ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - if (zEvent.getFromZone() == Zone.BATTLEFIELD) { - discard(); - return false; - } - } - break; - case UNTAP: - // prevent to untap the target creature - if (game.getTurn().getStepType() == PhaseStep.UNTAP && event.getTargetId().equals(targetPointer.getFirst(game, source))) { - Permanent targetCreature = game.getPermanent(targetPointer.getFirst(game, source)); - if (targetCreature != null) { - return targetCreature.isControlledBy(game.getActivePlayerId()); - } else { - discard(); - return false; - } - } - break; - case LOST_CONTROL: - // end effect if source control is changed - if (event.getTargetId().equals(source.getSourceId())) { - discard(); - return false; - } - break; - } - return false; - } -} - -class TimeOfIceWatcher extends Watcher { - - TimeOfIceWatcher() { - super(WatcherScope.CARD); - } - - @Override - public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.LOST_CONTROL - && event.getPlayerId().equals(controllerId) - && event.getTargetId().equals(sourceId)) { - condition = true; - game.replaceEvent(event); - return; - } - if (event.getType() == GameEvent.EventType.ZONE_CHANGE && event.getTargetId().equals(sourceId)) { - ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - if (zEvent.getFromZone() == Zone.BATTLEFIELD) { - condition = true; - game.replaceEvent(event); - } - } - } - - @Override - public void reset() { - //don't reset condition each turn - only when this leaves the battlefield - } -} diff --git a/Mage.Sets/src/mage/cards/t/TimeToFeed.java b/Mage.Sets/src/mage/cards/t/TimeToFeed.java index 92a4a5857e9..d6b40306365 100644 --- a/Mage.Sets/src/mage/cards/t/TimeToFeed.java +++ b/Mage.Sets/src/mage/cards/t/TimeToFeed.java @@ -15,12 +15,10 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; -import mage.constants.TargetController; import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; import mage.target.Target; @@ -40,11 +38,6 @@ import mage.target.common.TargetCreaturePermanent; */ public final class TimeToFeed extends CardImpl { - private static final FilterCreaturePermanent filter1 = new FilterCreaturePermanent("creature an opponent controls"); - static { - filter1.add(TargetController.OPPONENT.getControllerPredicate()); - } - public TimeToFeed(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{G}"); @@ -53,10 +46,11 @@ public final class TimeToFeed extends CardImpl { this.getSpellAbility().addEffect(new TimeToFeedTextEffect()); // Target creature you control fights that creature. Effect effect = new FightTargetsEffect(); - effect.setText("Target creature you control fights that creature"); + effect.setText("Target creature you control fights that creature. " + + "(Each deals damage equal to its power to the other.)"); this.getSpellAbility().addEffect(effect); - Target target = new TargetCreaturePermanent(filter1); + Target target = new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE); this.getSpellAbility().addTarget(target); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); diff --git a/Mage.Sets/src/mage/cards/t/TiminYouthfulGeist.java b/Mage.Sets/src/mage/cards/t/TiminYouthfulGeist.java new file mode 100644 index 00000000000..e2e3582ba79 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TiminYouthfulGeist.java @@ -0,0 +1,55 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfCombatTriggeredAbility; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.PartnerWithAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.TargetController; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TiminYouthfulGeist extends CardImpl { + + public TiminYouthfulGeist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // Partner with Rhoda, Geist Avenger + this.addAbility(new PartnerWithAbility("Rhoda, Geist Avenger")); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // At the beginning of each combat, tap up to one target creature. + Ability ability = new BeginningOfCombatTriggeredAbility( + new TapTargetEffect().setText("tap up to one target creature"), + TargetController.ANY, false + ); + ability.addTarget(new TargetCreaturePermanent(0, 1)); + this.addAbility(ability); + } + + private TiminYouthfulGeist(final TiminYouthfulGeist card) { + super(card); + } + + @Override + public TiminYouthfulGeist copy() { + return new TiminYouthfulGeist(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TimotharBaronOfBats.java b/Mage.Sets/src/mage/cards/t/TimotharBaronOfBats.java new file mode 100644 index 00000000000..5641da434dc --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TimotharBaronOfBats.java @@ -0,0 +1,169 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.common.DiesCreatureTriggeredAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.CostAdjuster; +import mage.abilities.costs.Costs; +import mage.abilities.costs.CostsImpl; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.costs.common.ExileFromGraveCost; +import mage.abilities.costs.mana.ManaCost; +import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.SacrificeSourceEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.hint.StaticHint; +import mage.abilities.keyword.WardAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.filter.predicate.mageobject.CardIdPredicate; +import mage.game.Game; +import mage.game.permanent.token.BatToken; +import mage.players.Player; +import mage.target.common.TargetCardInYourGraveyard; +import mage.target.targetpointer.FixedTargets; + +import java.util.UUID; + +public class TimotharBaronOfBats extends CardImpl { + + private static final FilterPermanent anotherVampireFilter = new FilterControlledCreaturePermanent("another nontoken Vampire you control"); + static { + anotherVampireFilter.add(AnotherPredicate.instance); + anotherVampireFilter.add(SubType.VAMPIRE.getPredicate()); + } + + public TimotharBaronOfBats(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.addSubType(SubType.VAMPIRE); + this.addSubType(SubType.NOBLE); + + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Ward—Discard a card. + this.addAbility(new WardAbility(new DiscardCardCost())); + + // Whenever another nontoken Vampire you control dies, you may pay {1} and exile it. + // If you do, create a 1/1 black Bat creature token with flying. + // It gains “When this creature deals combat damage to a player, + // sacrifice it and return the exiled card to the battlefield tapped.” + this.addAbility(new DiesCreatureTriggeredAbility( + new TimotharBaronOfBatsCreateBatEffect(), + false, + anotherVampireFilter, + true + )); + } + + private TimotharBaronOfBats(final TimotharBaronOfBats card) { super(card); } + + @Override + public Card copy() { return new TimotharBaronOfBats(this); } +} + +class TimotharBaronOfBatsCreateBatEffect extends OneShotEffect { + + TimotharBaronOfBatsCreateBatEffect() { + super(Outcome.Benefit); + staticText = "you may pay {1} and exile it. " + + "If you do, create a 1/1 black Bat creature token with flying. " + + "It gains \"When this creature deals combat damage to a player, " + + "sacrifice it and return the exiled card to the battlefield tapped.\""; + } + + private TimotharBaronOfBatsCreateBatEffect(final TimotharBaronOfBatsCreateBatEffect effect) { super(effect); } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { return false; } + + // Check vampire card still exists and is still in the graveyard + Card vampireCard = game.getCard(targetPointer.getFirst(game, source)); + if (vampireCard == null) { return false; } + + // Create costs + ManaCosts costs = new ManaCostsImpl<>("{1}"); + // Exiling the card is handled after the owner pays the mana cost. + String costPromptMessage = "Pay {1} and exile " + vampireCard.getName() + "? " + + "If you do, create a create a 1/1 black Bat creature token with flying. " + + "It gains \"When this creature deals combat damage to a player, " + + "sacrifice it and return the exiled card to the battlefield tapped\"."; + + // Ask player if they wanna pay cost + if (!costs.canPay(source, source, controller.getId(), game)) { return false; } + if (!controller.chooseUse(Outcome.Benefit, costPromptMessage, source, game)) { return false; } + if (!costs.pay(source, game, source, controller.getId(),false)) { return false; } + // Exile the card as part of the cost. + // Handled this way so that the player doesn't have to dig through their graveyard for the card. + controller.moveCards(vampireCard, Zone.EXILED, source, game); + + BatToken bat = new BatToken(); + bat.putOntoBattlefield(1, game, source); + + DealsCombatDamageToAPlayerTriggeredAbility sacAndReturnAbility = new DealsCombatDamageToAPlayerTriggeredAbility( + new SacrificeSourceEffect(), + false, + "When this creature deals combat damage to a player, " + + "sacrifice it and return the exiled card to the battlefield tapped", + false); + sacAndReturnAbility.addEffect(new TimotharBaronOfBatsReturnEffect(new MageObjectReference(vampireCard, game))); + sacAndReturnAbility.addHint(new StaticHint("Exiled card: " + vampireCard.getName())); + + GainAbilityTargetEffect gainAbilityTargetEffect = new GainAbilityTargetEffect(sacAndReturnAbility, Duration.Custom); + gainAbilityTargetEffect.setTargetPointer(new FixedTargets(bat, game)); + game.addEffect(gainAbilityTargetEffect, source); + + return true; + } + + @Override + public TimotharBaronOfBatsCreateBatEffect copy() { return new TimotharBaronOfBatsCreateBatEffect(this); } +} + +class TimotharBaronOfBatsReturnEffect extends OneShotEffect { + + private final MageObjectReference morOfCardToReturn; + + TimotharBaronOfBatsReturnEffect(MageObjectReference morOfCardToReturn) { + super(Outcome.PutCreatureInPlay); + staticText = "return the exiled card to the battlefield tapped"; + this.morOfCardToReturn = morOfCardToReturn; + } + + private TimotharBaronOfBatsReturnEffect(final TimotharBaronOfBatsReturnEffect effect) { + super(effect); + this.morOfCardToReturn = effect.morOfCardToReturn; + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { return false; } + + Card card = morOfCardToReturn.getCard(game); + if (card == null) { return false; } + + return player.moveCards(card, Zone.BATTLEFIELD, source, game, true, false, true, null); + } + + @Override + public Effect copy() { return new TimotharBaronOfBatsReturnEffect(this); } +} diff --git a/Mage.Sets/src/mage/cards/t/TirelessAngler.java b/Mage.Sets/src/mage/cards/t/TirelessAngler.java new file mode 100644 index 00000000000..efa0a2bb708 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TirelessAngler.java @@ -0,0 +1,72 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.effects.common.DraftFromSpellbookEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicates; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TirelessAngler extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent("an Island or Swamp"); + + static { + filter.add(Predicates.or( + SubType.ISLAND.getPredicate(), + SubType.SWAMP.getPredicate() + )); + } + + private static final List spellbook = Collections.unmodifiableList(Arrays.asList( + // "Archipelagore", mutate card + "Fleet Swallower", + "Moat Piranhas", + "Mystic Skyfish", + "Nadir Kraken", + // "Pouncing Shoreshark", mutate card + "Riptide Turtle", + "Ruin Crab", + // "Sea-Dasher Octopus", mutate card + "Serpent of Yawning Depths", + "Sigiled Starfish", + "Spined Megalodon", + "Stinging Lionfish", + "Voracious Greatshark", + "Wormhole Serpent" + )); + + public TirelessAngler(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ROGUE); + this.power = new MageInt(1); + this.toughness = new MageInt(4); + + // Whenever an Island or Swamp enters the battlefield under your control, draft a card from Tireless Angler's spellbook. + this.addAbility(new EntersBattlefieldControlledTriggeredAbility( + new DraftFromSpellbookEffect(spellbook), filter + )); + } + + private TirelessAngler(final TirelessAngler card) { + super(card); + } + + @Override + public TirelessAngler copy() { + return new TirelessAngler(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TirelessHauler.java b/Mage.Sets/src/mage/cards/t/TirelessHauler.java index c50d0ac4ca8..4f3cb398b9a 100644 --- a/Mage.Sets/src/mage/cards/t/TirelessHauler.java +++ b/Mage.Sets/src/mage/cards/t/TirelessHauler.java @@ -2,7 +2,6 @@ package mage.cards.t; import mage.MageInt; import mage.abilities.keyword.DayboundAbility; -import mage.abilities.keyword.TransformAbility; import mage.abilities.keyword.VigilanceAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -23,14 +22,12 @@ public final class TirelessHauler extends CardImpl { this.subtype.add(SubType.WEREWOLF); this.power = new MageInt(4); this.toughness = new MageInt(5); - this.transformable = true; this.secondSideCardClazz = mage.cards.d.DireStrainBrawler.class; // Vigilance this.addAbility(VigilanceAbility.getInstance()); // Daybound - this.addAbility(new TransformAbility()); this.addAbility(new DayboundAbility()); } diff --git a/Mage.Sets/src/mage/cards/t/TitanicBrawl.java b/Mage.Sets/src/mage/cards/t/TitanicBrawl.java index 5b83aadafdb..e42199ec912 100644 --- a/Mage.Sets/src/mage/cards/t/TitanicBrawl.java +++ b/Mage.Sets/src/mage/cards/t/TitanicBrawl.java @@ -9,10 +9,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Zone; -import mage.counters.CounterType; -import mage.filter.FilterPermanent; import mage.filter.StaticFilters; -import mage.filter.common.FilterControlledCreaturePermanent; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; @@ -23,14 +20,7 @@ import java.util.UUID; */ public final class TitanicBrawl extends CardImpl { - private static final FilterPermanent filter - = new FilterControlledCreaturePermanent("a creature you control with a +1/+1 counter on it"); - - static { - filter.add(CounterType.P1P1.getPredicate()); - } - - private static final Condition condition = new SourceTargetsPermanentCondition(filter); + private static final Condition condition = new SourceTargetsPermanentCondition(StaticFilters.FILTER_A_CONTROLLED_CREATURE_P1P1); public TitanicBrawl(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}"); diff --git a/Mage.Sets/src/mage/cards/t/TitanicGrowth.java b/Mage.Sets/src/mage/cards/t/TitanicGrowth.java index da385c3f5ed..b9d1749b5ef 100644 --- a/Mage.Sets/src/mage/cards/t/TitanicGrowth.java +++ b/Mage.Sets/src/mage/cards/t/TitanicGrowth.java @@ -1,34 +1,36 @@ - - package mage.cards.t; import java.util.UUID; +import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.BoostTargetEffect; 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; /** * @author Loki */ public final class TitanicGrowth extends CardImpl { - + public TitanicGrowth(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{G}"); - - this.getSpellAbility().addEffect(new BoostTargetEffect(4, 4, Duration.EndOfTurn)); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}"); + + Effect boostEffect = new BoostTargetEffect(4, 4, Duration.EndOfTurn); + boostEffect.setOutcome(Outcome.Benefit); + this.getSpellAbility().addEffect(boostEffect); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } - + private TitanicGrowth(final TitanicGrowth card) { super(card); } - + @Override public TitanicGrowth copy() { return new TitanicGrowth(this); } - + } diff --git a/Mage.Sets/src/mage/cards/t/TitansNest.java b/Mage.Sets/src/mage/cards/t/TitansNest.java index 1b6e3a2c292..fd7d9af5279 100644 --- a/Mage.Sets/src/mage/cards/t/TitansNest.java +++ b/Mage.Sets/src/mage/cards/t/TitansNest.java @@ -92,7 +92,7 @@ class TitansNestManaAbility extends ActivatedManaAbilityImpl { TitansNestManaAbility() { super(Zone.BATTLEFIELD, (BasicManaEffect) new BasicManaEffect( new TitansNestConditionalMana(), new CardsInControllerGraveyardCount()) - .setText("Add {C}. Spend this mana only to cast a colored spell without {X} in its mana cost."), + .setText("Add {C}. Spend this mana only to cast a spell that's one or more colors without {X} in its mana cost."), new ExileFromGraveCost(new TargetCardInYourGraveyard())); this.netMana.add(Mana.ColorlessMana(1)); } @@ -111,7 +111,7 @@ class TitansNestConditionalMana extends ConditionalMana { TitansNestConditionalMana() { super(Mana.ColorlessMana(1)); - staticText = "Spend this mana only to cast a colored spell without {X} in its mana cost."; + staticText = "Spend this mana only to cast a spell that's one or more colors without {X} in its mana cost."; addCondition(new TitansNestManaCondition()); } } diff --git a/Mage.Sets/src/mage/cards/t/TollOfTheInvasion.java b/Mage.Sets/src/mage/cards/t/TollOfTheInvasion.java index 88a1bf0e13c..6f738ed5705 100644 --- a/Mage.Sets/src/mage/cards/t/TollOfTheInvasion.java +++ b/Mage.Sets/src/mage/cards/t/TollOfTheInvasion.java @@ -5,7 +5,6 @@ import mage.abilities.effects.keyword.AmassEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.TargetController; import mage.filter.StaticFilters; import mage.target.common.TargetOpponent; @@ -20,13 +19,11 @@ public final class TollOfTheInvasion extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}"); // Target opponent reveals their hand. You choose a nonland card from it. That player discards that card. - this.getSpellAbility().addEffect(new DiscardCardYouChooseTargetEffect( - StaticFilters.FILTER_CARD_NON_LAND, TargetController.OPPONENT - )); + this.getSpellAbility().addEffect(new DiscardCardYouChooseTargetEffect(StaticFilters.FILTER_CARD_NON_LAND)); this.getSpellAbility().addTarget(new TargetOpponent()); // Amass 1. - this.getSpellAbility().addEffect(new AmassEffect(1)); + this.getSpellAbility().addEffect(new AmassEffect(1).concatBy("
")); } private TollOfTheInvasion(final TollOfTheInvasion card) { diff --git a/Mage.Sets/src/mage/cards/t/TomikDistinguishedAdvokist.java b/Mage.Sets/src/mage/cards/t/TomikDistinguishedAdvokist.java index 2250dea4f3d..6d9df04acaf 100644 --- a/Mage.Sets/src/mage/cards/t/TomikDistinguishedAdvokist.java +++ b/Mage.Sets/src/mage/cards/t/TomikDistinguishedAdvokist.java @@ -11,10 +11,10 @@ import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.FilterObject; import mage.filter.FilterStackObject; import mage.filter.StaticFilters; -import mage.filter.predicate.Predicate; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -28,6 +28,12 @@ import java.util.UUID; */ public final class TomikDistinguishedAdvokist extends CardImpl { + private static final FilterStackObject filter = new FilterStackObject(); + + static { + filter.add(TargetedByOpponentsPredicate.instance); + } + public TomikDistinguishedAdvokist(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}{W}"); @@ -41,8 +47,6 @@ public final class TomikDistinguishedAdvokist extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Lands on the battlefield and land cards in graveyards can't be the targets of spells or abilities your opponents control. - FilterObject filter = new FilterStackObject(); - filter.add(new TargetedByOpponentsPredicate(this.getId())); Ability ability = new SimpleStaticAbility(new CantBeTargetedAllEffect( StaticFilters.FILTER_LANDS, filter, Duration.WhileOnBattlefield ).setText("lands on the battlefield")); @@ -63,18 +67,13 @@ public final class TomikDistinguishedAdvokist extends CardImpl { } } -class TargetedByOpponentsPredicate implements Predicate { - - private final UUID sourceId; - - public TargetedByOpponentsPredicate(UUID sourceId) { - this.sourceId = sourceId; - } +enum TargetedByOpponentsPredicate implements ObjectSourcePlayerPredicate { + instance; @Override - public boolean apply(MageObject input, Game game) { - StackObject stackObject = game.getStack().getStackObject(input.getId()); - Permanent source = game.getPermanentOrLKIBattlefield(this.sourceId); + public boolean apply(ObjectSourcePlayer input, Game game) { + StackObject stackObject = game.getStack().getStackObject(input.getObject().getId()); + Permanent source = game.getPermanentOrLKIBattlefield(input.getSourceId()); if (stackObject != null && source != null) { Player controller = game.getPlayer(source.getControllerId()); return controller != null && game.isOpponent(controller, stackObject.getControllerId()); diff --git a/Mage.Sets/src/mage/cards/t/ToothCollector.java b/Mage.Sets/src/mage/cards/t/ToothCollector.java index 81071645fa7..462ac416315 100644 --- a/Mage.Sets/src/mage/cards/t/ToothCollector.java +++ b/Mage.Sets/src/mage/cards/t/ToothCollector.java @@ -15,8 +15,8 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.TargetController; import mage.constants.Zone; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.game.Game; @@ -29,12 +29,6 @@ import mage.target.common.TargetCreaturePermanent; */ public final class ToothCollector extends CardImpl { - private static final FilterCreaturePermanent FILTER = new FilterCreaturePermanent("creature an opponent controls"); - - static { - FILTER.add(TargetController.OPPONENT.getControllerPredicate()); - } - public ToothCollector(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); this.subtype.add(SubType.HUMAN); @@ -44,7 +38,7 @@ public final class ToothCollector extends CardImpl { // When Tooth Collector enters the battlefield, target creature an opponent controls gets -1/-1 until end of turn. Ability ability = new EntersBattlefieldTriggeredAbility(new BoostTargetEffect(-1, -1, Duration.EndOfTurn)); - ability.addTarget(new TargetCreaturePermanent(FILTER)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); this.addAbility(ability); // {Delirium — At the beginning of each opponent's upkeep, if there are four or more card types among cards in your graveyard, diff --git a/Mage.Sets/src/mage/cards/t/Topplegeist.java b/Mage.Sets/src/mage/cards/t/Topplegeist.java index 43e9d63d9f8..9e5c4bedd1b 100644 --- a/Mage.Sets/src/mage/cards/t/Topplegeist.java +++ b/Mage.Sets/src/mage/cards/t/Topplegeist.java @@ -15,8 +15,8 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; import mage.constants.Zone; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.game.Game; @@ -29,12 +29,6 @@ import mage.target.common.TargetCreaturePermanent; */ public final class Topplegeist extends CardImpl { - private static final FilterCreaturePermanent FILTER = new FilterCreaturePermanent("creature an opponent controls"); - - static { - FILTER.add(TargetController.OPPONENT.getControllerPredicate()); - } - public Topplegeist(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}"); this.subtype.add(SubType.SPIRIT); @@ -46,7 +40,7 @@ public final class Topplegeist extends CardImpl { // When Topplegeist enters the battlefield, tap target creature an opponent controls. Ability ability = new EntersBattlefieldTriggeredAbility(new TapTargetEffect()); - ability.addTarget(new TargetCreaturePermanent(FILTER)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); this.addAbility(ability); // Delirium — At the beginning of each opponent's upkeep, if there are four or more card types among cards in your graveyard, diff --git a/Mage.Sets/src/mage/cards/t/TorensFistOfTheAngels.java b/Mage.Sets/src/mage/cards/t/TorensFistOfTheAngels.java new file mode 100644 index 00000000000..78f226d0f56 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TorensFistOfTheAngels.java @@ -0,0 +1,49 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.TrainingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.StaticFilters; +import mage.game.permanent.token.HumanSoldierTrainingToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TorensFistOfTheAngels extends CardImpl { + + public TorensFistOfTheAngels(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Training + this.addAbility(new TrainingAbility()); + + // Whenever you cast a creature spell, create a 1/1 green and white Human Soldier creature token with training. + this.addAbility(new SpellCastControllerTriggeredAbility( + new CreateTokenEffect(new HumanSoldierTrainingToken()), + StaticFilters.FILTER_SPELL_A_CREATURE, false + )); + } + + private TorensFistOfTheAngels(final TorensFistOfTheAngels card) { + super(card); + } + + @Override + public TorensFistOfTheAngels copy() { + return new TorensFistOfTheAngels(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TormentedPariah.java b/Mage.Sets/src/mage/cards/t/TormentedPariah.java index 36a88c23c02..7d5997a1c5c 100644 --- a/Mage.Sets/src/mage/cards/t/TormentedPariah.java +++ b/Mage.Sets/src/mage/cards/t/TormentedPariah.java @@ -21,7 +21,6 @@ public final class TormentedPariah extends CardImpl { this.subtype.add(SubType.WARRIOR); this.subtype.add(SubType.WEREWOLF); - this.transformable = true; this.secondSideCardClazz = mage.cards.r.RampagingWerewolf.class; this.power = new MageInt(3); diff --git a/Mage.Sets/src/mage/cards/t/TormentorsHelm.java b/Mage.Sets/src/mage/cards/t/TormentorsHelm.java index f29690f44bd..a5d37895ecf 100644 --- a/Mage.Sets/src/mage/cards/t/TormentorsHelm.java +++ b/Mage.Sets/src/mage/cards/t/TormentorsHelm.java @@ -68,7 +68,7 @@ class TormentorsHelmTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.BLOCKER_DECLARED; + return event.getType() == GameEvent.EventType.CREATURE_BLOCKED; } @Override @@ -79,7 +79,7 @@ class TormentorsHelmTriggeredAbility extends TriggeredAbilityImpl { if (creature != null && creature.getId().equals(event.getTargetId())) { this.getEffects().clear(); TormentorsHelmEffect effect = new TormentorsHelmEffect(creature.getId()); - effect.setTargetPointer(new FixedTarget(event.getPlayerId(), game)); + effect.setTargetPointer(new FixedTarget(game.getCombat().getDefendingPlayerId(creature.getId(), game))); this.addEffect(effect); return true; } diff --git a/Mage.Sets/src/mage/cards/t/ToshiroUmezawa.java b/Mage.Sets/src/mage/cards/t/ToshiroUmezawa.java index 909fdca8692..7da68f1aa2e 100644 --- a/Mage.Sets/src/mage/cards/t/ToshiroUmezawa.java +++ b/Mage.Sets/src/mage/cards/t/ToshiroUmezawa.java @@ -13,10 +13,9 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.FilterCard; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.events.ZoneChangeEvent; import mage.game.stack.Spell; import mage.game.stack.StackObject; @@ -28,13 +27,9 @@ import mage.target.common.TargetCardInYourGraveyard; * @author LevelX2 */ public final class ToshiroUmezawa extends CardImpl { - - private static final FilterCreaturePermanent filter - = new FilterCreaturePermanent("a creature an opponent controls"); private static final FilterCard filterInstant = new FilterCard("instant card from your graveyard"); static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); filterInstant.add(CardType.INSTANT.getPredicate()); } @@ -52,7 +47,7 @@ public final class ToshiroUmezawa extends CardImpl { // Whenever a creature an opponent controls dies, you may cast target // instant card from your graveyard. If that card would be put into a // graveyard this turn, exile it instead. - Ability ability = new DiesCreatureTriggeredAbility(new ToshiroUmezawaEffect(), true, filter); + Ability ability = new DiesCreatureTriggeredAbility(new ToshiroUmezawaEffect(), true, StaticFilters.FILTER_OPPONENTS_PERMANENT_A_CREATURE); ability.addTarget(new TargetCardInYourGraveyard(1, 1, filterInstant)); this.addAbility(ability); @@ -72,8 +67,8 @@ class ToshiroUmezawaEffect extends OneShotEffect { public ToshiroUmezawaEffect() { super(Outcome.Benefit); - this.staticText = "cast target instant card from your graveyard. " - + "If that card would be put into a graveyard this turn, exile it instead"; + this.staticText = "you may cast target instant card from your graveyard. " + + "If that spell would be put into a graveyard this turn, exile it instead"; } public ToshiroUmezawaEffect(final ToshiroUmezawaEffect effect) { diff --git a/Mage.Sets/src/mage/cards/t/TouchTheSpiritRealm.java b/Mage.Sets/src/mage/cards/t/TouchTheSpiritRealm.java new file mode 100644 index 00000000000..a881b31e24e --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TouchTheSpiritRealm.java @@ -0,0 +1,95 @@ + +package mage.cards.t; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; +import mage.abilities.common.delayed.OnLeaveReturnExiledToBattlefieldAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.common.ExileUntilSourceLeavesEffect; +import mage.abilities.effects.common.ReturnToBattlefieldUnderOwnerControlTargetEffect; +import mage.abilities.keyword.ChannelAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; + +/** + * + * @author Addictiveme + */ +public final class TouchTheSpiritRealm extends CardImpl { + + private static final FilterPermanent filter = StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_CREATURE; + + public TouchTheSpiritRealm(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); + + // When Touch the Spirit Realm enters the battlefield, exile up to one target artifact or creature until + // Touch the Spirit Realm leaves the battlefield. + Ability ability = new EntersBattlefieldTriggeredAbility(new ExileUntilSourceLeavesEffect(filter.getMessage()) + .setText("exile up to one target " + filter.getMessage() + + " until {this} leaves the battlefield")); + ability.addTarget(new TargetPermanent(0, 1, filter, false)); + ability.addEffect(new CreateDelayedTriggeredAbilityEffect(new OnLeaveReturnExiledToBattlefieldAbility())); + this.addAbility(ability); + + // Channel - {1}{W}, Discard Touch the Spirit Realm: Exile target artifact or creature. + // Return it to the battlefield under its owner's control at the beginning of the next end step. + Ability channelAbility = new ChannelAbility("{1}{W}", new TouchTheSpiritRealmEffect()); + channelAbility.addTarget(new TargetPermanent(filter)); + this.addAbility(channelAbility); + } + + private TouchTheSpiritRealm(final TouchTheSpiritRealm card) { + super(card); + } + + @Override + public TouchTheSpiritRealm copy() { + return new TouchTheSpiritRealm(this); + } +} + +class TouchTheSpiritRealmEffect extends OneShotEffect { + + public TouchTheSpiritRealmEffect() { + super(Outcome.Detriment); + staticText = "Exile target artifact or creature. Return it to the battlefield under its owner's control at the beginning of the next end step"; + } + + private TouchTheSpiritRealmEffect(final TouchTheSpiritRealmEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (player == null || permanent == null) { + return false; + } + Card card = permanent.getMainCard(); + player.moveCardsToExile(permanent, source, game, true, CardUtil.getExileZoneId(game, source), CardUtil.getSourceName(game, source)); + ReturnToBattlefieldUnderOwnerControlTargetEffect returnEffect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, true); + returnEffect.setTargetPointer(new FixedTarget(card,game)); + game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(returnEffect), source); + return true; + } + + @Override + public TouchTheSpiritRealmEffect copy() { + return new TouchTheSpiritRealmEffect(this); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/t/TourachsCanticle.java b/Mage.Sets/src/mage/cards/t/TourachsCanticle.java index b2a6e752eab..117ddff379b 100644 --- a/Mage.Sets/src/mage/cards/t/TourachsCanticle.java +++ b/Mage.Sets/src/mage/cards/t/TourachsCanticle.java @@ -20,9 +20,7 @@ public final class TourachsCanticle extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{B}"); // Target opponent reveals their hand. You choose a card from it. That player discards that card, then discards a card at random. - this.getSpellAbility().addEffect(new DiscardCardYouChooseTargetEffect( - StaticFilters.FILTER_CARD, TargetController.OPPONENT - )); + this.getSpellAbility().addEffect(new DiscardCardYouChooseTargetEffect()); this.getSpellAbility().addEffect(new DiscardTargetEffect(1, true) .setText(", then discards a card at random")); this.getSpellAbility().addTarget(new TargetOpponent()); diff --git a/Mage.Sets/src/mage/cards/t/TovolarDireOverlord.java b/Mage.Sets/src/mage/cards/t/TovolarDireOverlord.java new file mode 100644 index 00000000000..6ec93b9729e --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TovolarDireOverlord.java @@ -0,0 +1,123 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.DealsDamageToAPlayerAllTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.DayboundAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TovolarDireOverlord extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent("a Wolf or Werewolf you control"); + + static { + filter.add(Predicates.or( + SubType.WOLF.getPredicate(), + SubType.WEREWOLF.getPredicate() + )); + } + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter, ComparisonType.MORE_THAN, 2); + private static final Hint hint = new ValueHint("Wolves and Werewolves you control", new PermanentsOnBattlefieldCount(filter)); + + public TovolarDireOverlord(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WEREWOLF); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + this.secondSideCardClazz = mage.cards.t.TovolarTheMidnightScourge.class; + + // Whenever a Wolf or Werewolf you control deals combat damage to a player, draw a card. + this.addAbility(new DealsDamageToAPlayerAllTriggeredAbility( + new DrawCardSourceControllerEffect(1), filter, + false, SetTargetPointer.NONE, true + )); + + // At the beginning of your upkeep, if you control three or more Wolves and/or Werewolves, it becomes night. Then transform any number of Human Werewolves you control. + this.addAbility(new ConditionalInterveningIfTriggeredAbility( + new BeginningOfUpkeepTriggeredAbility( + new TovolarDireOverlordEffect(), TargetController.YOU, false + ), condition, "At the beginning of your upkeep, if you control three or more Wolves " + + "and/or Werewolves, it becomes night. Then transform any number of Human Werewolves you control." + )); + + // Daybound + this.addAbility(new DayboundAbility()); + } + + private TovolarDireOverlord(final TovolarDireOverlord card) { + super(card); + } + + @Override + public TovolarDireOverlord copy() { + return new TovolarDireOverlord(this); + } +} + +class TovolarDireOverlordEffect extends OneShotEffect { + + private static final FilterPermanent filter = new FilterControlledPermanent("Human Werewolves you control"); + + static { + filter.add(SubType.HUMAN.getPredicate()); + filter.add(SubType.WEREWOLF.getPredicate()); + } + + TovolarDireOverlordEffect() { + super(Outcome.Benefit); + } + + private TovolarDireOverlordEffect(final TovolarDireOverlordEffect effect) { + super(effect); + } + + @Override + public TovolarDireOverlordEffect copy() { + return new TovolarDireOverlordEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + game.setDaytime(false); + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return true; + } + TargetPermanent target = new TargetPermanent(0, Integer.MAX_VALUE, filter, true); + player.choose(outcome, target, source.getControllerId(), game); + for (UUID permanentId : target.getTargets()) { + Permanent permanent = game.getPermanent(permanentId); + if (permanent != null) { + permanent.transform(source, game); + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/t/TovolarTheMidnightScourge.java b/Mage.Sets/src/mage/cards/t/TovolarTheMidnightScourge.java new file mode 100644 index 00000000000..667ce360431 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TovolarTheMidnightScourge.java @@ -0,0 +1,78 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DealsDamageToAPlayerAllTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.ManacostVariableValue; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.NightboundAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TovolarTheMidnightScourge extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent("a Wolf or Werewolf you control"); + + static { + filter.add(Predicates.or( + SubType.WOLF.getPredicate(), + SubType.WEREWOLF.getPredicate() + )); + } + + public TovolarTheMidnightScourge(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.WEREWOLF); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + this.color.setRed(true); + this.color.setGreen(true); + this.nightCard = true; + + // Whenever a Wolf or Werewolf you control deals combat damage to a player, draw a card. + this.addAbility(new DealsDamageToAPlayerAllTriggeredAbility( + new DrawCardSourceControllerEffect(1), filter, + false, SetTargetPointer.NONE, true + )); + + // {X}{R}{G}: Target Wolf or Werewolf you control gets +X/+0 and gains trample until end of turn. + Ability ability = new SimpleActivatedAbility(new GainAbilityTargetEffect( + TrampleAbility.getInstance(), Duration.EndOfTurn + ).setText("Target Wolf or Werewolf you control gets +X/+0"), new ManaCostsImpl<>("{X}{R}{G}")); + ability.addEffect(new BoostTargetEffect( + ManacostVariableValue.REGULAR, StaticValue.get(0), Duration.EndOfTurn + ).setText("and gains trample until end of turn")); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + + // Nightbound + this.addAbility(new NightboundAbility()); + } + + private TovolarTheMidnightScourge(final TovolarTheMidnightScourge card) { + super(card); + } + + @Override + public TovolarTheMidnightScourge copy() { + return new TovolarTheMidnightScourge(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TovolarsHuntmaster.java b/Mage.Sets/src/mage/cards/t/TovolarsHuntmaster.java index 728ea25305f..4e075bb75b4 100644 --- a/Mage.Sets/src/mage/cards/t/TovolarsHuntmaster.java +++ b/Mage.Sets/src/mage/cards/t/TovolarsHuntmaster.java @@ -4,7 +4,6 @@ import mage.MageInt; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.keyword.DayboundAbility; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -25,14 +24,12 @@ public final class TovolarsHuntmaster extends CardImpl { this.subtype.add(SubType.WEREWOLF); this.power = new MageInt(6); this.toughness = new MageInt(6); - this.transformable = true; this.secondSideCardClazz = mage.cards.t.TovolarsPackleader.class; // Whenever Tovolar's Huntmaster enters the battlefield, create two 2/2 green Wolf creature tokens. this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new WolfToken(), 2))); // Daybound - this.addAbility(new TransformAbility()); this.addAbility(new DayboundAbility()); } diff --git a/Mage.Sets/src/mage/cards/t/TovolarsMagehunter.java b/Mage.Sets/src/mage/cards/t/TovolarsMagehunter.java index 046a6da18ad..0fcba4dc888 100644 --- a/Mage.Sets/src/mage/cards/t/TovolarsMagehunter.java +++ b/Mage.Sets/src/mage/cards/t/TovolarsMagehunter.java @@ -30,7 +30,6 @@ public final class TovolarsMagehunter extends CardImpl { // this card is the second face of double-faced card this.nightCard = true; - this.transformable = true; // Whenever an opponent casts a spell, Tovolar's Magehunter deals 2 damage to that player. this.addAbility(new TovolarsMagehunterTriggeredAbility()); diff --git a/Mage.Sets/src/mage/cards/t/TovolarsPackleader.java b/Mage.Sets/src/mage/cards/t/TovolarsPackleader.java index ce1a3bff2a2..22f9b010bf5 100644 --- a/Mage.Sets/src/mage/cards/t/TovolarsPackleader.java +++ b/Mage.Sets/src/mage/cards/t/TovolarsPackleader.java @@ -45,7 +45,6 @@ public final class TovolarsPackleader extends CardImpl { this.power = new MageInt(7); this.toughness = new MageInt(7); this.color.setGreen(true); - this.transformable = true; this.nightCard = true; // Whenever Tovolar's Packleader enters the battlefield or attacks, create two 2/2 green Wolf creature tokens. @@ -54,7 +53,7 @@ public final class TovolarsPackleader extends CardImpl { )); // {2}{G}{G}: Another target Wolf or Werewolf you control fights target creature you don't control. - Ability ability = new SimpleActivatedAbility(new FightTargetsEffect( + Ability ability = new SimpleActivatedAbility(new FightTargetsEffect().setText( "another target Wolf or Werewolf you control fights target creature you don't control" ), new ManaCostsImpl<>("{2}{G}{G}")); ability.addTarget(new TargetPermanent(filter)); diff --git a/Mage.Sets/src/mage/cards/t/TowashiGuideBot.java b/Mage.Sets/src/mage/cards/t/TowashiGuideBot.java new file mode 100644 index 00000000000..27ddb16b728 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TowashiGuideBot.java @@ -0,0 +1,89 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.CostAdjuster; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.InfoEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.permanent.ModifiedPredicate; +import mage.game.Game; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TowashiGuideBot extends CardImpl { + + public TowashiGuideBot(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{4}"); + + this.subtype.add(SubType.CONSTRUCT); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // When Towashi Guide-Bot enters the battlefield, put a +1/+1 counter on target creature you control. + Ability ability = new EntersBattlefieldTriggeredAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance()) + ); + ability.addTarget(new TargetControlledCreaturePermanent()); + this.addAbility(ability); + + // {4}, {T}: Draw a card. This ability costs {1} less to activate for each modified creature you control. + ability = new SimpleActivatedAbility(new DrawCardSourceControllerEffect(1), new GenericManaCost(4)); + ability.addEffect(new InfoEffect("This ability costs {1} less to activate for each modified creature you control")); + ability.addCost(new TapSourceCost()); + ability.setCostAdjuster(TowashiGuideBotAdjuster.instance); + this.addAbility(ability.addHint(TowashiGuideBotAdjuster.getHint())); + } + + private TowashiGuideBot(final TowashiGuideBot card) { + super(card); + } + + @Override + public TowashiGuideBot copy() { + return new TowashiGuideBot(this); + } +} + +enum TowashiGuideBotAdjuster implements CostAdjuster { + instance; + private static final FilterPermanent filter = new FilterControlledCreaturePermanent(); + + static { + filter.add(ModifiedPredicate.instance); + } + + private static final Hint hint = new ValueHint( + "Modified creatures you control", new PermanentsOnBattlefieldCount(filter) + ); + + public static Hint getHint() { + return hint; + } + + @Override + public void adjustCosts(Ability ability, Game game) { + CardUtil.reduceCost(ability, game.getBattlefield().count( + filter, ability.getSourceId(), ability.getControllerId(), game + )); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TowashiSongshaper.java b/Mage.Sets/src/mage/cards/t/TowashiSongshaper.java new file mode 100644 index 00000000000..a210fe0c4cd --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TowashiSongshaper.java @@ -0,0 +1,50 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledArtifactPermanent; +import mage.filter.predicate.mageobject.AnotherPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TowashiSongshaper extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledArtifactPermanent("another artifact"); + + static { + filter.add(AnotherPredicate.instance); + } + + public TowashiSongshaper(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{1}{R}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ARTIFICER); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Whenever another artifact enters the battlefield under your control, Towashi Songshaper gets +1/+0 until end of turn. + this.addAbility(new EntersBattlefieldControlledTriggeredAbility( + new BoostSourceEffect(1, 0, Duration.EndOfTurn), filter + )); + } + + private TowashiSongshaper(final TowashiSongshaper card) { + super(card); + } + + @Override + public TowashiSongshaper copy() { + return new TowashiSongshaper(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TowerAbove.java b/Mage.Sets/src/mage/cards/t/TowerAbove.java index 09f23eed1e5..835602e56f7 100644 --- a/Mage.Sets/src/mage/cards/t/TowerAbove.java +++ b/Mage.Sets/src/mage/cards/t/TowerAbove.java @@ -1,4 +1,3 @@ - package mage.cards.t; import java.util.UUID; @@ -19,7 +18,6 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; import mage.target.targetpointer.FixedTarget; @@ -31,8 +29,7 @@ import mage.target.targetpointer.FixedTarget; public final class TowerAbove extends CardImpl { public TowerAbove(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2/G}{2/G}{2/G}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2/G}{2/G}{2/G}"); // ({2G} can be paid with any two mana or with {G}. This card's converted mana cost is 6.) // Until end of turn, target creature gets +4/+4 and gains trample, wither, and "When this creature attacks, target creature blocks it this turn if able." @@ -73,10 +70,10 @@ class TowerAboveEffect extends OneShotEffect { ContinuousEffect effect2 = new GainAbilityTargetEffect(TrampleAbility.getInstance(), Duration.EndOfTurn); ContinuousEffect effect3 = new GainAbilityTargetEffect(WitherAbility.getInstance(), Duration.EndOfTurn); ContinuousEffect effect4 = new GainAbilityTargetEffect(new TowerAboveTriggeredAbility(), Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(target.getId())); - effect2.setTargetPointer(new FixedTarget(target.getId())); - effect3.setTargetPointer(new FixedTarget(target.getId())); - effect4.setTargetPointer(new FixedTarget(target.getId())); + effect.setTargetPointer(new FixedTarget(target.getId(), game)); + effect2.setTargetPointer(new FixedTarget(target.getId(), game)); + effect3.setTargetPointer(new FixedTarget(target.getId(), game)); + effect4.setTargetPointer(new FixedTarget(target.getId(), game)); effect4.setText(""); game.addEffect(effect, source); game.addEffect(effect2, source); diff --git a/Mage.Sets/src/mage/cards/t/TownGossipmonger.java b/Mage.Sets/src/mage/cards/t/TownGossipmonger.java index 672fac061f8..1d032506c25 100644 --- a/Mage.Sets/src/mage/cards/t/TownGossipmonger.java +++ b/Mage.Sets/src/mage/cards/t/TownGossipmonger.java @@ -36,12 +36,11 @@ public final class TownGossipmonger extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); - this.transformable = true; this.secondSideCardClazz = mage.cards.i.IncitedRabble.class; // {T}, Tap an untapped creature you control: Transform Town Gossipmonger. this.addAbility(new TransformAbility()); - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new TransformSourceEffect(true), new TapSourceCost()); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new TransformSourceEffect(), new TapSourceCost()); ability.addCost(new TapTargetCost(new TargetControlledCreaturePermanent(1, 1, filter, true))); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/t/ToxicDeluge.java b/Mage.Sets/src/mage/cards/t/ToxicDeluge.java index 61598d39d14..ad79c962218 100644 --- a/Mage.Sets/src/mage/cards/t/ToxicDeluge.java +++ b/Mage.Sets/src/mage/cards/t/ToxicDeluge.java @@ -1,4 +1,3 @@ - package mage.cards.t; import java.util.UUID; @@ -11,7 +10,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; /** * @@ -19,16 +18,16 @@ import mage.filter.common.FilterCreaturePermanent; */ public final class ToxicDeluge extends CardImpl { + private static final DynamicValue xValue = new SignInversionDynamicValue(GetXValue.instance); + public ToxicDeluge(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{B}"); - // As an additional cost to cast Toxic Deluge, pay X life. this.getSpellAbility().addCost(new PayVariableLifeCost(true)); + // All creatures get -X/-X until end of turn. - DynamicValue xValue = new SignInversionDynamicValue(GetXValue.instance); - this.getSpellAbility().addEffect(new BoostAllEffect(xValue, xValue, Duration.EndOfTurn, new FilterCreaturePermanent("All creatures"), false, - null, true)); + this.getSpellAbility().addEffect(new BoostAllEffect(xValue, xValue, Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_ALL_CREATURES, false, null, true)); } private ToxicDeluge(final ToxicDeluge card) { diff --git a/Mage.Sets/src/mage/cards/t/ToxicScorpion.java b/Mage.Sets/src/mage/cards/t/ToxicScorpion.java new file mode 100644 index 00000000000..f4188486466 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/ToxicScorpion.java @@ -0,0 +1,58 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.DeathtouchAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ToxicScorpion extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("another target creature you control"); + + static { + filter.add(AnotherPredicate.instance); + } + + public ToxicScorpion(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); + + this.subtype.add(SubType.SCORPION); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Deathtouch + this.addAbility(DeathtouchAbility.getInstance()); + + // When Toxic Scorpion enters the battlefield, another target creature you control gains deathtouch until end of turn. + Ability ability = new EntersBattlefieldTriggeredAbility(new GainAbilityTargetEffect( + DeathtouchAbility.getInstance(), Duration.EndOfTurn + )); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + } + + private ToxicScorpion(final ToxicScorpion card) { + super(card); + } + + @Override + public ToxicScorpion copy() { + return new ToxicScorpion(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/ToxicStench.java b/Mage.Sets/src/mage/cards/t/ToxicStench.java index b828236bec6..314fbf881b1 100644 --- a/Mage.Sets/src/mage/cards/t/ToxicStench.java +++ b/Mage.Sets/src/mage/cards/t/ToxicStench.java @@ -1,8 +1,6 @@ - package mage.cards.t; import java.util.UUID; -import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.condition.InvertCondition; import mage.abilities.condition.common.CardsInControllerGraveyardCondition; @@ -16,9 +14,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ColorPredicate; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; @@ -30,12 +26,6 @@ import mage.target.targetpointer.FixedTarget; */ public final class ToxicStench extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public ToxicStench(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{B}"); @@ -51,7 +41,7 @@ public final class ToxicStench extends CardImpl { new CardsInControllerGraveyardCondition(7), "

Threshold — If seven or more cards are in your graveyard, instead destroy that creature. It can't be regenerated.")); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); } private ToxicStench(final ToxicStench card) { diff --git a/Mage.Sets/src/mage/cards/t/ToxrillTheCorrosive.java b/Mage.Sets/src/mage/cards/t/ToxrillTheCorrosive.java new file mode 100644 index 00000000000..18b469e1022 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/ToxrillTheCorrosive.java @@ -0,0 +1,113 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.common.DiesCreatureTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.counter.AddCountersAllEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.SlugToken; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ToxrillTheCorrosive extends CardImpl { + + private static final FilterPermanent filter + = new FilterCreaturePermanent("a creature you don't control with a slime counter on it"); + private static final FilterControlledPermanent filter2 + = new FilterControlledPermanent(SubType.SLUG, "a Slug"); + + static { + filter.add(TargetController.NOT_YOU.getControllerPredicate()); + filter.add(CounterType.SLIME.getPredicate()); + } + + public ToxrillTheCorrosive(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{B}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.SLUG); + this.subtype.add(SubType.HORROR); + this.power = new MageInt(7); + this.toughness = new MageInt(7); + + // At the beginning of each end step, put a slime counter on each creature you don't control. + this.addAbility(new BeginningOfEndStepTriggeredAbility(new AddCountersAllEffect( + CounterType.SLIME.createInstance(), StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL + ), TargetController.ANY, false)); + + // Creatures you don't control get -1/-1 for each slime counter on them. + this.addAbility(new SimpleStaticAbility(new ToxrillTheCorrosiveEffect())); + + // Whenever a creature you don't control with a slime counter on it dies, create a 1/1 black Slug creature token. + this.addAbility(new DiesCreatureTriggeredAbility( + new CreateTokenEffect(new SlugToken()), false, filter + )); + + // {U}{B}, Sacrifice a Slug: Draw a card. + Ability ability = new SimpleActivatedAbility( + new DrawCardSourceControllerEffect(1), new ManaCostsImpl<>("{U}{B}") + ); + ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(filter2))); + this.addAbility(ability); + } + + private ToxrillTheCorrosive(final ToxrillTheCorrosive card) { + super(card); + } + + @Override + public ToxrillTheCorrosive copy() { + return new ToxrillTheCorrosive(this); + } +} + +class ToxrillTheCorrosiveEffect extends ContinuousEffectImpl { + + ToxrillTheCorrosiveEffect() { + super(Duration.WhileOnBattlefield, Layer.PTChangingEffects_7, SubLayer.ModifyPT_7c, Outcome.UnboostCreature); + staticText = "creatures you don't control get -1/-1 for each slime counter on them"; + } + + private ToxrillTheCorrosiveEffect(final ToxrillTheCorrosiveEffect effect) { + super(effect); + } + + @Override + public ToxrillTheCorrosiveEffect copy() { + return new ToxrillTheCorrosiveEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + for (Permanent permanent : game.getBattlefield().getActivePermanents( + StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL, + source.getControllerId(), source.getSourceId(), game + )) { + int counter = permanent.getCounters(game).getCount(CounterType.SLIME); + permanent.getPower().boostValue(-counter); + permanent.getToughness().boostValue(-counter); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/t/TraitorsRoar.java b/Mage.Sets/src/mage/cards/t/TraitorsRoar.java index 387646e7765..d789661e71b 100644 --- a/Mage.Sets/src/mage/cards/t/TraitorsRoar.java +++ b/Mage.Sets/src/mage/cards/t/TraitorsRoar.java @@ -35,7 +35,7 @@ public final class TraitorsRoar extends CardImpl { this.getSpellAbility().addEffect(new TraitorsRoarEffect()); // Conspire - this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.ONE)); + this.addAbility(new ConspireAbility(ConspireAbility.ConspireTargets.ONE)); } diff --git a/Mage.Sets/src/mage/cards/t/TravelingMinister.java b/Mage.Sets/src/mage/cards/t/TravelingMinister.java new file mode 100644 index 00000000000..1323c0b1206 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TravelingMinister.java @@ -0,0 +1,47 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TravelingMinister extends CardImpl { + + public TravelingMinister(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // {T}: Target creature gets +1/+0 until end of turn. You gain 1 life. Activate only as a sorcery. + Ability ability = new ActivateAsSorceryActivatedAbility( + new BoostTargetEffect(1, 0), new TapSourceCost() + ); + ability.addEffect(new GainLifeEffect(1)); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + private TravelingMinister(final TravelingMinister card) { + super(card); + } + + @Override + public TravelingMinister copy() { + return new TravelingMinister(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TreasureMap.java b/Mage.Sets/src/mage/cards/t/TreasureMap.java index d6e9a374f01..f641c5d6b1a 100644 --- a/Mage.Sets/src/mage/cards/t/TreasureMap.java +++ b/Mage.Sets/src/mage/cards/t/TreasureMap.java @@ -29,7 +29,6 @@ public final class TreasureMap extends CardImpl { public TreasureMap(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); - this.transformable = true; this.secondSideCardClazz = TreasureCove.class; // {1}, {T}: Scry 1. Put a landmark counter on Treasure Map. Then if there are three or more landmark counters on it, remove those counters, transform Treasure Map, and create three colorless Treasure artifact tokens with "{T}, Sacrifice this artifact: Add one mana of any color." @@ -56,7 +55,7 @@ class TreasureMapEffect extends OneShotEffect { this.staticText = "Scry 1. Put a landmark counter on {this}. " + "Then if there are three or more landmark counters on it, " + "remove those counters, transform {this}, and create " - + "three colorless Treasure artifact tokens with \"{T}, Sacrifice this artifact: Add one mana of any color.\""; + + "three Treasure tokens"; } TreasureMapEffect(final TreasureMapEffect effect) { @@ -79,7 +78,7 @@ class TreasureMapEffect extends OneShotEffect { int counters = permanent.getCounters(game).getCount(CounterType.LANDMARK); if (counters > 2) { permanent.removeCounters("landmark", counters, source, game); - new TransformSourceEffect(true).apply(game, source); + new TransformSourceEffect().apply(game, source); new CreateTokenEffect(new TreasureToken(), 3).apply(game, source); } return true; diff --git a/Mage.Sets/src/mage/cards/t/TreetopBracers.java b/Mage.Sets/src/mage/cards/t/TreetopBracers.java index 1b7e9e577ef..c4f6dc72a55 100644 --- a/Mage.Sets/src/mage/cards/t/TreetopBracers.java +++ b/Mage.Sets/src/mage/cards/t/TreetopBracers.java @@ -2,8 +2,8 @@ package mage.cards.t; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.RestrictionEffect; import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.combat.CantBeBlockedByCreaturesAttachedEffect; import mage.abilities.effects.common.continuous.BoostEnchantedEffect; import mage.abilities.keyword.EnchantAbility; import mage.abilities.keyword.FlyingAbility; @@ -11,8 +11,9 @@ import mage.abilities.keyword.ReachAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.AbilityPredicate; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; @@ -23,6 +24,12 @@ import java.util.UUID; */ public final class TreetopBracers extends CardImpl { + private static final FilterCreaturePermanent onlyFlyingCreatures = new FilterCreaturePermanent("except by creatures with flying"); + + static { + onlyFlyingCreatures.add(Predicates.not(new AbilityPredicate(FlyingAbility.class))); + } + public TreetopBracers(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{G}"); this.subtype.add(SubType.AURA); @@ -35,8 +42,8 @@ public final class TreetopBracers extends CardImpl { this.addAbility(ability); // Enchanted creature gets +1/+1 and can't be blocked except by creatures with flying. - ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(1, 1, Duration.WhileOnBattlefield)); - ability.addEffect(new TreetopBracersRestrictEffect()); + ability = new SimpleStaticAbility(new BoostEnchantedEffect(1, 1, Duration.WhileOnBattlefield)); + ability.addEffect(new CantBeBlockedByCreaturesAttachedEffect(Duration.WhileOnBattlefield, onlyFlyingCreatures, AttachmentType.AURA).setText("and can't be blocked except by creatures with flying")); this.addAbility(ability); } @@ -49,35 +56,3 @@ public final class TreetopBracers extends CardImpl { return new TreetopBracers(this); } } - -class TreetopBracersRestrictEffect extends RestrictionEffect { - - public TreetopBracersRestrictEffect() { - super(Duration.WhileOnBattlefield); - staticText = "and can't be blocked except by creatures with flying"; - } - - public TreetopBracersRestrictEffect(final TreetopBracersRestrictEffect effect) { - super(effect); - } - - @Override - public boolean applies(Permanent permanent, Ability source, Game game) { - Permanent equipment = game.getPermanent(source.getSourceId()); - if (equipment != null && equipment.getAttachedTo() != null) { - Permanent equipped = game.getPermanent(equipment.getAttachedTo()); - return permanent != null && permanent.getId().equals(equipped.getId()); - } - return false; - } - - @Override - public boolean canBeBlocked(Permanent attacker, Permanent blocker, Ability source, Game game, boolean canUseChooseDialogs) { - return blocker.getAbilities().contains(FlyingAbility.getInstance()) || blocker.getAbilities().contains(ReachAbility.getInstance()); - } - - @Override - public TreetopBracersRestrictEffect copy() { - return new TreetopBracersRestrictEffect(this); - } -} diff --git a/Mage.Sets/src/mage/cards/t/TrespassersCurse.java b/Mage.Sets/src/mage/cards/t/TrespassersCurse.java index 255ddfa8b87..31b9ecd8540 100644 --- a/Mage.Sets/src/mage/cards/t/TrespassersCurse.java +++ b/Mage.Sets/src/mage/cards/t/TrespassersCurse.java @@ -1,4 +1,3 @@ - package mage.cards.t; import mage.abilities.Ability; @@ -77,7 +76,7 @@ class TrespassersCurseTriggeredAbility extends TriggeredAbilityImpl { && game.getControllerId(event.getTargetId()).equals(enchantment.getAttachedTo()) && game.getPermanent(event.getTargetId()).isCreature(game)) { for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(enchantment.getAttachedTo())); + effect.setTargetPointer(new FixedTarget(enchantment.getAttachedTo(), game)); } return true; } @@ -86,7 +85,7 @@ class TrespassersCurseTriggeredAbility extends TriggeredAbilityImpl { @Override public String getTriggerPhrase() { - return "Whenever a creature enters the battlefield under enchanted player's control, " ; + return "Whenever a creature enters the battlefield under enchanted player's control, "; } @Override diff --git a/Mage.Sets/src/mage/cards/t/TrespassingSouleater.java b/Mage.Sets/src/mage/cards/t/TrespassingSouleater.java index 6dc1726a4ad..6457bd0c574 100644 --- a/Mage.Sets/src/mage/cards/t/TrespassingSouleater.java +++ b/Mage.Sets/src/mage/cards/t/TrespassingSouleater.java @@ -1,27 +1,24 @@ - package mage.cards.t; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.mana.PhyrexianManaCost; +import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.combat.CantBeBlockedSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.ColoredManaSymbol; import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.SubType; + +import java.util.UUID; /** - * * @author North */ public final class TrespassingSouleater extends CardImpl { public TrespassingSouleater(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{3}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}"); this.subtype.add(SubType.PHYREXIAN); this.subtype.add(SubType.CONSTRUCT); @@ -29,9 +26,9 @@ public final class TrespassingSouleater extends CardImpl { this.toughness = new MageInt(2); // {U/P}: Trespassing Souleater can't be blocked this turn. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, - new CantBeBlockedSourceEffect(Duration.EndOfTurn), - new PhyrexianManaCost(ColoredManaSymbol.U))); + this.addAbility(new SimpleActivatedAbility( + new CantBeBlockedSourceEffect(Duration.EndOfTurn), new ManaCostsImpl<>("{U/P}") + )); } private TrespassingSouleater(final TrespassingSouleater card) { diff --git a/Mage.Sets/src/mage/cards/t/TrialOfAmbition.java b/Mage.Sets/src/mage/cards/t/TrialOfAmbition.java index 0845dfbb300..c6a4dfe0df7 100644 --- a/Mage.Sets/src/mage/cards/t/TrialOfAmbition.java +++ b/Mage.Sets/src/mage/cards/t/TrialOfAmbition.java @@ -31,7 +31,7 @@ public final class TrialOfAmbition extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}"); // When Trial of Ambition enters the battlefield, target opponent sacrifices a creature. - Ability ability = new EntersBattlefieldTriggeredAbility(new SacrificeEffect(StaticFilters.FILTER_PERMANENT_CREATURE_A, 1, "target opponent")); + Ability ability = new EntersBattlefieldTriggeredAbility(new SacrificeEffect(StaticFilters.FILTER_PERMANENT_A_CREATURE, 1, "target opponent")); ability.addTarget(new TargetOpponent()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/t/TrialOfKnowledge.java b/Mage.Sets/src/mage/cards/t/TrialOfKnowledge.java index ed566e61819..0e5eceeade4 100644 --- a/Mage.Sets/src/mage/cards/t/TrialOfKnowledge.java +++ b/Mage.Sets/src/mage/cards/t/TrialOfKnowledge.java @@ -32,7 +32,7 @@ public final class TrialOfKnowledge extends CardImpl { // When a Cartouche enters the battlefield under your control, return Trial of Knowledge to its owner's hand. this.addAbility(new EntersBattlefieldControlledTriggeredAbility(new ReturnToHandSourceEffect(), filter, - "When a Cartouche enters the battlefield under your control, return {this} to its owner's hand")); + "When a Cartouche enters the battlefield under your control, return {this} to its owner's hand.")); } private TrialOfKnowledge(final TrialOfKnowledge card) { diff --git a/Mage.Sets/src/mage/cards/t/TriangleOfWar.java b/Mage.Sets/src/mage/cards/t/TriangleOfWar.java index c866fd9405b..19a6ac1601e 100644 --- a/Mage.Sets/src/mage/cards/t/TriangleOfWar.java +++ b/Mage.Sets/src/mage/cards/t/TriangleOfWar.java @@ -10,9 +10,8 @@ import mage.abilities.effects.common.FightTargetsEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.TargetController; import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; @@ -21,12 +20,6 @@ import mage.target.common.TargetCreaturePermanent; * @author fireshoes */ public final class TriangleOfWar extends CardImpl { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } public TriangleOfWar(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{1}"); @@ -37,7 +30,7 @@ public final class TriangleOfWar extends CardImpl { new ManaCostsImpl("{2}")); ability.addCost(new SacrificeSourceCost()); ability.addTarget(new TargetControlledCreaturePermanent()); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/t/TributeToHorobi.java b/Mage.Sets/src/mage/cards/t/TributeToHorobi.java new file mode 100644 index 00000000000..d6379f3d6ed --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TributeToHorobi.java @@ -0,0 +1,80 @@ +package mage.cards.t; + +import java.util.UUID; + +import mage.abilities.Ability; +import mage.abilities.common.SagaAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ExileSagaAndReturnTransformedEffect; +import mage.abilities.keyword.TransformAbility; +import mage.constants.Outcome; +import mage.constants.SagaChapter; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.game.Game; +import mage.game.permanent.token.RatRogueToken; + +/** + * + * @author weirddan455 + */ +public final class TributeToHorobi extends CardImpl { + + public TributeToHorobi(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}"); + + this.subtype.add(SubType.SAGA); + this.secondSideCardClazz = mage.cards.e.EchoOfDeathsWail.class; + + // (As this Saga enters and after your draw step, add a lore counter.) + SagaAbility sagaAbility = new SagaAbility(this); + + // I, II — Each opponent creates a 1/1 black Rat Rouge creature token. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_II, new TributeToHorobiTokenEffect()); + + // III — Exile this Saga, then return it to the battlefield transformed under your control. + this.addAbility(new TransformAbility()); + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, new ExileSagaAndReturnTransformedEffect()); + + this.addAbility(sagaAbility); + } + + private TributeToHorobi(final TributeToHorobi card) { + super(card); + } + + @Override + public TributeToHorobi copy() { + return new TributeToHorobi(this); + } +} + +class TributeToHorobiTokenEffect extends OneShotEffect { + + public TributeToHorobiTokenEffect() { + super(Outcome.PutCreatureInPlay); + this.staticText = "Each opponent creates a 1/1 black Rat Rogue creature token"; + } + + private TributeToHorobiTokenEffect(final TributeToHorobiTokenEffect effect) { + super(effect); + } + + @Override + public TributeToHorobiTokenEffect copy() { + return new TributeToHorobiTokenEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + boolean success = false; + for (UUID opponentId : game.getOpponents(source.getControllerId())) { + if (new RatRogueToken().putOntoBattlefield(1, game, source, opponentId)) { + success = true; + } + } + return success; + } +} diff --git a/Mage.Sets/src/mage/cards/t/TritonTactics.java b/Mage.Sets/src/mage/cards/t/TritonTactics.java index 900ad9e83a5..6c3ebedf5fe 100644 --- a/Mage.Sets/src/mage/cards/t/TritonTactics.java +++ b/Mage.Sets/src/mage/cards/t/TritonTactics.java @@ -1,12 +1,9 @@ - package mage.cards.t; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; import mage.abilities.effects.common.DontUntapInControllersNextUntapStepTargetEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.cards.CardImpl; @@ -15,37 +12,31 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.WatcherScope; -import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; import mage.target.targetpointer.FixedTargets; -import mage.util.CardUtil; import mage.watchers.Watcher; import java.util.*; +import java.util.stream.Collectors; /** - * @author LevelX2 + * @author TheElk801 */ public final class TritonTactics extends CardImpl { public TritonTactics(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{U}"); - // Up to two target creatures each get +0/+3 until end of turn. Untap those creatures. - // At this turn's next end of combat, tap each creature that was blocked by one of those - // creatures this turn and it doesn't untap during its controller's next untap step. - Effect effect = new BoostTargetEffect(0, 3, Duration.EndOfTurn); - effect.setText("Up to two target creatures each get +0/+3 until end of turn"); - this.getSpellAbility().addEffect(effect); + // Up to two target creatures each get +0/+3 until end of turn. Untap those creatures. At this turn's next end of combat, tap each creature that was blocked by one of those creatures this turn and it doesn't untap during its controller's next untap step. + this.getSpellAbility().addEffect(new BoostTargetEffect( + 0, 3, Duration.EndOfTurn + ).setText("Up to two target creatures each get +0/+3 until end of turn")); + this.getSpellAbility().addEffect(new TritonTacticsUntapEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanent(0, 2)); - this.getSpellAbility().addEffect(new TritonTacticsUntapTargetEffect()); - this.getSpellAbility().addEffect(new CreateDelayedTriggeredAbilityEffect(new TritonTacticsTriggeredAbility())); - - this.getSpellAbility().addWatcher(new BlockedCreaturesWatcher()); - + this.getSpellAbility().addWatcher(new TritonTacticsWatcher()); } private TritonTactics(final TritonTactics card) { @@ -58,61 +49,57 @@ public final class TritonTactics extends CardImpl { } } -class TritonTacticsUntapTargetEffect extends OneShotEffect { +class TritonTacticsUntapEffect extends OneShotEffect { - public TritonTacticsUntapTargetEffect() { + public TritonTacticsUntapEffect() { super(Outcome.Untap); - staticText = "Untap those creatures"; + staticText = "untap those creatures. At this turn's next end of combat, tap each creature that was blocked " + + "by one of those creatures this turn and it doesn't untap during its controller's next untap step"; } - public TritonTacticsUntapTargetEffect(final TritonTacticsUntapTargetEffect effect) { + public TritonTacticsUntapEffect(final TritonTacticsUntapEffect effect) { super(effect); } @Override - public TritonTacticsUntapTargetEffect copy() { - return new TritonTacticsUntapTargetEffect(this); + public TritonTacticsUntapEffect copy() { + return new TritonTacticsUntapEffect(this); } @Override public boolean apply(Game game, Ability source) { - Set targets = new HashSet<>(); - for (UUID target : targetPointer.getTargets(game, source)) { - Permanent permanent = game.getPermanent(target); - if (permanent != null) { - permanent.untap(game); - targets.add(CardUtil.getCardZoneString("", permanent.getId(), game)); - } + List permanents = getTargetPointer() + .getTargets(game, source) + .stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + if (permanents.isEmpty()) { + return false; } - if (!targets.isEmpty()) { - // save the targets for the watcher in a map with zone change counter (as the card is recast during combat it's neccessary to save with zone change counter) - Map> targetMap; - Object object = game.getState().getValue("targets" + source.getSourceId()); - if (object instanceof Map) { - targetMap = (Map>) object; - } else { - targetMap = new HashMap<>(); - } - targetMap.put(game.getCard(source.getSourceId()).getZoneChangeCounter(game), targets); - if (object == null) { - game.getState().setValue("targets" + source.getSourceId().toString(), targetMap); - } + for (Permanent permanent : permanents) { + permanent.untap(game); } + game.addDelayedTriggeredAbility(new TritonTacticsDelayedTriggeredAbility(permanents, game), source); return true; } - } -class TritonTacticsTriggeredAbility extends DelayedTriggeredAbility { +class TritonTacticsDelayedTriggeredAbility extends DelayedTriggeredAbility { - public TritonTacticsTriggeredAbility() { - super(new TritonTacticsEndOfCombatEffect(), Duration.EndOfTurn, true); + TritonTacticsDelayedTriggeredAbility(List permanents, Game game) { + super(new TritonTacticsTapEffect(permanents, game), Duration.EndOfTurn, true, false); } - public TritonTacticsTriggeredAbility(TritonTacticsTriggeredAbility ability) { + private TritonTacticsDelayedTriggeredAbility(final TritonTacticsDelayedTriggeredAbility ability) { super(ability); } + @Override + public TritonTacticsDelayedTriggeredAbility copy() { + return new TritonTacticsDelayedTriggeredAbility(this); + } + @Override public boolean checkEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.END_COMBAT_STEP_PRE; @@ -124,101 +111,79 @@ class TritonTacticsTriggeredAbility extends DelayedTriggeredAbility { } @Override - public TritonTacticsTriggeredAbility copy() { - return new TritonTacticsTriggeredAbility(this); - } - - @Override - public String getTriggerPhrase() { - return "At this turn's next end of combat, "; + public String getRule() { + return "At this turn's next end of combat, tap each creature that was blocked " + + "by one of those creatures this turn and it doesn't untap during its controller's next untap step."; } } -class TritonTacticsEndOfCombatEffect extends OneShotEffect { +class TritonTacticsTapEffect extends OneShotEffect { - public TritonTacticsEndOfCombatEffect() { - super(Outcome.Benefit); - this.staticText = "tap each creature that was blocked by one of those creatures this turn and it doesn't untap during its controller's next untap step"; + private final Set morSet = new HashSet<>(); + + TritonTacticsTapEffect(List permanents, Game game) { + super(Outcome.Tap); + permanents + .stream() + .map(permanent -> new MageObjectReference(permanent, game)) + .forEach(morSet::add); } - public TritonTacticsEndOfCombatEffect(final TritonTacticsEndOfCombatEffect effect) { + private TritonTacticsTapEffect(final TritonTacticsTapEffect effect) { super(effect); + morSet.addAll(effect.morSet); } @Override - public TritonTacticsEndOfCombatEffect copy() { - return new TritonTacticsEndOfCombatEffect(this); + public TritonTacticsTapEffect copy() { + return new TritonTacticsTapEffect(this); } @Override public boolean apply(Game game, Ability source) { - Map> attackerMap = null; - Object object = game.getState().getValue("blockedAttackers" + source.getSourceId()); - if (object instanceof Map) { - attackerMap = (Map>) object; - for (Set attackerSet : attackerMap.values()) { - List doNotUntapNextUntapStep = new ArrayList<>(); - for (Permanent creature : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), game)) { - if (attackerSet.contains(CardUtil.getCardZoneString(null, creature.getId(), game))) { - // tap creature and add the not untap effect - creature.tap(source, game); - doNotUntapNextUntapStep.add(creature); - game.informPlayers("Triton Tactics: " + creature.getName() + " doesn't untap during its controller's next untap step"); - } - } - if (!doNotUntapNextUntapStep.isEmpty()) { - ContinuousEffect effect = new DontUntapInControllersNextUntapStepTargetEffect("This creature"); - effect.setTargetPointer(new FixedTargets(doNotUntapNextUntapStep, game)); - game.addEffect(effect, source); - } - } + List permanents = TritonTacticsWatcher.getPermanents(morSet, game); + if (permanents.isEmpty()) { + return false; } - if (attackerMap != null) { - attackerMap.clear(); - } - + permanents.stream().forEach(permanent -> permanent.tap(source, game)); + game.addEffect(new DontUntapInControllersNextUntapStepTargetEffect() + .setTargetPointer(new FixedTargets(permanents, game)), source); return true; } } -class BlockedCreaturesWatcher extends Watcher { +class TritonTacticsWatcher extends Watcher { - public BlockedCreaturesWatcher() { - super(WatcherScope.CARD); + private final Map> map = new HashMap<>(); + private static final Set emptySet = Collections.unmodifiableSet(new HashSet<>()); + + TritonTacticsWatcher() { + super(WatcherScope.GAME); } @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.BLOCKER_DECLARED) { - Map> targetMap; - Object object = game.getState().getValue("targets" + this.getSourceId().toString()); - if (object instanceof Map) { - Permanent blocker = game.getPermanent(event.getSourceId()); - if (blocker != null) { - targetMap = (Map>) object; - for (Map.Entry> entry : targetMap.entrySet()) { - if (entry.getValue().contains(CardUtil.getCardZoneString("", blocker.getId(), game))) { - // save the attacking creature that was blocked by a creature effected by Triton Tactics - saveAttackingCreature(event.getTargetId(), entry.getKey(), game); - } - } - } - } - + map.computeIfAbsent( + new MageObjectReference(event.getSourceId(), game), x -> new HashSet<>() + ).add(new MageObjectReference(event.getTargetId(), game)); } } - private void saveAttackingCreature(UUID attackerId, Integer zoneChangeCounter, Game game) { - Set attackers; - Map> attackerMap; - Object object = game.getState().getValue("blockedAttackers" + getSourceId()); - if (object instanceof Map) { - attackerMap = (Map>) object; - } else { - attackerMap = new HashMap<>(); - } - attackers = attackerMap.computeIfAbsent(zoneChangeCounter, k -> new HashSet<>()); - attackers.add(CardUtil.getCardZoneString(null, attackerId, game)); - game.getState().setValue("blockedAttackers" + getSourceId().toString(), attackerMap); + @Override + public void reset() { + super.reset(); + map.clear(); + } + + static List getPermanents(Set targets, Game game) { + TritonTacticsWatcher watcher = game.getState().getWatcher(TritonTacticsWatcher.class); + return targets + .stream() + .map(mor -> watcher.map.getOrDefault(mor, emptySet)) + .flatMap(Collection::stream) + .map(mor -> mor.getPermanent(game)) + .filter(Objects::nonNull) + .collect(Collectors.toList()); } } diff --git a/Mage.Sets/src/mage/cards/t/TriumphOfGerrard.java b/Mage.Sets/src/mage/cards/t/TriumphOfGerrard.java index 92900412a8d..c0c246c8464 100644 --- a/Mage.Sets/src/mage/cards/t/TriumphOfGerrard.java +++ b/Mage.Sets/src/mage/cards/t/TriumphOfGerrard.java @@ -38,7 +38,7 @@ public final class TriumphOfGerrard extends CardImpl { this.subtype.add(SubType.SAGA); // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) - SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); + SagaAbility sagaAbility = new SagaAbility(this); // I, II — Put a +1/+1 counter on target creature you control with the greatest power. sagaAbility.addChapterEffect( this, diff --git a/Mage.Sets/src/mage/cards/t/TrollbredGuardian.java b/Mage.Sets/src/mage/cards/t/TrollbredGuardian.java index 1b70223aae3..45790ecabf2 100644 --- a/Mage.Sets/src/mage/cards/t/TrollbredGuardian.java +++ b/Mage.Sets/src/mage/cards/t/TrollbredGuardian.java @@ -11,9 +11,7 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; import mage.constants.Zone; -import mage.counters.CounterType; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; import java.util.UUID; @@ -22,12 +20,6 @@ import java.util.UUID; */ public final class TrollbredGuardian extends CardImpl { - private static final FilterPermanent filter = new FilterControlledCreaturePermanent(); - - static { - filter.add(CounterType.P1P1.getPredicate()); - } - public TrollbredGuardian(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}"); @@ -46,10 +38,9 @@ public final class TrollbredGuardian extends CardImpl { new GainAbilityAllEffect( TrampleAbility.getInstance(), Duration.WhileOnBattlefield, - filter, "Each creature you control " + - "with a +1/+1 counter on it has trample" + StaticFilters.FILTER_EACH_CONTROLLED_CREATURE_P1P1) ) - )); + ); } private TrollbredGuardian(final TrollbredGuardian card) { diff --git a/Mage.Sets/src/mage/cards/t/TrophyMage.java b/Mage.Sets/src/mage/cards/t/TrophyMage.java index ef63fefacd9..83931678c9b 100644 --- a/Mage.Sets/src/mage/cards/t/TrophyMage.java +++ b/Mage.Sets/src/mage/cards/t/TrophyMage.java @@ -1,21 +1,20 @@ - package mage.cards.t; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.ComparisonType; +import mage.constants.SubType; import mage.filter.FilterCard; import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInLibrary; +import java.util.UUID; + /** - * * @author Styxo */ public final class TrophyMage extends CardImpl { @@ -36,7 +35,9 @@ public final class TrophyMage extends CardImpl { this.toughness = new MageInt(2); // When Trophy Mage enters the battlefield, you may search your library for an artifact card with converted mana cost 3, reveal it, put it into your hand, then shuffle your library. - this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(0, 1, filter), true, true), true)); + this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryPutInHandEffect( + new TargetCardInLibrary(filter), true, true + ), true)); } private TrophyMage(final TrophyMage card) { diff --git a/Mage.Sets/src/mage/cards/t/TrostaniDiscordant.java b/Mage.Sets/src/mage/cards/t/TrostaniDiscordant.java index 55989f46f1b..1684e74e14a 100644 --- a/Mage.Sets/src/mage/cards/t/TrostaniDiscordant.java +++ b/Mage.Sets/src/mage/cards/t/TrostaniDiscordant.java @@ -1,37 +1,21 @@ package mage.cards.t; -import java.util.Iterator; -import java.util.UUID; import mage.MageInt; -import mage.MageObjectReference; -import mage.abilities.Ability; import mage.abilities.common.BeginningOfEndStepTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.continuous.BoostControlledEffect; -import mage.constants.SubType; -import mage.constants.SuperType; +import mage.abilities.effects.common.continuous.GainControlAllOwnedEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Layer; -import mage.constants.Outcome; -import mage.constants.SubLayer; -import mage.constants.TargetController; -import mage.constants.Zone; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.card.OwnerIdPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.constants.*; +import mage.filter.StaticFilters; import mage.game.permanent.token.SoldierLifelinkToken; -import mage.players.Player; + +import java.util.UUID; /** - * * @author TheElk801 */ public final class TrostaniDiscordant extends CardImpl { @@ -45,12 +29,9 @@ public final class TrostaniDiscordant extends CardImpl { this.toughness = new MageInt(4); // Other creatures you control get +1/+1. - this.addAbility(new SimpleStaticAbility( - Zone.BATTLEFIELD, - new BoostControlledEffect( - 1, 1, Duration.WhileOnBattlefield, true - ) - )); + this.addAbility(new SimpleStaticAbility(new BoostControlledEffect( + 1, 1, Duration.WhileOnBattlefield, true + ))); // When Trostani Discordant enters the battlefield, create two 1/1 white Soldier creature tokens with lifelink. this.addAbility(new EntersBattlefieldTriggeredAbility( @@ -59,7 +40,8 @@ public final class TrostaniDiscordant extends CardImpl { // At the beginning of your end step, each player gains control of all creatures they own. this.addAbility(new BeginningOfEndStepTriggeredAbility( - new TrostaniDiscordantEffect(), TargetController.YOU, false + new GainControlAllOwnedEffect(StaticFilters.FILTER_PERMANENT_CREATURES), + TargetController.YOU, false )); } @@ -72,54 +54,3 @@ public final class TrostaniDiscordant extends CardImpl { return new TrostaniDiscordant(this); } } - -class TrostaniDiscordantEffect extends ContinuousEffectImpl { - - public TrostaniDiscordantEffect() { - super(Duration.EndOfGame, Layer.ControlChangingEffects_2, SubLayer.NA, Outcome.GainControl); - this.staticText = "each player gains control of all creatures they own"; - } - - public TrostaniDiscordantEffect(final TrostaniDiscordantEffect effect) { - super(effect); - } - - @Override - public TrostaniDiscordantEffect copy() { - return new TrostaniDiscordantEffect(this); - } - - @Override - public void init(Ability source, Game game) { - super.init(source, game); - // add all creatures in range - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - FilterPermanent playerFilter = new FilterCreaturePermanent(); - playerFilter.add(new OwnerIdPredicate(playerId)); - for (Permanent permanent : game.getBattlefield().getActivePermanents(playerFilter, playerId, game)) { - affectedObjectList.add(new MageObjectReference(permanent, game)); - } - } - } - } - - @Override - public boolean apply(Game game, Ability source) { - for (Iterator it = affectedObjectList.iterator(); it.hasNext();) { - Permanent creature = it.next().getPermanent(game); - if (creature != null) { - if (!creature.isControlledBy(creature.getOwnerId())) { - creature.changeControllerId(creature.getOwnerId(), game, source); - } - } else { - it.remove(); - } - } - if (affectedObjectList.isEmpty()) { - this.discard(); - } - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/t/TruefireCaptain.java b/Mage.Sets/src/mage/cards/t/TruefireCaptain.java index d27f1b5df26..bc426562b06 100644 --- a/Mage.Sets/src/mage/cards/t/TruefireCaptain.java +++ b/Mage.Sets/src/mage/cards/t/TruefireCaptain.java @@ -35,7 +35,7 @@ public final class TruefireCaptain extends CardImpl { // Whenever Truefire Captain is dealt damage, it deals that much damage to target player. Ability ability = new DealtDamageToSourceTriggeredAbility( new TruefireCaptainEffect(), - false, false, true + false, false ); ability.addTarget(new TargetPlayer()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/t/TrumpetBlast.java b/Mage.Sets/src/mage/cards/t/TrumpetBlast.java index f3ec87b2311..b6c5b904591 100644 --- a/Mage.Sets/src/mage/cards/t/TrumpetBlast.java +++ b/Mage.Sets/src/mage/cards/t/TrumpetBlast.java @@ -1,4 +1,3 @@ - package mage.cards.t; import java.util.UUID; @@ -7,7 +6,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.common.FilterAttackingCreature; +import mage.filter.StaticFilters; /** * @@ -15,13 +14,11 @@ import mage.filter.common.FilterAttackingCreature; */ public final class TrumpetBlast extends CardImpl { - private static final FilterAttackingCreature filter = new FilterAttackingCreature("Attacking creatures"); - public TrumpetBlast(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{R}"); // Attacking creatures get +2/+0 until end of turn. - this.getSpellAbility().addEffect(new BoostAllEffect(2, 0, Duration.EndOfTurn, filter, false)); + this.getSpellAbility().addEffect(new BoostAllEffect(2, 0, Duration.EndOfTurn, StaticFilters.FILTER_ATTACKING_CREATURES, false)); } private TrumpetBlast(final TrumpetBlast card) { diff --git a/Mage.Sets/src/mage/cards/t/TundraKavu.java b/Mage.Sets/src/mage/cards/t/TundraKavu.java index f04ff13ab7d..e8cb2e1bc25 100644 --- a/Mage.Sets/src/mage/cards/t/TundraKavu.java +++ b/Mage.Sets/src/mage/cards/t/TundraKavu.java @@ -52,7 +52,7 @@ public final class TundraKavu extends CardImpl { class TundraKavuEffect extends BecomesBasicLandTargetEffect { public TundraKavuEffect() { - super(Duration.EndOfTurn, false, true); + super(Duration.EndOfTurn); staticText = "Target land becomes a Plains or an Island until end of turn."; } diff --git a/Mage.Sets/src/mage/cards/t/TuskguardCaptain.java b/Mage.Sets/src/mage/cards/t/TuskguardCaptain.java index ddb23ea7efb..bd626b67997 100644 --- a/Mage.Sets/src/mage/cards/t/TuskguardCaptain.java +++ b/Mage.Sets/src/mage/cards/t/TuskguardCaptain.java @@ -13,10 +13,8 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.TargetController; import mage.constants.Zone; -import mage.counters.CounterType; -import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; /** * @@ -24,16 +22,6 @@ import mage.filter.FilterPermanent; */ public final class TuskguardCaptain 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 trample"; - public TuskguardCaptain(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{G}"); this.subtype.add(SubType.HUMAN); @@ -45,9 +33,14 @@ public final class TuskguardCaptain extends CardImpl { // Outlast G this.addAbility(new OutlastAbility(new ManaCostsImpl("{G}"))); // Each creature you control with a +1/+1 counter on it has trample. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(TrampleAbility.getInstance(), Duration.WhileOnBattlefield, filter, rule))); - - + this.addAbility(new SimpleStaticAbility( + Zone.BATTLEFIELD, + new GainAbilityAllEffect( + TrampleAbility.getInstance(), + Duration.WhileOnBattlefield, + StaticFilters.FILTER_EACH_CONTROLLED_CREATURE_P1P1) + ) + ); } private TuskguardCaptain(final TuskguardCaptain card) { diff --git a/Mage.Sets/src/mage/cards/t/TwinBolt.java b/Mage.Sets/src/mage/cards/t/TwinBolt.java index 27cb18a9194..bc9f455a484 100644 --- a/Mage.Sets/src/mage/cards/t/TwinBolt.java +++ b/Mage.Sets/src/mage/cards/t/TwinBolt.java @@ -1,6 +1,5 @@ package mage.cards.t; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.DamageMultiEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -17,10 +16,8 @@ public final class TwinBolt extends CardImpl { public TwinBolt(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{R}"); - // Twin Bolt deals 2 damage divided as you choose among one or two target creatures and/or players. - Effect effect = new DamageMultiEffect(2); - effect.setText("{this} deals 2 damage divided as you choose among one or two target creatures and/or players"); - this.getSpellAbility().addEffect(effect); + // Twin Bolt deals 2 damage divided as you choose among one or two targets. + this.getSpellAbility().addEffect(new DamageMultiEffect(2)); this.getSpellAbility().addTarget(new TargetAnyTargetAmount(2)); } diff --git a/Mage.Sets/src/mage/cards/t/TwinbladeGeist.java b/Mage.Sets/src/mage/cards/t/TwinbladeGeist.java new file mode 100644 index 00000000000..3323597e51e --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TwinbladeGeist.java @@ -0,0 +1,43 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.keyword.DisturbAbility; +import mage.abilities.keyword.DoubleStrikeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TwinbladeGeist extends CardImpl { + + public TwinbladeGeist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.subtype.add(SubType.SPIRIT); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + this.secondSideCardClazz = mage.cards.t.TwinbladeInvocation.class; + + // Double strike + this.addAbility(DoubleStrikeAbility.getInstance()); + + // Disturb {2}{W} + this.addAbility(new DisturbAbility(this, "{2}{W}")); + } + + private TwinbladeGeist(final TwinbladeGeist card) { + super(card); + } + + @Override + public TwinbladeGeist copy() { + return new TwinbladeGeist(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TwinbladeInvocation.java b/Mage.Sets/src/mage/cards/t/TwinbladeInvocation.java new file mode 100644 index 00000000000..fa73595eb2d --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TwinbladeInvocation.java @@ -0,0 +1,58 @@ +package mage.cards.t; + +import mage.abilities.Ability; +import mage.abilities.common.PutIntoGraveFromAnywhereSourceAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.ExileSourceEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.DoubleStrikeAbility; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TwinbladeInvocation extends CardImpl { + + public TwinbladeInvocation(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, ""); + + this.subtype.add(SubType.AURA); + this.color.setWhite(true); + this.nightCard = true; + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // Enchanted creature has double strike. + this.addAbility(new SimpleStaticAbility(new GainAbilityAttachedEffect( + DoubleStrikeAbility.getInstance(), AttachmentType.AURA + ))); + + // If Twinblade Invocation would be put into a graveyard from anywhere, exile it instead. + this.addAbility(new PutIntoGraveFromAnywhereSourceAbility(new ExileSourceEffect().setText("exile it instead"))); + } + + private TwinbladeInvocation(final TwinbladeInvocation card) { + super(card); + } + + @Override + public TwinbladeInvocation copy() { + return new TwinbladeInvocation(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/Twinflame.java b/Mage.Sets/src/mage/cards/t/Twinflame.java index 7487d457628..748b833d0f0 100644 --- a/Mage.Sets/src/mage/cards/t/Twinflame.java +++ b/Mage.Sets/src/mage/cards/t/Twinflame.java @@ -77,7 +77,7 @@ class TwinflameCopyEffect extends OneShotEffect { CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(source.getControllerId(), null, true); effect.setTargetPointer(new FixedTarget(creature, game)); effect.apply(game, source); - toExile.addAll(effect.getAddedPermanent()); + toExile.addAll(effect.getAddedPermanents()); } } ExileTargetEffect exileEffect = new ExileTargetEffect(); diff --git a/Mage.Sets/src/mage/cards/t/TwinshotSniper.java b/Mage.Sets/src/mage/cards/t/TwinshotSniper.java new file mode 100644 index 00000000000..9da44c7b3dd --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TwinshotSniper.java @@ -0,0 +1,52 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.keyword.ChannelAbility; +import mage.abilities.keyword.ReachAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.target.common.TargetAnyTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TwinshotSniper extends CardImpl { + + public TwinshotSniper(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}{R}"); + + this.subtype.add(SubType.GOBLIN); + this.subtype.add(SubType.ARCHER); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Reach + this.addAbility(ReachAbility.getInstance()); + + // When Twinshot Sniper enters the battlefield, it deals 2 damage to any target. + Ability ability = new EntersBattlefieldTriggeredAbility(new DamageTargetEffect(2, "it")); + ability.addTarget(new TargetAnyTarget()); + this.addAbility(ability); + + // Channel — {1}{R}, Discard Twinshot Sniper: It deals 2 damage to any target. + ability = new ChannelAbility("{1}{R}", new DamageTargetEffect(2, "it")); + ability.addTarget(new TargetAnyTarget()); + this.addAbility(ability); + } + + private TwinshotSniper(final TwinshotSniper card) { + super(card); + } + + @Override + public TwinshotSniper copy() { + return new TwinshotSniper(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TwistedEmbrace.java b/Mage.Sets/src/mage/cards/t/TwistedEmbrace.java new file mode 100644 index 00000000000..c98e9d36019 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TwistedEmbrace.java @@ -0,0 +1,72 @@ +package mage.cards.t; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +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.DestroyTargetEffect; +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.constants.TargetController; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TwistedEmbrace extends CardImpl { + + private static final FilterPermanent filter + = new FilterCreatureOrPlaneswalkerPermanent("creature or planeswalker an opponent controls"); + + static { + filter.add(TargetController.OPPONENT.getControllerPredicate()); + } + + private static final Condition condition + = new AttachedToMatchesFilterCondition(StaticFilters.FILTER_PERMANENT_CREATURE); + + public TwistedEmbrace(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}{B}"); + + this.subtype.add(SubType.AURA); + + // Enchant artifact or creature you control + TargetPermanent auraTarget = new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT_OR_CREATURE); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + this.addAbility(new EnchantAbility(auraTarget.getTargetName())); + + // When Twisted Embrace enters the battlefield, destroy target creature or planeswalker an opponent controls. + Ability ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect()); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + + // As long as enchanted permanent is a creature, it gets +1/+1. + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new BoostEnchantedEffect(1, 1), condition, + "as long as enchanted permanent is a creature, it gets +1/+1" + ))); + } + + private TwistedEmbrace(final TwistedEmbrace card) { + super(card); + } + + @Override + public TwistedEmbrace copy() { + return new TwistedEmbrace(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TwoHeadedSliver.java b/Mage.Sets/src/mage/cards/t/TwoHeadedSliver.java index 6a30a5c5f54..09529fa6510 100644 --- a/Mage.Sets/src/mage/cards/t/TwoHeadedSliver.java +++ b/Mage.Sets/src/mage/cards/t/TwoHeadedSliver.java @@ -31,7 +31,7 @@ public final class TwoHeadedSliver extends CardImpl { // All Sliver creatures have menace. (They can't be blocked except by two or more creatures.) this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect( new MenaceAbility(), - Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS, + Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_ALL_SLIVERS, "All Sliver creatures have menace. (They can't be blocked except by two or more creatures.)"))); } diff --git a/Mage.Sets/src/mage/cards/t/TymaretCallsTheDead.java b/Mage.Sets/src/mage/cards/t/TymaretCallsTheDead.java index 42608f2556a..af1e2f21289 100644 --- a/Mage.Sets/src/mage/cards/t/TymaretCallsTheDead.java +++ b/Mage.Sets/src/mage/cards/t/TymaretCallsTheDead.java @@ -33,7 +33,7 @@ public final class TymaretCallsTheDead extends CardImpl { this.subtype.add(SubType.SAGA); // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) - SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); + SagaAbility sagaAbility = new SagaAbility(this); // I, II — Put the top three cards of your library into your graveyard. Then you may exile a creature or enchantment card from your graveyard. If you do, create a 2/2 black Zombie creature token. sagaAbility.addChapterEffect( diff --git a/Mage.Sets/src/mage/cards/t/TyvarKell.java b/Mage.Sets/src/mage/cards/t/TyvarKell.java index d8bb915daac..fd3ababcc8b 100644 --- a/Mage.Sets/src/mage/cards/t/TyvarKell.java +++ b/Mage.Sets/src/mage/cards/t/TyvarKell.java @@ -2,7 +2,6 @@ package mage.cards.t; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.GetEmblemEffect; @@ -39,7 +38,7 @@ public final class TyvarKell extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.TYVAR); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // Elves you control have "{T}: Add {B}." this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( diff --git a/Mage.Sets/src/mage/cards/u/UWing.java b/Mage.Sets/src/mage/cards/u/UWing.java index a783f9ac5c1..ceb7c92a7ca 100644 --- a/Mage.Sets/src/mage/cards/u/UWing.java +++ b/Mage.Sets/src/mage/cards/u/UWing.java @@ -38,7 +38,7 @@ public final class UWing extends CardImpl { // As long as U-Wing is tapped, it gets +1/+0. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect( new BoostSourceEffect(1, 0, Duration.WhileOnBattlefield), - SourceTappedCondition.instance, + SourceTappedCondition.TAPPED, "As long as {this} is tapped, it gets +1/+0"))); // As long as U-Wing is untapped, it gets +0/+1. diff --git a/Mage.Sets/src/mage/cards/u/UbaMask.java b/Mage.Sets/src/mage/cards/u/UbaMask.java index 33c5751767f..d51f4b92552 100644 --- a/Mage.Sets/src/mage/cards/u/UbaMask.java +++ b/Mage.Sets/src/mage/cards/u/UbaMask.java @@ -1,4 +1,3 @@ - package mage.cards.u; import mage.MageObject; @@ -14,12 +13,12 @@ import mage.constants.*; import mage.game.Game; import mage.game.events.GameEvent; import mage.players.Player; +import mage.util.CardUtil; import mage.watchers.Watcher; import java.util.*; /** - * * @author LevelX2 */ public final class UbaMask extends CardImpl { @@ -112,6 +111,7 @@ class UbaMaskPlayEffect extends AsThoughEffectImpl { @Override public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + objectId = CardUtil.getMainCardId(game, objectId); // for split cards Card card = game.getCard(objectId); if (card != null && affectedControllerId.equals(card.getOwnerId()) diff --git a/Mage.Sets/src/mage/cards/u/UbulSarGatekeepers.java b/Mage.Sets/src/mage/cards/u/UbulSarGatekeepers.java index 12d946e6260..71db6dc49eb 100644 --- a/Mage.Sets/src/mage/cards/u/UbulSarGatekeepers.java +++ b/Mage.Sets/src/mage/cards/u/UbulSarGatekeepers.java @@ -11,8 +11,8 @@ import mage.abilities.hint.ConditionHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; +import mage.filter.StaticFilters; import mage.filter.common.FilterControlledPermanent; -import mage.filter.common.FilterCreaturePermanent; import mage.target.Target; import mage.target.common.TargetCreaturePermanent; @@ -24,11 +24,9 @@ import java.util.UUID; public final class UbulSarGatekeepers extends CardImpl { private static final FilterControlledPermanent filter = new FilterControlledPermanent(); - private static final FilterCreaturePermanent targetFilter = new FilterCreaturePermanent("creature an opponent controls"); static { filter.add(SubType.GATE.getPredicate()); - targetFilter.add(TargetController.OPPONENT.getControllerPredicate()); } private static final Condition gatesCondition = new PermanentsOnTheBattlefieldCondition(filter, ComparisonType.MORE_THAN, 1); @@ -46,7 +44,7 @@ public final class UbulSarGatekeepers extends CardImpl { new EntersBattlefieldTriggeredAbility(new BoostTargetEffect(-2, -2, Duration.EndOfTurn)), gatesCondition, "Whenever {this} enters the battlefield, if you control two or more Gates, target creature an opponent controls gets -2/-2 until end of turn."); - Target target = new TargetCreaturePermanent(targetFilter); + Target target = new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE); ability.addTarget(target); ability.addHint(new ConditionHint(gatesCondition, "You control two or more Gates")); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/u/UginTheIneffable.java b/Mage.Sets/src/mage/cards/u/UginTheIneffable.java index 9c966ea870c..8b3743724e9 100644 --- a/Mage.Sets/src/mage/cards/u/UginTheIneffable.java +++ b/Mage.Sets/src/mage/cards/u/UginTheIneffable.java @@ -5,7 +5,6 @@ import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.Effect; @@ -57,7 +56,7 @@ public final class UginTheIneffable extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.UGIN); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // Colorless spells you cast cost {2} less to cast. this.addAbility(new SimpleStaticAbility(new SpellsCostReductionControllerEffect( @@ -137,7 +136,7 @@ class UginTheIneffableEffect extends OneShotEffect { // look at face-down card in exile UginTheIneffableLookAtFaceDownEffect lookAtEffect = new UginTheIneffableLookAtFaceDownEffect(); - lookAtEffect.setTargetPointer(new FixedTarget(card.getId())); + lookAtEffect.setTargetPointer(new FixedTarget(card.getId(), game)); game.addEffect(lookAtEffect, source); tokenObjs.add(new MageObjectReference(addedTokenId, game)); diff --git a/Mage.Sets/src/mage/cards/u/UginTheSpiritDragon.java b/Mage.Sets/src/mage/cards/u/UginTheSpiritDragon.java index b8170dd9fc9..53bdc9bb348 100644 --- a/Mage.Sets/src/mage/cards/u/UginTheSpiritDragon.java +++ b/Mage.Sets/src/mage/cards/u/UginTheSpiritDragon.java @@ -6,7 +6,6 @@ import java.util.Set; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.costs.Cost; import mage.abilities.costs.common.PayVariableLoyaltyCost; import mage.abilities.effects.OneShotEffect; @@ -42,7 +41,7 @@ public final class UginTheSpiritDragon extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.UGIN); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(7)); + this.setStartingLoyalty(7); // +2: Ugin, the Spirit Dragon deals 3 damage to any target. LoyaltyAbility ability = new LoyaltyAbility(new DamageTargetEffect(3), 2); diff --git a/Mage.Sets/src/mage/cards/u/UlrichOfTheKrallenhorde.java b/Mage.Sets/src/mage/cards/u/UlrichOfTheKrallenhorde.java index 276c819511e..db4150de835 100644 --- a/Mage.Sets/src/mage/cards/u/UlrichOfTheKrallenhorde.java +++ b/Mage.Sets/src/mage/cards/u/UlrichOfTheKrallenhorde.java @@ -1,16 +1,16 @@ package mage.cards.u; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.Ability; +import mage.abilities.common.TransformsOrEntersTriggeredAbility; import mage.abilities.common.WerewolfFrontTriggeredAbility; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; import mage.target.common.TargetCreaturePermanent; import java.util.UUID; @@ -28,12 +28,14 @@ public final class UlrichOfTheKrallenhorde extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(4); - - this.transformable = true; this.secondSideCardClazz = UlrichUncontestedAlpha.class; // Whenever this creature enters the battlefield or transforms into Ulrich of the Krallenhorde, target creature gets +4/+4 until end of turn. - this.addAbility(new UlrichOfTheKrallenhordeAbility()); + Ability ability = new TransformsOrEntersTriggeredAbility( + new BoostTargetEffect(4, 4), false + ); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); // At the beginning of each upkeep, if no spells were cast last turn, transform Ulrich of the Krallenhorde. this.addAbility(new TransformAbility()); @@ -49,44 +51,3 @@ public final class UlrichOfTheKrallenhorde extends CardImpl { return new UlrichOfTheKrallenhorde(this); } } - -class UlrichOfTheKrallenhordeAbility extends TriggeredAbilityImpl { - - public UlrichOfTheKrallenhordeAbility() { - super(Zone.BATTLEFIELD, new BoostTargetEffect(4, 4, Duration.EndOfTurn), false); - this.addTarget(new TargetCreaturePermanent()); - } - - public UlrichOfTheKrallenhordeAbility(final UlrichOfTheKrallenhordeAbility ability) { - super(ability); - } - - @Override - public UlrichOfTheKrallenhordeAbility copy() { - return new UlrichOfTheKrallenhordeAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TRANSFORMED || event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.TRANSFORMED && event.getTargetId().equals(this.getSourceId())) { - Permanent permanent = game.getPermanent(sourceId); - if (permanent != null && !permanent.isTransformed()) { - return true; - } - } - if (event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD && event.getTargetId().equals(this.getSourceId())) { - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever this creature enters the battlefield or transforms into Ulrich of the Krallenhorde, target creature gets +4/+4 until end of turn."; - } -} diff --git a/Mage.Sets/src/mage/cards/u/UlrichUncontestedAlpha.java b/Mage.Sets/src/mage/cards/u/UlrichUncontestedAlpha.java index 5da9d4f8c41..5f6f2f0af87 100644 --- a/Mage.Sets/src/mage/cards/u/UlrichUncontestedAlpha.java +++ b/Mage.Sets/src/mage/cards/u/UlrichUncontestedAlpha.java @@ -1,19 +1,19 @@ package mage.cards.u; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.Ability; +import mage.abilities.common.TransformIntoSourceTriggeredAbility; import mage.abilities.common.WerewolfBackTriggeredAbility; import mage.abilities.effects.common.FightTargetSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.TargetController; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.target.Target; -import mage.target.common.TargetCreaturePermanent; +import mage.target.TargetPermanent; import java.util.UUID; @@ -22,6 +22,14 @@ import java.util.UUID; */ public final class UlrichUncontestedAlpha extends CardImpl { + private static final FilterCreaturePermanent filter + = new FilterCreaturePermanent("non-Werewolf creature you don't control"); + + static { + filter.add(Predicates.not(SubType.WEREWOLF.getPredicate())); + filter.add(TargetController.NOT_YOU.getControllerPredicate()); + } + public UlrichUncontestedAlpha(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); addSuperType(SuperType.LEGENDARY); @@ -33,10 +41,15 @@ public final class UlrichUncontestedAlpha extends CardImpl { // this card is the second face of double-faced card this.nightCard = true; - this.transformable = true; // Whenever this creature transforms into Ulrich, Uncontested Alpha, you may have it fight target non-Werewolf creature you don't control. - this.addAbility(new UlrichUncontestedAlphaAbility()); + Ability ability = new TransformIntoSourceTriggeredAbility( + new FightTargetSourceEffect() + .setText("you may have it fight target non-Werewolf creature you don't control"), + true, true + ); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); // At the beginning of each upkeep, if a player cast two or more spells last turn, transform Ulrich, Uncontested Alpha. this.addAbility(new WerewolfBackTriggeredAbility()); @@ -51,49 +64,3 @@ public final class UlrichUncontestedAlpha extends CardImpl { return new UlrichUncontestedAlpha(this); } } - -class UlrichUncontestedAlphaAbility extends TriggeredAbilityImpl { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("non-Werewolf creature you don't control"); - - static { - filter.add(Predicates.not(SubType.WEREWOLF.getPredicate())); - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - - public UlrichUncontestedAlphaAbility() { - super(Zone.BATTLEFIELD, new FightTargetSourceEffect(), true); - Target target = new TargetCreaturePermanent(filter); - this.addTarget(target); - } - - public UlrichUncontestedAlphaAbility(final UlrichUncontestedAlphaAbility ability) { - super(ability); - } - - @Override - public UlrichUncontestedAlphaAbility copy() { - return new UlrichUncontestedAlphaAbility(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 "Whenever this creature transforms into Ulrich, Uncontested Alpha, you may have it fight target non-Werewolf creature you don't control."; - } -} diff --git a/Mage.Sets/src/mage/cards/u/UlvenwaldBehemoth.java b/Mage.Sets/src/mage/cards/u/UlvenwaldBehemoth.java new file mode 100644 index 00000000000..46d6434e6c9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/u/UlvenwaldBehemoth.java @@ -0,0 +1,63 @@ +package mage.cards.u; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class UlvenwaldBehemoth extends CardImpl { + + public UlvenwaldBehemoth(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); + + this.subtype.add(SubType.BEAST); + this.subtype.add(SubType.HORROR); + this.power = new MageInt(8); + this.toughness = new MageInt(8); + this.color.setGreen(true); + this.nightCard = true; + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // Other creatures you control get +1/+1 and have trample and haste. + Ability ability = new SimpleStaticAbility(new BoostControlledEffect( + 1, 1, Duration.WhileOnBattlefield, true + )); + ability.addEffect(new GainAbilityControlledEffect( + TrampleAbility.getInstance(), Duration.WhileOnBattlefield, + StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE + ).setText("and have trample")); + ability.addEffect(new GainAbilityControlledEffect( + HasteAbility.getInstance(), Duration.WhileOnBattlefield, + StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE + ).setText("and haste")); + this.addAbility(ability); + } + + private UlvenwaldBehemoth(final UlvenwaldBehemoth card) { + super(card); + } + + @Override + public UlvenwaldBehemoth copy() { + return new UlvenwaldBehemoth(this); + } +} diff --git a/Mage.Sets/src/mage/cards/u/UlvenwaldCaptive.java b/Mage.Sets/src/mage/cards/u/UlvenwaldCaptive.java index c6a47b7eefe..bc813d5ac1a 100644 --- a/Mage.Sets/src/mage/cards/u/UlvenwaldCaptive.java +++ b/Mage.Sets/src/mage/cards/u/UlvenwaldCaptive.java @@ -28,7 +28,6 @@ public final class UlvenwaldCaptive extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(2); - this.transformable = true; this.secondSideCardClazz = UlvenwaldAbomination.class; // Defender @@ -39,7 +38,7 @@ public final class UlvenwaldCaptive extends CardImpl { // {5}{G}{G}: Transform Ulvenwald Captive. this.addAbility(new TransformAbility()); - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new TransformSourceEffect(true), new ManaCostsImpl("{5}{G}{G}"))); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new TransformSourceEffect(), new ManaCostsImpl("{5}{G}{G}"))); } private UlvenwaldCaptive(final UlvenwaldCaptive card) { diff --git a/Mage.Sets/src/mage/cards/u/UlvenwaldMystics.java b/Mage.Sets/src/mage/cards/u/UlvenwaldMystics.java index 3703f8792ae..f4c838a6c34 100644 --- a/Mage.Sets/src/mage/cards/u/UlvenwaldMystics.java +++ b/Mage.Sets/src/mage/cards/u/UlvenwaldMystics.java @@ -21,7 +21,6 @@ public final class UlvenwaldMystics extends CardImpl { this.subtype.add(SubType.SHAMAN); this.subtype.add(SubType.WEREWOLF); - this.transformable = true; this.secondSideCardClazz = UlvenwaldPrimordials.class; this.power = new MageInt(3); diff --git a/Mage.Sets/src/mage/cards/u/UlvenwaldOddity.java b/Mage.Sets/src/mage/cards/u/UlvenwaldOddity.java new file mode 100644 index 00000000000..ae8c3820b74 --- /dev/null +++ b/Mage.Sets/src/mage/cards/u/UlvenwaldOddity.java @@ -0,0 +1,51 @@ +package mage.cards.u; + +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.abilities.keyword.TransformAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class UlvenwaldOddity extends CardImpl { + + public UlvenwaldOddity(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{G}"); + + this.subtype.add(SubType.BEAST); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + this.secondSideCardClazz = mage.cards.u.UlvenwaldBehemoth.class; + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // {5}{G}{G}: Transform Ulvenwald Oddity. + this.addAbility(new TransformAbility()); + this.addAbility(new SimpleActivatedAbility( + new TransformSourceEffect(), new ManaCostsImpl<>("{5}{G}{G}") + )); + } + + private UlvenwaldOddity(final UlvenwaldOddity card) { + super(card); + } + + @Override + public UlvenwaldOddity copy() { + return new UlvenwaldOddity(this); + } +} diff --git a/Mage.Sets/src/mage/cards/u/UlvenwaldPrimordials.java b/Mage.Sets/src/mage/cards/u/UlvenwaldPrimordials.java index b76a5fe24ac..d963de0208c 100644 --- a/Mage.Sets/src/mage/cards/u/UlvenwaldPrimordials.java +++ b/Mage.Sets/src/mage/cards/u/UlvenwaldPrimordials.java @@ -24,7 +24,6 @@ public final class UlvenwaldPrimordials extends CardImpl { // this card is the second face of double-faced card this.nightCard = true; - this.transformable = true; this.power = new MageInt(5); this.toughness = new MageInt(5); diff --git a/Mage.Sets/src/mage/cards/u/UlvenwaldTracker.java b/Mage.Sets/src/mage/cards/u/UlvenwaldTracker.java index 5eace8f7904..d45b79ca504 100644 --- a/Mage.Sets/src/mage/cards/u/UlvenwaldTracker.java +++ b/Mage.Sets/src/mage/cards/u/UlvenwaldTracker.java @@ -1,7 +1,5 @@ - package mage.cards.u; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -12,15 +10,14 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.other.AnotherTargetPredicate; +import mage.filter.StaticFilters; import mage.target.Target; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author North */ public final class UlvenwaldTracker extends CardImpl { @@ -34,14 +31,12 @@ public final class UlvenwaldTracker extends CardImpl { this.toughness = new MageInt(1); // {1}{G}, {tap}: Target creature you control fights another target creature. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new FightTargetsEffect(), new ManaCostsImpl("{1}{G}")); + Ability ability = new SimpleActivatedAbility(new FightTargetsEffect(false), new ManaCostsImpl<>("{1}{G}")); ability.addCost(new TapSourceCost()); Target controlledTarget = new TargetControlledCreaturePermanent(); controlledTarget.setTargetTag(1); ability.addTarget(controlledTarget); - FilterCreaturePermanent filter = new FilterCreaturePermanent(); - filter.add(new AnotherTargetPredicate(2)); - Target secondTarget = new TargetCreaturePermanent(filter); + Target secondTarget = new TargetCreaturePermanent(StaticFilters.FILTER_ANOTHER_CREATURE_TARGET_2); secondTarget.setTargetTag(2); ability.addTarget(secondTarget); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/u/UmbrisFearManifest.java b/Mage.Sets/src/mage/cards/u/UmbrisFearManifest.java new file mode 100644 index 00000000000..74652b457bb --- /dev/null +++ b/Mage.Sets/src/mage/cards/u/UmbrisFearManifest.java @@ -0,0 +1,140 @@ +package mage.cards.u; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetOpponent; + +import java.util.Collection; +import java.util.Objects; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class UmbrisFearManifest extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent("Nightmare or Horror"); + + static { + filter.add(Predicates.or( + SubType.NIGHTMARE.getPredicate(), + SubType.HORROR.getPredicate() + )); + } + + public UmbrisFearManifest(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.NIGHTMARE); + this.subtype.add(SubType.HORROR); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Umbris, Fear Manifest gets +1/+1 for each card your opponents own in exile. + this.addAbility(new SimpleStaticAbility(new BoostSourceEffect( + UmbrisFearManifestValue.instance, + UmbrisFearManifestValue.instance, + Duration.WhileOnBattlefield + ))); + + // Whenever Umbris or another Nightmare or Horror enters the battlefield under your control, target opponent exiles cards from the top of their library until they exile a land card. + Ability ability = new EntersBattlefieldThisOrAnotherTriggeredAbility( + new UmbrisFearManifestEffect(), filter, false, true + ); + ability.addTarget(new TargetOpponent()); + this.addAbility(ability); + } + + private UmbrisFearManifest(final UmbrisFearManifest card) { + super(card); + } + + @Override + public UmbrisFearManifest copy() { + return new UmbrisFearManifest(this); + } +} + +enum UmbrisFearManifestValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + return game + .getState() + .getExile() + .getExileZones() + .stream() + .map(exileZone -> exileZone.getCards(game)) + .flatMap(Collection::stream) + .filter(Objects::nonNull) + .map(Card::getOwnerId) + .filter(game.getOpponents(sourceAbility.getControllerId())::contains) + .mapToInt(x -> 1) + .sum(); + } + + @Override + public UmbrisFearManifestValue copy() { + return this; + } + + @Override + public String getMessage() { + return "card your opponents own in exile"; + } + + @Override + public String toString() { + return "1"; + } +} + +class UmbrisFearManifestEffect extends OneShotEffect { + + UmbrisFearManifestEffect() { + super(Outcome.Benefit); + staticText = "target opponent exiles cards from the top of their library until they exile a land card"; + } + + private UmbrisFearManifestEffect(final UmbrisFearManifestEffect effect) { + super(effect); + } + + @Override + public UmbrisFearManifestEffect copy() { + return new UmbrisFearManifestEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getFirstTarget()); + if (player == null) { + return false; + } + for (int i = 0; i < 1000; i++) { // avoiding a loop + Card card = player.getLibrary().getFromTop(game); + player.moveCards(card, Zone.EXILED, source, game); + if (card.isLand(game)) { + break; + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/u/UnchartedHaven.java b/Mage.Sets/src/mage/cards/u/UnchartedHaven.java new file mode 100644 index 00000000000..305a829ef75 --- /dev/null +++ b/Mage.Sets/src/mage/cards/u/UnchartedHaven.java @@ -0,0 +1,43 @@ +package mage.cards.u; + +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.ChooseColorEffect; +import mage.abilities.effects.mana.AddManaChosenColorEffect; +import mage.abilities.mana.SimpleManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class UnchartedHaven extends CardImpl { + + public UnchartedHaven(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // Uncharted Haven enters the battlefield tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // As Uncharted Haven enters the battlefield, choose a color. + this.addAbility(new AsEntersBattlefieldAbility(new ChooseColorEffect(Outcome.Neutral))); + + // {T}: Add one mana of the chosen color. + this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, new AddManaChosenColorEffect(), new TapSourceCost())); + } + + private UnchartedHaven(final UnchartedHaven card) { + super(card); + } + + @Override + public UnchartedHaven copy() { + return new UnchartedHaven(this); + } +} diff --git a/Mage.Sets/src/mage/cards/u/UndeadButler.java b/Mage.Sets/src/mage/cards/u/UndeadButler.java new file mode 100644 index 00000000000..10c9c921e02 --- /dev/null +++ b/Mage.Sets/src/mage/cards/u/UndeadButler.java @@ -0,0 +1,53 @@ +package mage.cards.u; + +import mage.MageInt; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.costs.common.ExileSourceFromGraveCost; +import mage.abilities.effects.common.DoWhenCostPaid; +import mage.abilities.effects.common.MillCardsControllerEffect; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class UndeadButler extends CardImpl { + + public UndeadButler(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}"); + + this.subtype.add(SubType.ZOMBIE); + this.power = new MageInt(1); + this.toughness = new MageInt(2); + + // When Undead Butler enters the battlefield, mill three cards. + this.addAbility(new EntersBattlefieldTriggeredAbility(new MillCardsControllerEffect(3))); + + // When Undead Butler dies, you may exile it. When you do, return target creature card from your graveyard to your hand. + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new ReturnFromGraveyardToHandTargetEffect(), false + ); + ability.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); + this.addAbility(new DiesSourceTriggeredAbility(new DoWhenCostPaid( + ability, new ExileSourceFromGraveCost().setText("exile it"), "Exile {this}?" + ))); + } + + private UndeadButler(final UndeadButler card) { + super(card); + } + + @Override + public UndeadButler copy() { + return new UndeadButler(this); + } +} diff --git a/Mage.Sets/src/mage/cards/u/UndercityNecrolisk.java b/Mage.Sets/src/mage/cards/u/UndercityNecrolisk.java index 739a1181e00..67666a62983 100644 --- a/Mage.Sets/src/mage/cards/u/UndercityNecrolisk.java +++ b/Mage.Sets/src/mage/cards/u/UndercityNecrolisk.java @@ -3,18 +3,18 @@ package mage.cards.u; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; +import mage.abilities.ActivatedAbilityImpl; import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.costs.Cost; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.keyword.MenaceAbility; -import mage.constants.SubType; +import mage.constants.*; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterControlledPermanent; @@ -42,19 +42,22 @@ public final class UndercityNecrolisk extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(3); - // {1}, Sacrifice another creature: Put a +1/+1 counter on Undercity Necrolisk. It gains menace until end of turn. Activate this ability only any time you could cast a sorcery. - Ability ability = new ActivateAsSorceryActivatedAbility( + // {1}, Sacrifice another creature: + // Put a +1/+1 counter on Undercity Necrolisk. + Ability ability = new SilencedActivateAsSorceryActivatedAbility( Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.P1P1.createInstance()), new GenericManaCost(1) ); - ability.addEffect(new GainAbilitySourceEffect( - new MenaceAbility(), - Duration.EndOfTurn - ).setText("It gains menace until end of turn.")); ability.addCost(new SacrificeTargetCost( new TargetControlledPermanent(filter) )); + // It gains menace until end of turn. Activate this ability only any time you could cast a sorcery. + ability.addEffect(new GainAbilitySourceEffect( + new MenaceAbility(), + Duration.EndOfTurn + ).setText("It gains menace until end of turn. Activate only as a sorcery. " + + "(It can't be blocked except by two or more creatures.)")); this.addAbility(ability); } @@ -67,3 +70,23 @@ public final class UndercityNecrolisk extends CardImpl { return new UndercityNecrolisk(this); } } + +// Needed in order to move menace hint text to the very end. +class SilencedActivateAsSorceryActivatedAbility extends ActivatedAbilityImpl { + SilencedActivateAsSorceryActivatedAbility(Zone zone, Effect effect, Cost cost) { + super(zone, effect, cost); + timing = TimingRule.SORCERY; + } + + private SilencedActivateAsSorceryActivatedAbility(final SilencedActivateAsSorceryActivatedAbility ability) { + super(ability); + } + + @Override + public SilencedActivateAsSorceryActivatedAbility copy() { + return new SilencedActivateAsSorceryActivatedAbility(this); + } + + @Override + public String getRule() { return super.getRule(); } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/u/UndercityScrounger.java b/Mage.Sets/src/mage/cards/u/UndercityScrounger.java new file mode 100644 index 00000000000..56f6f20fbad --- /dev/null +++ b/Mage.Sets/src/mage/cards/u/UndercityScrounger.java @@ -0,0 +1,46 @@ +package mage.cards.u; + +import mage.MageInt; +import mage.abilities.condition.common.MorbidCondition; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.decorator.ConditionalActivatedAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.hint.common.MorbidHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.permanent.token.TreasureToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class UndercityScrounger extends CardImpl { + + public UndercityScrounger(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{2}{B}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ROGUE); + this.power = new MageInt(1); + this.toughness = new MageInt(4); + + // {T}: Create a Treasure token. Activate only if a creature died this turn. + this.addAbility(new ConditionalActivatedAbility( + Zone.BATTLEFIELD, new CreateTokenEffect(new TreasureToken()), + new TapSourceCost(), MorbidCondition.instance + ).addHint(MorbidHint.instance)); + } + + private UndercityScrounger(final UndercityScrounger card) { + super(card); + } + + @Override + public UndercityScrounger copy() { + return new UndercityScrounger(this); + } +} diff --git a/Mage.Sets/src/mage/cards/u/UndercityUprising.java b/Mage.Sets/src/mage/cards/u/UndercityUprising.java index 403f06dea3c..a3758ace5ea 100644 --- a/Mage.Sets/src/mage/cards/u/UndercityUprising.java +++ b/Mage.Sets/src/mage/cards/u/UndercityUprising.java @@ -24,10 +24,11 @@ public final class UndercityUprising extends CardImpl { // Creatures you control gain deathtouch until end of turn. Then target creature you control fights target creature you don't control. this.getSpellAbility().addEffect(new GainAbilityControlledEffect( DeathtouchAbility.getInstance(), Duration.EndOfTurn, - StaticFilters.FILTER_CONTROLLED_CREATURES + StaticFilters.FILTER_PERMANENT_CREATURES )); this.getSpellAbility().addEffect(new FightTargetsEffect() - .setText("Then target creature you control fights target creature you don't control")); + .setText("Then target creature you control fights target creature you don't control. " + + "(Each deals damage equal to its power to the other.)")); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); } diff --git a/Mage.Sets/src/mage/cards/u/UndyingMalice.java b/Mage.Sets/src/mage/cards/u/UndyingMalice.java new file mode 100644 index 00000000000..948ed327214 --- /dev/null +++ b/Mage.Sets/src/mage/cards/u/UndyingMalice.java @@ -0,0 +1,106 @@ +package mage.cards.u; + +import java.util.UUID; + +import mage.abilities.Ability; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.events.EntersTheBattlefieldEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author weirddan455 + */ +public final class UndyingMalice extends CardImpl { + + public UndyingMalice(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{B}"); + + // Until end of turn, target creature gains "When this creature dies, return it to the battlefield tapped under its owner's control with a +1/+1 counter on it." + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + this.getSpellAbility().addEffect(new GainAbilityTargetEffect(new DiesSourceTriggeredAbility(new UndyingMaliceReturnToBattlefieldEffect())) + .setText("Until end of turn, target creature gains \"When this creature dies, return it to the battlefield tapped under its owner's control with a +1/+1 counter on it.\"")); + } + + private UndyingMalice(final UndyingMalice card) { + super(card); + } + + @Override + public UndyingMalice copy() { + return new UndyingMalice(this); + } +} + +class UndyingMaliceReturnToBattlefieldEffect extends ReturnSourceFromGraveyardToBattlefieldEffect { + + public UndyingMaliceReturnToBattlefieldEffect() { + super(true); + staticText = "return it to the battlefield tapped under its owner's control with a +1/+1 counter on it"; + } + + private UndyingMaliceReturnToBattlefieldEffect(final UndyingMaliceReturnToBattlefieldEffect effect) { + super(effect); + } + + @Override + public UndyingMaliceReturnToBattlefieldEffect copy() { + return new UndyingMaliceReturnToBattlefieldEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + UndyingMaliceCounterEffect counterEffect = new UndyingMaliceCounterEffect(); + game.addEffect(counterEffect, source); + return super.apply(game, source); + } +} + +class UndyingMaliceCounterEffect extends ReplacementEffectImpl { + + public UndyingMaliceCounterEffect() { + super(Duration.EndOfStep, Outcome.BoostCreature); + } + + private UndyingMaliceCounterEffect(final UndyingMaliceCounterEffect effect) { + super(effect); + } + + @Override + public UndyingMaliceCounterEffect copy() { + return new UndyingMaliceCounterEffect(this); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return event.getTargetId().equals(source.getSourceId()); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Permanent creature = ((EntersTheBattlefieldEvent) event).getTarget(); + if (creature == null) { + return false; + } + creature.addCounters(CounterType.P1P1.createInstance(), source.getControllerId(), source, game, event.getAppliedEffects()); + discard(); + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/u/UnexplainedDisappearance.java b/Mage.Sets/src/mage/cards/u/UnexplainedDisappearance.java index 51b9cea915a..7730d95921d 100644 --- a/Mage.Sets/src/mage/cards/u/UnexplainedDisappearance.java +++ b/Mage.Sets/src/mage/cards/u/UnexplainedDisappearance.java @@ -22,7 +22,7 @@ public final class UnexplainedDisappearance extends CardImpl { this.getSpellAbility().addTarget(new TargetCreaturePermanent()); // Surveil 1. - this.getSpellAbility().addEffect(new SurveilEffect(1)); + this.getSpellAbility().addEffect(new SurveilEffect(1).concatBy("
")); } private UnexplainedDisappearance(final UnexplainedDisappearance card) { diff --git a/Mage.Sets/src/mage/cards/u/UnforgivingOne.java b/Mage.Sets/src/mage/cards/u/UnforgivingOne.java new file mode 100644 index 00000000000..8fce59a9b31 --- /dev/null +++ b/Mage.Sets/src/mage/cards/u/UnforgivingOne.java @@ -0,0 +1,91 @@ +package mage.cards.u; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.Card; +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.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.filter.predicate.permanent.ModifiedPredicate; +import mage.game.Game; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class UnforgivingOne extends CardImpl { + + private static final FilterCard filter = new FilterCreatureCard( + "creature card with mana value less than or equal " + + "to the number of modified creatures you control" + ); + + static { + filter.add(UnforgivingOnePredicate.instance); + } + + public UnforgivingOne(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Menace + this.addAbility(new MenaceAbility()); + + // Whenever Unforgiving One attacks, return target creature card with mana value X or less from your graveyard to the battlefield, where X is the number of modified creatures you control. + Ability ability = new AttacksTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect() + .setText("return target creature card with mana value X or less from your graveyard " + + "to the battlefield, where X is the number of modified creatures you control")); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + this.addAbility(ability.addHint(UnforgivingOnePredicate.getHint())); + } + + private UnforgivingOne(final UnforgivingOne card) { + super(card); + } + + @Override + public UnforgivingOne copy() { + return new UnforgivingOne(this); + } +} + +enum UnforgivingOnePredicate implements ObjectSourcePlayerPredicate { + instance; + private static final FilterPermanent filter = new FilterControlledCreaturePermanent(); + + static { + filter.add(ModifiedPredicate.instance); + } + + private static final Hint hint = new ValueHint( + "Modified creatures you control", new PermanentsOnBattlefieldCount(filter) + ); + + public static Hint getHint() { + return hint; + } + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + return input.getObject().getManaValue() + <= game.getBattlefield().count(filter, input.getSourceId(), input.getPlayerId(), game); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/u/UnhallowedCathar.java b/Mage.Sets/src/mage/cards/u/UnhallowedCathar.java index b765c55e0d1..9f61fbec892 100644 --- a/Mage.Sets/src/mage/cards/u/UnhallowedCathar.java +++ b/Mage.Sets/src/mage/cards/u/UnhallowedCathar.java @@ -23,7 +23,6 @@ public final class UnhallowedCathar extends CardImpl { // this card is the second face of double-faced card this.nightCard = true; - this.transformable = true; this.power = new MageInt(2); this.toughness = new MageInt(1); diff --git a/Mage.Sets/src/mage/cards/u/UnhallowedPhalanx.java b/Mage.Sets/src/mage/cards/u/UnhallowedPhalanx.java new file mode 100644 index 00000000000..13d50b67fb0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/u/UnhallowedPhalanx.java @@ -0,0 +1,37 @@ +package mage.cards.u; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class UnhallowedPhalanx extends CardImpl { + + public UnhallowedPhalanx(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}"); + + this.subtype.add(SubType.ZOMBIE); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(1); + this.toughness = new MageInt(13); + + // Unhallowed Phalanx enters the battlefield tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + } + + private UnhallowedPhalanx(final UnhallowedPhalanx card) { + super(card); + } + + @Override + public UnhallowedPhalanx copy() { + return new UnhallowedPhalanx(this); + } +} diff --git a/Mage.Sets/src/mage/cards/u/UnholyOfficiant.java b/Mage.Sets/src/mage/cards/u/UnholyOfficiant.java new file mode 100644 index 00000000000..36ac9f6aabb --- /dev/null +++ b/Mage.Sets/src/mage/cards/u/UnholyOfficiant.java @@ -0,0 +1,46 @@ +package mage.cards.u; + +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class UnholyOfficiant extends CardImpl { + + public UnholyOfficiant(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}"); + + this.subtype.add(SubType.VAMPIRE); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(1); + this.toughness = new MageInt(2); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // {4}{W}: Put a +1/+1 counter on Unholy Officiant. + this.addAbility(new SimpleActivatedAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), new ManaCostsImpl<>("{4}{W}") + )); + } + + private UnholyOfficiant(final UnholyOfficiant card) { + super(card); + } + + @Override + public UnholyOfficiant copy() { + return new UnholyOfficiant(this); + } +} diff --git a/Mage.Sets/src/mage/cards/u/UninvitedGeist.java b/Mage.Sets/src/mage/cards/u/UninvitedGeist.java index f83b9c384b6..1a635c3c0d9 100644 --- a/Mage.Sets/src/mage/cards/u/UninvitedGeist.java +++ b/Mage.Sets/src/mage/cards/u/UninvitedGeist.java @@ -24,7 +24,6 @@ public final class UninvitedGeist extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); - this.transformable = true; this.secondSideCardClazz = UnimpededTrespasser.class; // Skulk (This creature can't be blocked by creatures with greater power.) @@ -32,7 +31,7 @@ public final class UninvitedGeist extends CardImpl { // When Uninvited Geist deals combat damage to a player, transform it. this.addAbility(new TransformAbility()); - this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new TransformSourceEffect(true), false)); + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new TransformSourceEffect(), false)); } diff --git a/Mage.Sets/src/mage/cards/u/UnityOfPurpose.java b/Mage.Sets/src/mage/cards/u/UnityOfPurpose.java index 258f9f4c579..77a04b1274f 100644 --- a/Mage.Sets/src/mage/cards/u/UnityOfPurpose.java +++ b/Mage.Sets/src/mage/cards/u/UnityOfPurpose.java @@ -7,8 +7,7 @@ import mage.abilities.effects.keyword.SupportEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.counters.CounterType; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; /** * @@ -16,20 +15,15 @@ import mage.filter.common.FilterCreaturePermanent; */ public final class UnityOfPurpose extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("each creature you control with a +1/+1 counter on it"); - - static { - filter.add(CounterType.P1P1.getPredicate()); - } - public UnityOfPurpose(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{3}{U}"); // Support 2. - getSpellAbility().addEffect(new SupportEffect(this, 2, false)); + this.getSpellAbility().addEffect(new SupportEffect(this, 2, false)); // Untap each creature you control with a +1/+1 counter on it. - this.getSpellAbility().addEffect(new UntapAllControllerEffect(filter, "Untap each creature you control with a +1/+1 counter on it")); + this.getSpellAbility().addEffect(new UntapAllControllerEffect( + StaticFilters.FILTER_EACH_CONTROLLED_CREATURE_P1P1, "Untap each creature you control with a +1/+1 counter on it")); } private UnityOfPurpose(final UnityOfPurpose card) { diff --git a/Mage.Sets/src/mage/cards/u/UniversalSurveillance.java b/Mage.Sets/src/mage/cards/u/UniversalSurveillance.java new file mode 100644 index 00000000000..d45a25ce2bd --- /dev/null +++ b/Mage.Sets/src/mage/cards/u/UniversalSurveillance.java @@ -0,0 +1,35 @@ +package mage.cards.u; + +import mage.abilities.dynamicvalue.common.ManacostVariableValue; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.keyword.ImproviseAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class UniversalSurveillance extends CardImpl { + + public UniversalSurveillance(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{U}{U}{U}"); + + // Improvise + this.addAbility(new ImproviseAbility()); + + // Draw X cards. + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(ManacostVariableValue.REGULAR)); + } + + private UniversalSurveillance(final UniversalSurveillance card) { + super(card); + } + + @Override + public UniversalSurveillance copy() { + return new UniversalSurveillance(this); + } +} diff --git a/Mage.Sets/src/mage/cards/u/UnmakeTheGraves.java b/Mage.Sets/src/mage/cards/u/UnmakeTheGraves.java index 55901c0dffb..3c3ba3e79df 100644 --- a/Mage.Sets/src/mage/cards/u/UnmakeTheGraves.java +++ b/Mage.Sets/src/mage/cards/u/UnmakeTheGraves.java @@ -1,4 +1,3 @@ - package mage.cards.u; import java.util.UUID; @@ -7,7 +6,7 @@ import mage.abilities.keyword.ConvokeAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterCreatureCard; +import mage.filter.StaticFilters; import mage.target.common.TargetCardInYourGraveyard; /** @@ -19,13 +18,12 @@ public final class UnmakeTheGraves extends CardImpl { public UnmakeTheGraves(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{4}{B}"); - // Convoke this.addAbility(new ConvokeAbility()); - + // Return up to two target creature cards from your graveyard to your hand. this.getSpellAbility().addEffect(new ReturnFromGraveyardToHandTargetEffect()); - this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, 2, new FilterCreatureCard("creature cards from your graveyard"))); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, 2, StaticFilters.FILTER_CARD_CREATURES_YOUR_GRAVEYARD)); } private UnmakeTheGraves(final UnmakeTheGraves card) { diff --git a/Mage.Sets/src/mage/cards/u/UnnaturalAggression.java b/Mage.Sets/src/mage/cards/u/UnnaturalAggression.java index e59573e8879..cd97cbce9d8 100644 --- a/Mage.Sets/src/mage/cards/u/UnnaturalAggression.java +++ b/Mage.Sets/src/mage/cards/u/UnnaturalAggression.java @@ -9,8 +9,7 @@ import mage.abilities.keyword.DevoidAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; import mage.target.targetpointer.SecondTargetPointer; @@ -21,12 +20,6 @@ import mage.target.targetpointer.SecondTargetPointer; */ public final class UnnaturalAggression extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public UnnaturalAggression(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{G}"); @@ -34,9 +27,9 @@ public final class UnnaturalAggression extends CardImpl { this.addAbility(new DevoidAbility(this.color)); // Target creature you control fights target creature an opponent controls. - this.getSpellAbility().addEffect(new FightTargetsEffect()); + this.getSpellAbility().addEffect(new FightTargetsEffect(false)); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); // If the creature an opponent controls would die this turn, exile it instead. Effect effect = new ExileTargetIfDiesEffect(); effect.setText("If the creature an opponent controls would die this turn, exile it instead"); diff --git a/Mage.Sets/src/mage/cards/u/UnnaturalMoonrise.java b/Mage.Sets/src/mage/cards/u/UnnaturalMoonrise.java new file mode 100644 index 00000000000..45824e42ec8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/u/UnnaturalMoonrise.java @@ -0,0 +1,81 @@ +package mage.cards.u; + +import mage.abilities.Ability; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.hint.common.DayNightHint; +import mage.abilities.keyword.FlashbackAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.game.Game; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class UnnaturalMoonrise extends CardImpl { + + public UnnaturalMoonrise(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{R}{G}"); + + // It becomes night. Until end of turn, target creature gets +1/+0 and gains trample and has "Whenever this creature deals combat damage to a player, draw a card." + this.getSpellAbility().addEffect(new UnnaturalMoonriseEffect()); + this.getSpellAbility().addEffect(new BoostTargetEffect(1, 0) + .setText("Until end of turn, target creature gets +1/+0")); + this.getSpellAbility().addEffect(new GainAbilityTargetEffect( + TrampleAbility.getInstance(), Duration.EndOfTurn + ).setText("and gains trample")); + this.getSpellAbility().addEffect(new GainAbilityTargetEffect( + new DealsCombatDamageToAPlayerTriggeredAbility( + new DrawCardSourceControllerEffect(1), false + ).setTriggerPhrase("Whenever this creature deals combat damage to a player, "), Duration.EndOfTurn + ).setText("and \"Whenever this creature deals combat damage to a player, draw a card.\"")); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + this.getSpellAbility().addHint(DayNightHint.instance); + + // Flashback {2}{R}{G} + this.addAbility(new FlashbackAbility(this, new ManaCostsImpl<>("{2}{R}{G}"))); + } + + private UnnaturalMoonrise(final UnnaturalMoonrise card) { + super(card); + } + + @Override + public UnnaturalMoonrise copy() { + return new UnnaturalMoonrise(this); + } +} + +class UnnaturalMoonriseEffect extends OneShotEffect { + + UnnaturalMoonriseEffect() { + super(Outcome.Benefit); + staticText = "It becomes night."; + } + + private UnnaturalMoonriseEffect(final UnnaturalMoonriseEffect effect) { + super(effect); + } + + @Override + public UnnaturalMoonriseEffect copy() { + return new UnnaturalMoonriseEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + game.setDaytime(false); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/u/UnquenchableFury.java b/Mage.Sets/src/mage/cards/u/UnquenchableFury.java new file mode 100644 index 00000000000..282ad79c8da --- /dev/null +++ b/Mage.Sets/src/mage/cards/u/UnquenchableFury.java @@ -0,0 +1,118 @@ +package mage.cards.u; + +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.ReturnSourceFromGraveyardToHandEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class UnquenchableFury extends CardImpl { + + public UnquenchableFury(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{R}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + this.addAbility(new EnchantAbility(auraTarget.getTargetName())); + + // Enchanted creature has "Whenever this creature attacks, it deals X damage to defending player, where X is the number of cards in their hand." + this.addAbility(new SimpleStaticAbility(new GainAbilityAttachedEffect(new AttacksTriggeredAbility( + new UnquenchableFuryEffect(), false, null, SetTargetPointer.PLAYER + ), AttachmentType.AURA))); + + // When Unquenchable Fury is put into your graveyard from the battlfield, return it to your hand. + this.addAbility(new UnquenchableFuryTriggeredAbility()); + } + + private UnquenchableFury(final UnquenchableFury card) { + super(card); + } + + @Override + public UnquenchableFury copy() { + return new UnquenchableFury(this); + } +} + +class UnquenchableFuryEffect extends OneShotEffect { + + UnquenchableFuryEffect() { + super(Outcome.Benefit); + staticText = "it deals X damage to defending player, where X is the number of cards in their hand"; + } + + private UnquenchableFuryEffect(final UnquenchableFuryEffect effect) { + super(effect); + } + + @Override + public UnquenchableFuryEffect copy() { + return new UnquenchableFuryEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(getTargetPointer().getFirst(game, source)); + return player != null + && !player.getHand().isEmpty() + && player.damage(player.getHand().size(), source, game) > 0; + } +} + +class UnquenchableFuryTriggeredAbility extends TriggeredAbilityImpl { + + UnquenchableFuryTriggeredAbility() { + super(Zone.BATTLEFIELD, new ReturnSourceFromGraveyardToHandEffect()); + } + + private UnquenchableFuryTriggeredAbility(final UnquenchableFuryTriggeredAbility ability) { + super(ability); + } + + @Override + public UnquenchableFuryTriggeredAbility copy() { + return new UnquenchableFuryTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + Permanent permanent = zEvent.getTarget(); + return permanent != null && zEvent.isDiesEvent() + && permanent.getId().equals(this.getSourceId()) + && permanent.isOwnedBy(permanent.getControllerId()); + } + + @Override + public String getRule() { + return "When {this} is put into your graveyard from the battlfield, return it to your hand."; + } +} diff --git a/Mage.Sets/src/mage/cards/u/UnstoppableOgre.java b/Mage.Sets/src/mage/cards/u/UnstoppableOgre.java new file mode 100644 index 00000000000..703f1662455 --- /dev/null +++ b/Mage.Sets/src/mage/cards/u/UnstoppableOgre.java @@ -0,0 +1,43 @@ +package mage.cards.u; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.combat.CantBlockTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class UnstoppableOgre extends CardImpl { + + public UnstoppableOgre(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{2}{R}"); + + this.subtype.add(SubType.OGRE); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(4); + this.toughness = new MageInt(1); + + // When Unstoppable Ogre enters the battlefield, target creature can't block this turn. + Ability ability = new EntersBattlefieldTriggeredAbility(new CantBlockTargetEffect(Duration.EndOfTurn)); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + private UnstoppableOgre(final UnstoppableOgre card) { + super(card); + } + + @Override + public UnstoppableOgre copy() { + return new UnstoppableOgre(this); + } +} diff --git a/Mage.Sets/src/mage/cards/u/UntamedHunger.java b/Mage.Sets/src/mage/cards/u/UntamedHunger.java index db93e685300..38048d27704 100644 --- a/Mage.Sets/src/mage/cards/u/UntamedHunger.java +++ b/Mage.Sets/src/mage/cards/u/UntamedHunger.java @@ -40,7 +40,8 @@ public final class UntamedHunger extends CardImpl { // Enchanted creature gets +2/+1 and has menace. ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(2, 1)); Effect effect = new GainAbilityAttachedEffect(new MenaceAbility(), AttachmentType.AURA); - effect.setText("and has menace"); + effect.setText("and has menace. " + + "(It can't be blocked except by two or more creatures.)"); ability.addEffect(effect); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/u/UntamedPup.java b/Mage.Sets/src/mage/cards/u/UntamedPup.java index 519b2ef3353..4dbcd4007c9 100644 --- a/Mage.Sets/src/mage/cards/u/UntamedPup.java +++ b/Mage.Sets/src/mage/cards/u/UntamedPup.java @@ -42,7 +42,6 @@ public final class UntamedPup extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(4); this.color.setGreen(true); - this.transformable = true; this.nightCard = true; // Trample @@ -50,7 +49,7 @@ public final class UntamedPup extends CardImpl { // Other Wolves and Werewolves you control have trample. this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( - TrampleAbility.getInstance(), Duration.WhileOnBattlefield, filter + TrampleAbility.getInstance(), Duration.WhileOnBattlefield, filter, true ))); // {3}{G}: Put a +1/+1 counter on target creature. diff --git a/Mage.Sets/src/mage/cards/u/UntetheredExpress.java b/Mage.Sets/src/mage/cards/u/UntetheredExpress.java index 6ac63afece8..4c097b1ab7a 100644 --- a/Mage.Sets/src/mage/cards/u/UntetheredExpress.java +++ b/Mage.Sets/src/mage/cards/u/UntetheredExpress.java @@ -30,7 +30,7 @@ public final class UntetheredExpress extends CardImpl { this.addAbility(TrampleAbility.getInstance()); // Whenever Untethered Express attacks, put a +1/+1 counter on it. - this.addAbility(new AttacksTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false)); + this.addAbility(new AttacksTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()).setText("put a +1/+1 counter on it"), false)); // Crew 1 this.addAbility(new CrewAbility(1)); diff --git a/Mage.Sets/src/mage/cards/u/UnwillingRecruit.java b/Mage.Sets/src/mage/cards/u/UnwillingRecruit.java index 34e4304e46c..155bf2b40f3 100644 --- a/Mage.Sets/src/mage/cards/u/UnwillingRecruit.java +++ b/Mage.Sets/src/mage/cards/u/UnwillingRecruit.java @@ -1,4 +1,3 @@ - package mage.cards.u; import java.util.UUID; @@ -25,8 +24,7 @@ import mage.target.targetpointer.FixedTarget; public final class UnwillingRecruit extends CardImpl { public UnwillingRecruit(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{X}{R}{R}{R}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{R}{R}{R}"); // Gain control of target creature until end of turn. Untap that creature. It gets +X/+0 and gains haste until end of turn. this.getSpellAbility().addEffect(new UnwillingRecruitEffect()); @@ -64,7 +62,7 @@ class UnwillingRecruitEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Permanent targetCreature = game.getPermanent(source.getFirstTarget()); if (targetCreature != null) { - source.getEffects().get(0).setTargetPointer(new FixedTarget(targetCreature.getId())); + source.getEffects().get(0).setTargetPointer(new FixedTarget(targetCreature.getId(), game)); game.addEffect(new GainControlTargetEffect(Duration.EndOfTurn), source); targetCreature.untap(game); game.addEffect(new BoostTargetEffect(source.getManaCostsToPay().getX(), 0, Duration.EndOfTurn), source); diff --git a/Mage.Sets/src/mage/cards/u/UpriserRenegade.java b/Mage.Sets/src/mage/cards/u/UpriserRenegade.java new file mode 100644 index 00000000000..a90d7e87ae8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/u/UpriserRenegade.java @@ -0,0 +1,63 @@ +package mage.cards.u; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.filter.predicate.permanent.ModifiedPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class UpriserRenegade extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("other modified creature you control"); + + static { + filter.add(AnotherPredicate.instance); + filter.add(ModifiedPredicate.instance); + } + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter, 2); + private static final Hint hint = new ValueHint( + "Other modified creatures you control", new PermanentsOnBattlefieldCount(filter) + ); + + public UpriserRenegade(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SAMURAI); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // Upriser Renegade gets +2/+0 for each other modified creature you control. + this.addAbility(new SimpleStaticAbility(new BoostSourceEffect( + xValue, StaticValue.get(0), Duration.WhileOnBattlefield + )).addHint(hint)); + } + + private UpriserRenegade(final UpriserRenegade card) { + super(card); + } + + @Override + public UpriserRenegade copy() { + return new UpriserRenegade(this); + } +} diff --git a/Mage.Sets/src/mage/cards/u/UrabraskTheHidden.java b/Mage.Sets/src/mage/cards/u/UrabraskTheHidden.java index 79d3d043600..b9e5f9d0e74 100644 --- a/Mage.Sets/src/mage/cards/u/UrabraskTheHidden.java +++ b/Mage.Sets/src/mage/cards/u/UrabraskTheHidden.java @@ -1,30 +1,28 @@ package mage.cards.u; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.PermanentsEnterBattlefieldTappedEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.filter.common.FilterControlledCreaturePermanent; -import mage.game.Game; -import mage.game.events.EntersTheBattlefieldEvent; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * * @author Loki */ public final class UrabraskTheHidden extends CardImpl { public UrabraskTheHidden(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{R}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{R}"); addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.PHYREXIAN); this.subtype.add(SubType.PRAETOR); @@ -32,8 +30,14 @@ public final class UrabraskTheHidden extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(4); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledEffect(HasteAbility.getInstance(), Duration.WhileOnBattlefield, new FilterControlledCreaturePermanent("Creatures")))); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new UrabraskTheHiddenEffect())); + this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( + HasteAbility.getInstance(), Duration.WhileOnBattlefield, + StaticFilters.FILTER_PERMANENT_CREATURES + ))); + + this.addAbility(new SimpleStaticAbility(new PermanentsEnterBattlefieldTappedEffect( + StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE + ).setText("creatures your opponents control enter the battlefield tapped"))); } private UrabraskTheHidden(final UrabraskTheHidden card) { @@ -45,45 +49,3 @@ public final class UrabraskTheHidden extends CardImpl { return new UrabraskTheHidden(this); } } - -class UrabraskTheHiddenEffect extends ReplacementEffectImpl { - - UrabraskTheHiddenEffect() { - super(Duration.WhileOnBattlefield, Outcome.Tap); - staticText = "Creatures your opponents control enter the battlefield tapped"; - } - - UrabraskTheHiddenEffect(final UrabraskTheHiddenEffect effect) { - super(effect); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Permanent target = ((EntersTheBattlefieldEvent) event).getTarget(); - if (target != null) { - target.setTapped(true); - } - return false; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (game.getOpponents(source.getControllerId()).contains(event.getPlayerId())) { - Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget(); - if (permanent != null && permanent.isCreature(game)) { - return true; - } - } - return false; - } - - @Override - public UrabraskTheHiddenEffect copy() { - return new UrabraskTheHiddenEffect(this); - } -} diff --git a/Mage.Sets/src/mage/cards/u/UrborgUprising.java b/Mage.Sets/src/mage/cards/u/UrborgUprising.java index 4bd67178ba7..ba255c80bf2 100644 --- a/Mage.Sets/src/mage/cards/u/UrborgUprising.java +++ b/Mage.Sets/src/mage/cards/u/UrborgUprising.java @@ -1,4 +1,3 @@ - package mage.cards.u; import java.util.UUID; @@ -7,7 +6,7 @@ import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterCreatureCard; +import mage.filter.StaticFilters; import mage.target.common.TargetCardInYourGraveyard; /** @@ -20,7 +19,7 @@ public final class UrborgUprising extends CardImpl { // Return up to two target creature cards from your graveyard to your hand. this.getSpellAbility().addEffect(new ReturnFromGraveyardToHandTargetEffect()); - this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, 2, new FilterCreatureCard("creature cards from your graveyard"))); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, 2, StaticFilters.FILTER_CARD_CREATURES_YOUR_GRAVEYARD)); // Draw a card. this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); diff --git a/Mage.Sets/src/mage/cards/u/UrzaAcademyHeadmaster.java b/Mage.Sets/src/mage/cards/u/UrzaAcademyHeadmaster.java index 43021ee3c4c..56481232f39 100644 --- a/Mage.Sets/src/mage/cards/u/UrzaAcademyHeadmaster.java +++ b/Mage.Sets/src/mage/cards/u/UrzaAcademyHeadmaster.java @@ -3,7 +3,6 @@ package mage.cards.u; import mage.Mana; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.dynamicvalue.common.ControllerLifeCount; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.dynamicvalue.common.PermanentsTargetOpponentControlsCount; @@ -55,7 +54,7 @@ public final class UrzaAcademyHeadmaster extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.URZA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Head to AskUrza.com and click +1. this.addAbility(new LoyaltyAbility(new UrzaAcademyHeadmasterRandomEffect(1, setInfo), 1)); diff --git a/Mage.Sets/src/mage/cards/u/UrzasEngine.java b/Mage.Sets/src/mage/cards/u/UrzasEngine.java index 2b4205c9757..11a324795e3 100644 --- a/Mage.Sets/src/mage/cards/u/UrzasEngine.java +++ b/Mage.Sets/src/mage/cards/u/UrzasEngine.java @@ -1,5 +1,3 @@ - - package mage.cards.u; import java.util.UUID; @@ -29,8 +27,8 @@ import mage.target.targetpointer.FixedTarget; */ public final class UrzasEngine extends CardImpl { - public UrzasEngine (UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{5}"); + public UrzasEngine(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{5}"); this.subtype.add(SubType.JUGGERNAUT); this.power = new MageInt(1); this.toughness = new MageInt(5); @@ -45,7 +43,7 @@ public final class UrzasEngine extends CardImpl { this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new UrzasEngineEffect(), new ManaCostsImpl("{3}"))); } - public UrzasEngine (final UrzasEngine card) { + public UrzasEngine(final UrzasEngine card) { super(card); } @@ -78,9 +76,12 @@ class UrzasEngineEffect extends OneShotEffect { if (sourcePermanent != null) { for (UUID bandedId : sourcePermanent.getBandedCards()) { Permanent banded = game.getPermanent(bandedId); - if (banded != null && banded.isAttacking() && banded.getBandedCards() != null && banded.getBandedCards().contains(sourcePermanent.getId())) { + if (banded != null + && banded.isAttacking() + && banded.getBandedCards() != null + && banded.getBandedCards().contains(sourcePermanent.getId())) { GainAbilityTargetEffect effect = new GainAbilityTargetEffect(TrampleAbility.getInstance(), Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(bandedId)); + effect.setTargetPointer(new FixedTarget(bandedId, game)); game.addEffect(effect, source); } } diff --git a/Mage.Sets/src/mage/cards/u/UrzasSaga.java b/Mage.Sets/src/mage/cards/u/UrzasSaga.java index 473d00d0727..24c49da05aa 100644 --- a/Mage.Sets/src/mage/cards/u/UrzasSaga.java +++ b/Mage.Sets/src/mage/cards/u/UrzasSaga.java @@ -45,7 +45,7 @@ public final class UrzasSaga extends CardImpl { this.subtype.add(SubType.SAGA); // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) - SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); + SagaAbility sagaAbility = new SagaAbility(this); // I — Urza's Saga gains "{T}: Add {C}." sagaAbility.addChapterEffect( diff --git a/Mage.Sets/src/mage/cards/v/ValentinDeanOfTheVein.java b/Mage.Sets/src/mage/cards/v/ValentinDeanOfTheVein.java index 113ebfeda14..47d2369a315 100644 --- a/Mage.Sets/src/mage/cards/v/ValentinDeanOfTheVein.java +++ b/Mage.Sets/src/mage/cards/v/ValentinDeanOfTheVein.java @@ -44,7 +44,7 @@ public final class ValentinDeanOfTheVein extends ModalDoubleFacesCard { this.getLeftHalfCard().setPT(1, 1); // Menace - this.getLeftHalfCard().addAbility(new MenaceAbility()); + this.getLeftHalfCard().addAbility(new MenaceAbility(false)); // Lifelink this.getLeftHalfCard().addAbility(LifelinkAbility.getInstance()); diff --git a/Mage.Sets/src/mage/cards/v/ValiantRescuer.java b/Mage.Sets/src/mage/cards/v/ValiantRescuer.java index 52df6596b74..3014e4aad79 100644 --- a/Mage.Sets/src/mage/cards/v/ValiantRescuer.java +++ b/Mage.Sets/src/mage/cards/v/ValiantRescuer.java @@ -16,6 +16,7 @@ import mage.game.events.GameEvent; import mage.game.permanent.token.HumanSoldierToken; import mage.game.stack.StackAbility; import mage.game.stack.StackObject; +import mage.util.CardUtil; import mage.watchers.Watcher; import java.util.HashMap; @@ -114,7 +115,7 @@ class ValiantRescuerWatcher extends Watcher { && item.getStackAbility() instanceof CyclingAbility) { playerMap.computeIfAbsent(event.getPlayerId(), u -> new HashMap<>()); playerMap.get(event.getPlayerId()).compute( - event.getSourceId(), (u, i) -> i == null ? 1 : Integer.sum(i, 1) + event.getSourceId(), CardUtil::setOrIncrementValue ); } } diff --git a/Mage.Sets/src/mage/cards/v/ValkiGodOfLies.java b/Mage.Sets/src/mage/cards/v/ValkiGodOfLies.java index dee95d173be..92ae7b85206 100644 --- a/Mage.Sets/src/mage/cards/v/ValkiGodOfLies.java +++ b/Mage.Sets/src/mage/cards/v/ValkiGodOfLies.java @@ -11,7 +11,6 @@ import mage.abilities.DelayedTriggeredAbility; import mage.abilities.LoyaltyAbility; import mage.abilities.common.AsEntersBattlefieldAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.ContinuousEffect; @@ -67,7 +66,7 @@ public final class ValkiGodOfLies extends ModalDoubleFacesCard { // Tibalt, Cosmic Impostor // Legendary Planeswalker — Tibalt this.getRightHalfCard().addSuperType(SuperType.LEGENDARY); - this.getRightHalfCard().addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.getRightHalfCard().setStartingLoyalty(5); // As Tibalt enters the battlefield, you get an emblem with “You may play cards exiled with Tibalt, Cosmic Impostor, and you may spend mana as though it were mana of any color to cast those spells.” this.getRightHalfCard().addAbility(new AsEntersBattlefieldAbility(new GetEmblemEffect(new TibaltCosmicImpostorEmblem()))); @@ -247,7 +246,7 @@ class ValkiGodOfLiesCopyExiledEffect extends OneShotEffect { Card chosenExiledCard = game.getCard(target.getFirstTarget()); if (chosenExiledCard != null) { ContinuousEffect copyEffect = new CopyEffect(Duration.WhileOnBattlefield, chosenExiledCard.getMainCard(), source.getSourceId()); - copyEffect.setTargetPointer(new FixedTarget(Valki.getId())); + copyEffect.setTargetPointer(new FixedTarget(Valki.getId(), game)); game.addEffect(copyEffect, source); return true; } @@ -335,7 +334,7 @@ class ExileAllCardsFromAllGraveyards extends OneShotEffect { public ExileAllCardsFromAllGraveyards() { super(Outcome.Benefit); - this.staticText = "Exile all cards from all graveyards. Add {R}{R}{R}"; + this.staticText = "Exile all graveyards. Add {R}{R}{R}"; } public ExileAllCardsFromAllGraveyards(final ExileAllCardsFromAllGraveyards effect) { diff --git a/Mage.Sets/src/mage/cards/v/ValorInAkros.java b/Mage.Sets/src/mage/cards/v/ValorInAkros.java index b1aa8d3e395..c26d77bc428 100644 --- a/Mage.Sets/src/mage/cards/v/ValorInAkros.java +++ b/Mage.Sets/src/mage/cards/v/ValorInAkros.java @@ -24,7 +24,7 @@ public final class ValorInAkros extends CardImpl { this.addAbility(new EntersBattlefieldControlledTriggeredAbility( Zone.BATTLEFIELD, new BoostControlledEffect(1, 1, Duration.EndOfTurn), - StaticFilters.FILTER_PERMANENT_CREATURE_A, + StaticFilters.FILTER_PERMANENT_A_CREATURE, false) ); } diff --git a/Mage.Sets/src/mage/cards/v/VampireSlayer.java b/Mage.Sets/src/mage/cards/v/VampireSlayer.java new file mode 100644 index 00000000000..6ff7eb1386a --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VampireSlayer.java @@ -0,0 +1,43 @@ +package mage.cards.v; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.DealsDamageToACreatureTriggeredAbility; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.common.FilterCreaturePermanent; + +/** + * + * @author weirddan455 + */ +public final class VampireSlayer extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(SubType.VAMPIRE, "a Vampire"); + + public VampireSlayer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Whenever Vampire Slayer deals damage to a Vampire, destroy that creature. + this.addAbility(new DealsDamageToACreatureTriggeredAbility( + new DestroyTargetEffect(), false, false, true, filter + )); + } + + private VampireSlayer(final VampireSlayer card) { + super(card); + } + + @Override + public VampireSlayer copy() { + return new VampireSlayer(this); + } +} diff --git a/Mage.Sets/src/mage/cards/v/VampiresKiss.java b/Mage.Sets/src/mage/cards/v/VampiresKiss.java new file mode 100644 index 00000000000..ceda7bbd959 --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VampiresKiss.java @@ -0,0 +1,37 @@ +package mage.cards.v; + +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.LoseLifeTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.game.permanent.token.BloodToken; +import mage.target.TargetPlayer; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class VampiresKiss extends CardImpl { + + public VampiresKiss(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}"); + + // Target player loses 2 life and you gain 2 life. Create two Blood tokens. + this.getSpellAbility().addEffect(new LoseLifeTargetEffect(2)); + this.getSpellAbility().addEffect(new GainLifeEffect(2).concatBy("and")); + this.getSpellAbility().addEffect(new CreateTokenEffect(new BloodToken(), 2)); + this.getSpellAbility().addTarget(new TargetPlayer()); + } + + private VampiresKiss(final VampiresKiss card) { + super(card); + } + + @Override + public VampiresKiss copy() { + return new VampiresKiss(this); + } +} diff --git a/Mage.Sets/src/mage/cards/v/VampiresVengeance.java b/Mage.Sets/src/mage/cards/v/VampiresVengeance.java new file mode 100644 index 00000000000..53bfdb6c459 --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VampiresVengeance.java @@ -0,0 +1,43 @@ +package mage.cards.v; + +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DamageAllEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.game.permanent.token.BloodToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class VampiresVengeance extends CardImpl { + + private static final FilterPermanent filter = new FilterCreaturePermanent("non-Vampire creature."); + + static { + filter.add(Predicates.not(SubType.VAMPIRE.getPredicate())); + } + + public VampiresVengeance(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{R}"); + + // Vampires' Vengeance deals 2 damage to each non-Vampire creature. Create a Blood token. + this.getSpellAbility().addEffect(new DamageAllEffect(2, filter)); + this.getSpellAbility().addEffect(new CreateTokenEffect(new BloodToken())); + } + + private VampiresVengeance(final VampiresVengeance card) { + super(card); + } + + @Override + public VampiresVengeance copy() { + return new VampiresVengeance(this); + } +} diff --git a/Mage.Sets/src/mage/cards/v/VancesBlastingCannons.java b/Mage.Sets/src/mage/cards/v/VancesBlastingCannons.java index 2c8eac60fff..225ad68753b 100644 --- a/Mage.Sets/src/mage/cards/v/VancesBlastingCannons.java +++ b/Mage.Sets/src/mage/cards/v/VancesBlastingCannons.java @@ -37,7 +37,6 @@ public final class VancesBlastingCannons extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{R}"); this.addSuperType(SuperType.LEGENDARY); - this.transformable = true; this.secondSideCardClazz = mage.cards.s.SpitfireBastion.class; // At the beginning of your upkeep, exile the top card of your library. If it's a nonland card, you may cast that card this turn. @@ -132,7 +131,7 @@ class CastFromNonHandZoneTargetEffect extends AsThoughEffectImpl { class VancesBlastingCannonsFlipTrigger extends TriggeredAbilityImpl { public VancesBlastingCannonsFlipTrigger() { - super(Zone.BATTLEFIELD, new TransformSourceEffect(true), true); + super(Zone.BATTLEFIELD, new TransformSourceEffect(), true); } public VancesBlastingCannonsFlipTrigger(final VancesBlastingCannonsFlipTrigger ability) { diff --git a/Mage.Sets/src/mage/cards/v/VarinaLichQueen.java b/Mage.Sets/src/mage/cards/v/VarinaLichQueen.java index 9022a33f65f..4a0134a0c22 100644 --- a/Mage.Sets/src/mage/cards/v/VarinaLichQueen.java +++ b/Mage.Sets/src/mage/cards/v/VarinaLichQueen.java @@ -17,7 +17,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Zone; -import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -51,7 +51,7 @@ public final class VarinaLichQueen extends CardImpl { ), new GenericManaCost(2) ); ability.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard( - 2, new FilterCard("cards from your graveyard") + 2, StaticFilters.FILTER_CARDS_FROM_YOUR_GRAVEYARD ))); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/v/VaultOfTheArchangel.java b/Mage.Sets/src/mage/cards/v/VaultOfTheArchangel.java index 5cf42beabd8..5d8f355900e 100644 --- a/Mage.Sets/src/mage/cards/v/VaultOfTheArchangel.java +++ b/Mage.Sets/src/mage/cards/v/VaultOfTheArchangel.java @@ -1,7 +1,5 @@ - package mage.cards.v; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; @@ -14,25 +12,30 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Zone; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * * @author North */ public final class VaultOfTheArchangel extends CardImpl { public VaultOfTheArchangel(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.LAND},""); + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); // {tap}: Add {C}. this.addAbility(new ColorlessManaAbility()); + // {2}{W}{B}, {tap}: Creatures you control gain deathtouch and lifelink until end of turn. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, - new GainAbilityControlledEffect(DeathtouchAbility.getInstance(), Duration.EndOfTurn, new FilterControlledCreaturePermanent("Creatures"), false), - new ManaCostsImpl("{2}{W}{B}")); - ability.addEffect(new GainAbilityControlledEffect(LifelinkAbility.getInstance(), Duration.EndOfTurn, new FilterControlledCreaturePermanent("Creatures"), false)); + Ability ability = new SimpleActivatedAbility(new GainAbilityControlledEffect( + DeathtouchAbility.getInstance(), Duration.EndOfTurn, + StaticFilters.FILTER_PERMANENT_CREATURE + ).setText("creatures you control gain deathtouch"), new ManaCostsImpl<>("{2}{W}{B}")); + ability.addEffect(new GainAbilityControlledEffect( + LifelinkAbility.getInstance(), Duration.EndOfTurn, + StaticFilters.FILTER_PERMANENT_CREATURE + ).setText("and lifelink until end of turn")); ability.addCost(new TapSourceCost()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/v/VectorGlider.java b/Mage.Sets/src/mage/cards/v/VectorGlider.java new file mode 100644 index 00000000000..bab0f279c24 --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VectorGlider.java @@ -0,0 +1,38 @@ +package mage.cards.v; + +import mage.MageInt; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class VectorGlider extends CardImpl { + + public VectorGlider(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, ""); + + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + this.color.setBlue(true); + this.nightCard = true; + + // Flying + this.addAbility(FlyingAbility.getInstance()); + } + + private VectorGlider(final VectorGlider card) { + super(card); + } + + @Override + public VectorGlider copy() { + return new VectorGlider(this); + } +} diff --git a/Mage.Sets/src/mage/cards/v/VedalkenShackles.java b/Mage.Sets/src/mage/cards/v/VedalkenShackles.java index 2c6adc7e209..8e895739159 100644 --- a/Mage.Sets/src/mage/cards/v/VedalkenShackles.java +++ b/Mage.Sets/src/mage/cards/v/VedalkenShackles.java @@ -44,7 +44,7 @@ public final class VedalkenShackles extends CardImpl { // {2}, {tap}: Gain control of target creature with power less than or equal to the number of Islands you control for as long as Vedalken Shackles remains tapped. ConditionalContinuousEffect effect = new ConditionalContinuousEffect( - new GainControlTargetEffect(Duration.Custom), SourceTappedCondition.instance, + new GainControlTargetEffect(Duration.Custom), SourceTappedCondition.TAPPED, "Gain control of target creature with power less than or equal to the number of Islands you control for as long as {this} remains tapped"); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new GenericManaCost(2)); ability.addCost(new TapSourceCost()); diff --git a/Mage.Sets/src/mage/cards/v/VeilOfBirds.java b/Mage.Sets/src/mage/cards/v/VeilOfBirds.java index bfe2b42e189..6c21620d77f 100644 --- a/Mage.Sets/src/mage/cards/v/VeilOfBirds.java +++ b/Mage.Sets/src/mage/cards/v/VeilOfBirds.java @@ -31,7 +31,7 @@ public final class VeilOfBirds extends CardImpl { // When an opponent casts a spell, if Veil of Birds is an enchantment, Veil of Birds becomes a 1/1 Bird creature with flying. TriggeredAbility ability = new SpellCastOpponentTriggeredAbility(new BecomesCreatureSourceEffect(new VeilOfBirdsToken(), "", Duration.WhileOnBattlefield, true, false), filter, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_ENCHANTMENT_PERMANENT), + this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_PERMANENT_ENCHANTMENT), "Whenever an opponent casts a spell, if Veil of Birds is an enchantment, Veil of Birds becomes a 1/1 Bird creature with flying.")); } diff --git a/Mage.Sets/src/mage/cards/v/VeiledApparition.java b/Mage.Sets/src/mage/cards/v/VeiledApparition.java index a19ac3f3347..d2c0e860b69 100644 --- a/Mage.Sets/src/mage/cards/v/VeiledApparition.java +++ b/Mage.Sets/src/mage/cards/v/VeiledApparition.java @@ -37,7 +37,7 @@ public final class VeiledApparition extends CardImpl { // When an opponent casts a spell, if Veiled Apparition is an enchantment, Veiled Apparition becomes a 3/3 Illusion creature with flying and "At the beginning of your upkeep, sacrifice Veiled Apparition unless you pay {1}{U}." TriggeredAbility ability = new SpellCastOpponentTriggeredAbility(new BecomesCreatureSourceEffect(new VeilApparitionToken(), "", Duration.WhileOnBattlefield, true, false), filter, false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_ENCHANTMENT_PERMANENT), + this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_PERMANENT_ENCHANTMENT), "Whenever an opponent casts a spell, if Veiled Apparition is an enchantment, Veiled Apparition becomes a 3/3 Illusion creature with flying and \"At the beginning of your upkeep, sacrifice Veiled Apparition unless you pay {1}{U}.")); } diff --git a/Mage.Sets/src/mage/cards/v/VeiledSentry.java b/Mage.Sets/src/mage/cards/v/VeiledSentry.java index 3a95e0c49b7..bef85b0c6c0 100644 --- a/Mage.Sets/src/mage/cards/v/VeiledSentry.java +++ b/Mage.Sets/src/mage/cards/v/VeiledSentry.java @@ -27,7 +27,7 @@ public final class VeiledSentry extends CardImpl { // When an opponent casts a spell, if Veiled Sentry is an enchantment, Veiled Sentry becomes an Illusion creature with power and toughness each equal to that spell's converted mana cost. TriggeredAbility ability = new SpellCastOpponentTriggeredAbility(Zone.BATTLEFIELD, new VeiledSentryEffect(), new FilterSpell(), false, SetTargetPointer.SPELL); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_ENCHANTMENT_PERMANENT), + this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_PERMANENT_ENCHANTMENT), "Whenever an opponent casts a spell, if Veiled Sentry is an enchantment, Veil Sentry becomes an Illusion creature with power and toughness equal to that spell's mana value.")); } diff --git a/Mage.Sets/src/mage/cards/v/VeiledSerpent.java b/Mage.Sets/src/mage/cards/v/VeiledSerpent.java index 8dc78141869..a17d802266c 100644 --- a/Mage.Sets/src/mage/cards/v/VeiledSerpent.java +++ b/Mage.Sets/src/mage/cards/v/VeiledSerpent.java @@ -34,7 +34,7 @@ public final class VeiledSerpent extends CardImpl { // When an opponent casts a spell, if Veiled Serpent is an enchantment, Veiled Serpent becomes a 4/4 Serpent creature that can't attack unless defending player controls an Island. TriggeredAbility ability = new SpellCastOpponentTriggeredAbility(new BecomesCreatureSourceEffect(new VeiledSerpentToken(), "", Duration.WhileOnBattlefield, true, false), new FilterSpell(), false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_ENCHANTMENT_PERMANENT), + this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_PERMANENT_ENCHANTMENT), "Whenever an opponent casts a spell, if Veiled Serpent is an enchantment, Veiled Serpent becomes a 4/4 Serpent creature that can't attack unless defending player controls an Island.")); // Cycling {2} diff --git a/Mage.Sets/src/mage/cards/v/VeinwitchCoven.java b/Mage.Sets/src/mage/cards/v/VeinwitchCoven.java index c7df40fe6b7..46bd550085d 100644 --- a/Mage.Sets/src/mage/cards/v/VeinwitchCoven.java +++ b/Mage.Sets/src/mage/cards/v/VeinwitchCoven.java @@ -30,7 +30,7 @@ public final class VeinwitchCoven extends CardImpl { this.toughness = new MageInt(3); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // Whenever you gain life, you pay {B}. If you do, return target creature card from your graveyard to your hand. Ability ability = new GainLifeControllerTriggeredAbility(new DoIfCostPaid( diff --git a/Mage.Sets/src/mage/cards/v/Vendetta.java b/Mage.Sets/src/mage/cards/v/Vendetta.java index 09990c355d3..cc2b4e2ae1f 100644 --- a/Mage.Sets/src/mage/cards/v/Vendetta.java +++ b/Mage.Sets/src/mage/cards/v/Vendetta.java @@ -1,8 +1,6 @@ - package mage.cards.v; import java.util.UUID; -import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DestroyTargetEffect; @@ -10,9 +8,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ColorPredicate; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -24,18 +20,11 @@ import mage.target.common.TargetCreaturePermanent; */ public final class Vendetta extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public Vendetta(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{B}"); - // Destroy target nonblack creature. It can't be regenerated. You lose life equal to that creature's toughness. - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); this.getSpellAbility().addEffect(new DestroyTargetEffect(true)); this.getSpellAbility().addEffect(new VendettaEffect()); } diff --git a/Mage.Sets/src/mage/cards/v/VeneratedTeacher.java b/Mage.Sets/src/mage/cards/v/VeneratedTeacher.java index d88fc3e25ce..2790b994444 100644 --- a/Mage.Sets/src/mage/cards/v/VeneratedTeacher.java +++ b/Mage.Sets/src/mage/cards/v/VeneratedTeacher.java @@ -14,7 +14,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Outcome; import mage.counters.CounterType; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; @@ -48,8 +48,6 @@ public final class VeneratedTeacher extends CardImpl { class VeneratedTeacherEffect extends OneShotEffect { - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("creatures you control"); - public VeneratedTeacherEffect() { super(Outcome.BoostCreature); staticText = "put two level counters on each creature you control with level up"; @@ -61,7 +59,7 @@ class VeneratedTeacherEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - List permanents = game.getBattlefield().getAllActivePermanents(filter, source.getControllerId(), game); + List permanents = game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_CONTROLLED_CREATURES, source.getControllerId(), game); if (!permanents.isEmpty()) { for (Permanent permanent : permanents) { for (Ability ability : permanent.getAbilities()) { diff --git a/Mage.Sets/src/mage/cards/v/VengefulAncestor.java b/Mage.Sets/src/mage/cards/v/VengefulAncestor.java new file mode 100644 index 00000000000..19b31e4d148 --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VengefulAncestor.java @@ -0,0 +1,106 @@ +package mage.cards.v; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksAllTriggeredAbility; +import mage.abilities.common.EntersBattlefieldOrAttacksSourceTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.combat.GoadTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SetTargetPointer; +import mage.constants.SubType; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class VengefulAncestor extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a goaded creature"); + + static { + filter.add(VengefulAncestorPredicate.instance); + } + + public VengefulAncestor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{R}"); + + this.subtype.add(SubType.SPIRIT); + this.subtype.add(SubType.DRAGON); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever Vengeful Ancestor enters the battlefield or attacks, goad target creature. + Ability ability = new EntersBattlefieldOrAttacksSourceTriggeredAbility(new GoadTargetEffect()); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + + // Whenever a goaded creature attacks, it deals 1 damage to its controller. + this.addAbility(new AttacksAllTriggeredAbility( + new VengefulAncestorEffect(), false, filter, + SetTargetPointer.NONE, false + )); + } + + private VengefulAncestor(final VengefulAncestor card) { + super(card); + } + + @Override + public VengefulAncestor copy() { + return new VengefulAncestor(this); + } +} + +enum VengefulAncestorPredicate implements Predicate { + instance; + + @Override + public boolean apply(Permanent input, Game game) { + return !input.getGoadingPlayers().isEmpty(); + } +} + +class VengefulAncestorEffect extends OneShotEffect { + + VengefulAncestorEffect() { + super(Outcome.Benefit); + staticText = "it deals 1 damage to its controller"; + } + + private VengefulAncestorEffect(final VengefulAncestorEffect effect) { + super(effect); + } + + @Override + public VengefulAncestorEffect copy() { + return new VengefulAncestorEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = (Permanent) getValue("attacker"); + if (permanent == null) { + return false; + } + Player player = game.getPlayer(permanent.getControllerId()); + if (player == null) { + return false; + } + return player.damage(1, permanent.getId(), source, game) > 0; + } +} diff --git a/Mage.Sets/src/mage/cards/v/VengefulStrangler.java b/Mage.Sets/src/mage/cards/v/VengefulStrangler.java index f1ca4ac0437..1b1de179298 100644 --- a/Mage.Sets/src/mage/cards/v/VengefulStrangler.java +++ b/Mage.Sets/src/mage/cards/v/VengefulStrangler.java @@ -38,7 +38,6 @@ public final class VengefulStrangler extends CardImpl { this.subtype.add(SubType.ROGUE); this.power = new MageInt(2); this.toughness = new MageInt(1); - this.transformable = true; this.secondSideCardClazz = mage.cards.s.StranglingGrasp.class; // Vengeful Strangler can't block. diff --git a/Mage.Sets/src/mage/cards/v/Venom.java b/Mage.Sets/src/mage/cards/v/Venom.java index ff7170f8e7c..a1106e9dce1 100644 --- a/Mage.Sets/src/mage/cards/v/Venom.java +++ b/Mage.Sets/src/mage/cards/v/Venom.java @@ -1,4 +1,3 @@ - package mage.cards.v; import java.util.Objects; @@ -19,7 +18,6 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; @@ -31,7 +29,7 @@ import mage.target.targetpointer.FixedTarget; public final class Venom extends CardImpl { public Venom(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{G}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{G}{G}"); this.subtype.add(SubType.AURA); // Enchant creature @@ -86,15 +84,17 @@ class VenomTriggeredAbility extends TriggeredAbilityImpl { if (enchantment != null && enchantment.getAttachedTo() != null) { Permanent enchantedCreature = game.getPermanent(enchantment.getAttachedTo()); if (enchantedCreature != null) { - if (blocker != null && !Objects.equals(blocker, enchantedCreature) + if (blocker != null + && !Objects.equals(blocker, enchantedCreature) && !blocker.hasSubtype(SubType.WALL, game) && Objects.equals(blocked, enchantedCreature)) { - this.getEffects().get(0).setTargetPointer(new FixedTarget(blocker.getId())); + this.getEffects().get(0).setTargetPointer(new FixedTarget(blocker.getId(), game)); return true; } - if (blocker != null && Objects.equals(blocker, enchantedCreature) + if (blocker != null + && Objects.equals(blocker, enchantedCreature) && !blocked.hasSubtype(SubType.WALL, game)) { - this.getEffects().get(0).setTargetPointer(new FixedTarget(blocked.getId())); + this.getEffects().get(0).setTargetPointer(new FixedTarget(blocked.getId(), game)); return true; } } diff --git a/Mage.Sets/src/mage/cards/v/VenomSliver.java b/Mage.Sets/src/mage/cards/v/VenomSliver.java index c0335b474d1..4ae8cbbf267 100644 --- a/Mage.Sets/src/mage/cards/v/VenomSliver.java +++ b/Mage.Sets/src/mage/cards/v/VenomSliver.java @@ -30,7 +30,7 @@ public final class VenomSliver extends CardImpl { // Sliver creatures you control have deathtouch. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledEffect(DeathtouchAbility.getInstance(), - Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS))); + Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_SLIVERS))); } private VenomSliver(final VenomSliver card) { diff --git a/Mage.Sets/src/mage/cards/v/VenserTheSojourner.java b/Mage.Sets/src/mage/cards/v/VenserTheSojourner.java index 1afea41ade6..72974ed9c53 100644 --- a/Mage.Sets/src/mage/cards/v/VenserTheSojourner.java +++ b/Mage.Sets/src/mage/cards/v/VenserTheSojourner.java @@ -5,7 +5,6 @@ import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; @@ -41,7 +40,7 @@ public final class VenserTheSojourner extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.VENSER); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +2: Exile target permanent you own. Return it to the battlefield under your control at the beginning of the next end step. LoyaltyAbility ability1 = new LoyaltyAbility(new VenserTheSojournerEffect(), 2); diff --git a/Mage.Sets/src/mage/cards/v/VerdelothTheAncient.java b/Mage.Sets/src/mage/cards/v/VerdelothTheAncient.java index d8269f60b52..7e243abcc7c 100644 --- a/Mage.Sets/src/mage/cards/v/VerdelothTheAncient.java +++ b/Mage.Sets/src/mage/cards/v/VerdelothTheAncient.java @@ -1,6 +1,7 @@ package mage.cards.v; import mage.MageInt; +import mage.MageObject; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.KickedCondition; @@ -11,10 +12,15 @@ import mage.abilities.effects.common.continuous.BoostAllEffect; import mage.abilities.keyword.KickerAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.PermanentIdPredicate; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; import mage.game.permanent.token.SaprolingToken; import java.util.UUID; @@ -24,6 +30,13 @@ import java.util.UUID; */ public final class VerdelothTheAncient extends CardImpl { + private static final FilterCreaturePermanent filter + = new FilterCreaturePermanent("Saproling creatures and other Treefolk creatures"); + + static { + filter.add(VerdelothTheAncientPredicate.instance); + } + public VerdelothTheAncient(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}{G}"); addSuperType(SuperType.LEGENDARY); @@ -36,20 +49,15 @@ public final class VerdelothTheAncient extends CardImpl { this.addAbility(new KickerAbility("{X}")); // Saproling creatures and other Treefolk creatures get +1/+1. - FilterCreaturePermanent filter = new FilterCreaturePermanent("Saproling creatures and other Treefolk creatures"); - filter.add(Predicates.or( - Predicates.and(SubType.TREEFOLK.getPredicate(), Predicates.not(new PermanentIdPredicate(this.getId()))), - SubType.SAPROLING.getPredicate()) - ); - filter.add(Predicates.not(new PermanentIdPredicate(this.getId()))); - - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostAllEffect(1, 1, Duration.WhileOnBattlefield, filter, false))); + this.addAbility(new SimpleStaticAbility(new BoostAllEffect( + 1, 1, Duration.WhileOnBattlefield, filter, false + ))); // When Verdeloth the Ancient enters the battlefield, if it was kicked, create X 1/1 green Saproling creature tokens. - EntersBattlefieldTriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new SaprolingToken(), GetKickerXValue.instance), false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, KickedCondition.instance, - "When {this} enters the battlefield, if it was kicked, create X 1/1 green Saproling creature tokens.")); - + this.addAbility(new ConditionalInterveningIfTriggeredAbility(new EntersBattlefieldTriggeredAbility( + new CreateTokenEffect(new SaprolingToken(), GetKickerXValue.instance), false + ), KickedCondition.instance, "When {this} enters the battlefield, " + + "if it was kicked, create X 1/1 green Saproling creature tokens.")); } private VerdelothTheAncient(final VerdelothTheAncient card) { @@ -60,4 +68,18 @@ public final class VerdelothTheAncient extends CardImpl { public VerdelothTheAncient copy() { return new VerdelothTheAncient(this); } -} \ No newline at end of file +} + +enum VerdelothTheAncientPredicate implements ObjectSourcePlayerPredicate { + instance; + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + MageObject obj = input.getObject(); + if (obj.getId().equals(input.getSourceId())) { + return obj.hasSubtype(SubType.SAPROLING, game); + } + return obj.hasSubtype(SubType.TREEFOLK, game) + || obj.hasSubtype(SubType.SAPROLING, game); + } +} diff --git a/Mage.Sets/src/mage/cards/v/VerityCircle.java b/Mage.Sets/src/mage/cards/v/VerityCircle.java index 18cf5119a35..f34265717bc 100644 --- a/Mage.Sets/src/mage/cards/v/VerityCircle.java +++ b/Mage.Sets/src/mage/cards/v/VerityCircle.java @@ -1,8 +1,8 @@ package mage.cards.v; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.TappedNotAttackingTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.TapTargetEffect; @@ -10,14 +10,9 @@ import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.AbilityPredicate; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.common.TargetCreaturePermanent; import java.util.UUID; @@ -37,10 +32,10 @@ public final class VerityCircle extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}"); // Whenever a creature an opponent controls becomes tapped, if it isn't being declared as an attacker, you may draw a card. - this.addAbility(new VerityCircleTriggeredAbility()); + this.addAbility(new TappedNotAttackingTriggeredAbility(new DrawCardSourceControllerEffect(1), true)); // {4}{U}: Tap target creature without flying. - Ability ability = new SimpleActivatedAbility(new TapTargetEffect(), new ManaCostsImpl("{4}{U}")); + Ability ability = new SimpleActivatedAbility(new TapTargetEffect(), new ManaCostsImpl<>("{4}{U}")); ability.addTarget(new TargetCreaturePermanent(filter)); this.addAbility(ability); } @@ -54,41 +49,3 @@ public final class VerityCircle extends CardImpl { return new VerityCircle(this); } } - -class VerityCircleTriggeredAbility extends TriggeredAbilityImpl { - - VerityCircleTriggeredAbility() { - super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), true); - } - - private VerityCircleTriggeredAbility(final VerityCircleTriggeredAbility ability) { - super(ability); - } - - @Override - public VerityCircleTriggeredAbility copy() { - return new VerityCircleTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TAPPED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getFlag()) { - return false; - } - Permanent permanent = game.getPermanent(event.getTargetId()); - Player player = game.getPlayer(controllerId); - return permanent != null && player != null && permanent.isCreature(game) - && player.hasOpponent(permanent.getControllerId(), game); - } - - @Override - public String getRule() { - return "Whenever a creature an opponent controls becomes tapped, " + - "if it isn't being declared as an attacker, you may draw a card."; - } -} diff --git a/Mage.Sets/src/mage/cards/v/VesselOfTheAllConsuming.java b/Mage.Sets/src/mage/cards/v/VesselOfTheAllConsuming.java new file mode 100644 index 00000000000..c032a42e8ef --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VesselOfTheAllConsuming.java @@ -0,0 +1,139 @@ +package mage.cards.v; + +import mage.MageInt; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.DealsDamageToAPlayerTriggeredAbility; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.common.LoseGameTargetPlayerEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.WatcherScope; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.events.DamagedEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.watchers.Watcher; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class VesselOfTheAllConsuming extends CardImpl { + + public VesselOfTheAllConsuming(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, ""); + + this.subtype.add(SubType.OGRE); + this.subtype.add(SubType.SHAMAN); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + this.color.setBlack(true); + this.color.setRed(true); + this.nightCard = true; + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Whenever Vessel of the All-Consuming deals damage, put a +1/+1 counter on it. + this.addAbility(new VesselOfTheAllConsumingTriggeredAbility()); + + // Whenever Vessel of the All-Consuming deals damage to a player, if it has dealt 10 or more damage to that player this turn, they lose the game. + this.addAbility(new ConditionalInterveningIfTriggeredAbility( + new DealsDamageToAPlayerTriggeredAbility( + new LoseGameTargetPlayerEffect(), false, true + ), VesselOfTheAllConsumingWatcher::checkPermanent, "Whenever {this} deals damage to a player, " + + "if it has dealt 10 or more damage to that player this turn, they lose the game." + )); + } + + private VesselOfTheAllConsuming(final VesselOfTheAllConsuming card) { + super(card); + } + + @Override + public VesselOfTheAllConsuming copy() { + return new VesselOfTheAllConsuming(this); + } + + public static Watcher makeWatcher() { + return new VesselOfTheAllConsumingWatcher(); + } +} + +class VesselOfTheAllConsumingTriggeredAbility extends TriggeredAbilityImpl { + + VesselOfTheAllConsumingTriggeredAbility() { + super(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.P1P1.createInstance())); + } + + private VesselOfTheAllConsumingTriggeredAbility(final VesselOfTheAllConsumingTriggeredAbility ability) { + super(ability); + } + + @Override + public VesselOfTheAllConsumingTriggeredAbility copy() { + return new VesselOfTheAllConsumingTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event instanceof DamagedEvent; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return event.getSourceId().equals(getSourceId()); + } + + @Override + public String getRule() { + return "Whenever {this} deals damage, put a +1/+1 counter on it."; + } +} + +class VesselOfTheAllConsumingWatcher extends Watcher { + + private final Map> morMap = new HashMap<>(); + private static final Map emptyMap = new HashMap<>(); + + VesselOfTheAllConsumingWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() != GameEvent.EventType.DAMAGED_PLAYER) { + return; + } + Permanent permanent = game.getPermanent(event.getSourceId()); + if (permanent != null) { + morMap.computeIfAbsent(new MageObjectReference(permanent, game), x -> new HashMap<>()) + .compute(event.getTargetId(), (u, i) -> i == null ? 1 : Integer.sum(i, 1)); + } + } + + @Override + public void reset() { + super.reset(); + morMap.clear(); + } + + static boolean checkPermanent(Game game, Ability source) { + return game.getState() + .getWatcher(VesselOfTheAllConsumingWatcher.class) + .morMap + .getOrDefault(new MageObjectReference(source), emptyMap) + .getOrDefault(source.getEffects().get(0).getTargetPointer().getFirst(game, source), 0) >= 10; + } +} diff --git a/Mage.Sets/src/mage/cards/v/VeteranBodyguard.java b/Mage.Sets/src/mage/cards/v/VeteranBodyguard.java index 8ff8e5eda16..c121568e1bd 100644 --- a/Mage.Sets/src/mage/cards/v/VeteranBodyguard.java +++ b/Mage.Sets/src/mage/cards/v/VeteranBodyguard.java @@ -38,7 +38,7 @@ public final class VeteranBodyguard extends CardImpl { // As long as Veteran Bodyguard is untapped, all damage that would be dealt to you by unblocked creatures is dealt to Veteran Bodyguard instead. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalPreventionEffect( new VeteranBodyguardEffect(), - new InvertCondition(SourceTappedCondition.instance), + SourceTappedCondition.UNTAPPED, "As long as {this} is untapped, all damage that would be dealt to you by unblocked creatures is dealt to {this} instead." ))); } diff --git a/Mage.Sets/src/mage/cards/v/VeteranBrawlers.java b/Mage.Sets/src/mage/cards/v/VeteranBrawlers.java index 8e44127cd11..a55ff32d49b 100644 --- a/Mage.Sets/src/mage/cards/v/VeteranBrawlers.java +++ b/Mage.Sets/src/mage/cards/v/VeteranBrawlers.java @@ -31,8 +31,6 @@ public final class VeteranBrawlers extends CardImpl { filter.add(TappedPredicate.UNTAPPED); } - static final private String rule = "{this} can't block if you control an untapped land"; - public VeteranBrawlers(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); this.subtype.add(SubType.HUMAN); @@ -64,7 +62,7 @@ class VeteranBrawlersCantBlockEffect extends RestrictionEffect { public VeteranBrawlersCantBlockEffect(FilterPermanent filter) { super(Duration.WhileOnBattlefield); this.filter = filter; - staticText = "{this} can't attack if you control " + filter.getMessage(); + staticText = "{this} can't block if you control " + filter.getMessage(); } public VeteranBrawlersCantBlockEffect(final VeteranBrawlersCantBlockEffect effect) { diff --git a/Mage.Sets/src/mage/cards/v/VeteransArmaments.java b/Mage.Sets/src/mage/cards/v/VeteransArmaments.java index 2b1caf8de08..7ca6198bd46 100644 --- a/Mage.Sets/src/mage/cards/v/VeteransArmaments.java +++ b/Mage.Sets/src/mage/cards/v/VeteransArmaments.java @@ -1,4 +1,3 @@ - package mage.cards.v; import java.util.UUID; @@ -26,10 +25,8 @@ import mage.filter.common.FilterCreaturePermanent; */ public final class VeteransArmaments extends CardImpl { - private static final FilterPermanent filter = new FilterCreaturePermanent("a Soldier creature"); - static { - filter.add(SubType.SOLDIER.getPredicate()); - } + private static final FilterPermanent filter = new FilterCreaturePermanent(SubType.SOLDIER, "a Soldier creature"); + private static final DynamicValue xValue = new AttackingCreatureCount(); public VeteransArmaments(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.TRIBAL,CardType.ARTIFACT},"{2}"); @@ -37,8 +34,7 @@ public final class VeteransArmaments extends CardImpl { this.subtype.add(SubType.EQUIPMENT); // Equipped creature has "Whenever this creature attacks or blocks, it gets +1/+1 until end of turn for each attacking creature." - DynamicValue attackingCreatures = new AttackingCreatureCount("attacking creature"); - Ability gainedAbility = new AttacksOrBlocksTriggeredAbility(new BoostSourceEffect(attackingCreatures,attackingCreatures, Duration.EndOfTurn),false); + Ability gainedAbility = new AttacksOrBlocksTriggeredAbility(new BoostSourceEffect(xValue, xValue, Duration.EndOfTurn, true), false); Effect effect = new GainAbilityAttachedEffect(gainedAbility, AttachmentType.EQUIPMENT); effect.setText("Equipped creature has \"Whenever this creature attacks or blocks, it gets +1/+1 until end of turn for each attacking creature.\""); Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, effect); diff --git a/Mage.Sets/src/mage/cards/v/VeteransVoice.java b/Mage.Sets/src/mage/cards/v/VeteransVoice.java index 8c909a52702..b8e8cec615e 100644 --- a/Mage.Sets/src/mage/cards/v/VeteransVoice.java +++ b/Mage.Sets/src/mage/cards/v/VeteransVoice.java @@ -1,9 +1,8 @@ - package mage.cards.v; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.ActivateIfConditionActivatedAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.AttachedToMatchesFilterCondition; import mage.abilities.costs.common.TapAttachedCost; import mage.abilities.effects.common.AttachEffect; @@ -11,33 +10,33 @@ import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Outcome; -import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.Predicate; import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.PermanentIdPredicate; import mage.filter.predicate.permanent.TappedPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.targetadjustment.TargetAdjuster; + +import java.util.UUID; /** - * * @author L_J */ public final class VeteransVoice extends CardImpl { - private static final FilterCreaturePermanent filterUntapped = new FilterCreaturePermanent("enchanted creature is untapped"); - + private static final FilterPermanent filterUntapped = new FilterCreaturePermanent("enchanted creature is untapped"); + static { filterUntapped.add(TappedPredicate.UNTAPPED); } + private static final Condition condition = new AttachedToMatchesFilterCondition(filterUntapped); + public VeteransVoice(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{R}"); this.subtype.add(SubType.AURA); @@ -46,16 +45,15 @@ public final class VeteransVoice extends CardImpl { TargetPermanent auraTarget = new TargetControlledCreaturePermanent(); this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); - Ability ability = new EnchantAbility(auraTarget.getTargetName()); - this.addAbility(ability); + this.addAbility(new EnchantAbility(auraTarget.getTargetName())); // Tap enchanted creature: Target creature other than the creature tapped this way gets +2/+1 until end of turn. Activate this ability only if enchanted creature is untapped. - FilterPermanent filterTarget = new FilterCreaturePermanent("creature other than the creature tapped this way"); - filterTarget.add(Predicates.not(new AttachmentByUUIDPredicate(this.getId()))); - Ability ability2 = new ActivateIfConditionActivatedAbility(Zone.BATTLEFIELD, - new BoostTargetEffect(2, 1, Duration.EndOfTurn), new TapAttachedCost(), new AttachedToMatchesFilterCondition(filterUntapped)); - ability2.addTarget(new TargetPermanent(filterTarget)); - this.addAbility(ability2); + this.addAbility(new ActivateIfConditionActivatedAbility( + Zone.BATTLEFIELD, + new BoostTargetEffect(2, 1, Duration.EndOfTurn) + .setText("target creature other than the creature tapped this way gets +2/+1 until end of turn"), + new TapAttachedCost(), condition + ).setTargetAdjuster(VeteransVoiceAdjuster.instance)); } private VeteransVoice(final VeteransVoice card) { @@ -68,21 +66,18 @@ public final class VeteransVoice extends CardImpl { } } -class AttachmentByUUIDPredicate implements Predicate { - - private final UUID id; - - public AttachmentByUUIDPredicate(UUID id) { - this.id = id; - } +enum VeteransVoiceAdjuster implements TargetAdjuster { + instance; @Override - public boolean apply(Permanent input, Game game) { - return input.getAttachments().contains(id); - } - - @Override - public String toString() { - return "AttachmentUUID(" + id + ')'; + public void adjustTargets(Ability ability, Game game) { + Permanent permanent = ability.getSourcePermanentIfItStillExists(game); + if (permanent == null) { + return; + } + FilterPermanent filter = new FilterCreaturePermanent(); + filter.add(Predicates.not(new PermanentIdPredicate(permanent.getAttachedTo()))); + ability.getTargets().clear(); + ability.addTarget(new TargetPermanent(filter)); } } diff --git a/Mage.Sets/src/mage/cards/v/VeyranVoiceOfDuality.java b/Mage.Sets/src/mage/cards/v/VeyranVoiceOfDuality.java index 53075230be6..00891bbeea6 100644 --- a/Mage.Sets/src/mage/cards/v/VeyranVoiceOfDuality.java +++ b/Mage.Sets/src/mage/cards/v/VeyranVoiceOfDuality.java @@ -53,8 +53,8 @@ class VeyranVoiceOfDualityEffect extends ReplacementEffectImpl { VeyranVoiceOfDualityEffect() { super(Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = "if you casting or copying an instant or sorcery spell causes a triggered ability " + - "of a permanent you control to trigger, that ability triggers an additional time"; + staticText = "if you casting or copying an instant or sorcery spell causes a triggered ability " + + "of a permanent you control to trigger, that ability triggers an additional time"; } private VeyranVoiceOfDualityEffect(final VeyranVoiceOfDualityEffect effect) { @@ -73,18 +73,19 @@ class VeyranVoiceOfDualityEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { - GameEvent sourceEvent = ((NumberOfTriggersEvent) event).getSourceEvent(); - if (sourceEvent.getType() != GameEvent.EventType.COPIED_STACKOBJECT - && sourceEvent.getType() != GameEvent.EventType.SPELL_CAST) { - return false; + NumberOfTriggersEvent numberOfTriggersEvent = (NumberOfTriggersEvent) event; + GameEvent sourceEvent = numberOfTriggersEvent.getSourceEvent(); + if (sourceEvent.getType() == GameEvent.EventType.SPELL_CAST + || sourceEvent.getType() == GameEvent.EventType.COPIED_STACKOBJECT) { + Spell spell = game.getSpell(sourceEvent.getTargetId()); + Permanent permanent = game.getPermanent(((NumberOfTriggersEvent) event).getSourceId()); + return spell != null + && permanent != null + && spell.isInstantOrSorcery(game) + && spell.isControlledBy(source.getControllerId()) + && permanent.isControlledBy(source.getControllerId()); } - Spell spell = game.getSpell(sourceEvent.getTargetId()); - Permanent permanent = game.getPermanent(sourceEvent.getSourceId()); - return spell != null - && permanent != null - && spell.isInstantOrSorcery(game) - && spell.isControlledBy(source.getControllerId()) - && permanent.isControlledBy(source.getControllerId()); + return false; } @Override diff --git a/Mage.Sets/src/mage/cards/v/VictorysHerald.java b/Mage.Sets/src/mage/cards/v/VictorysHerald.java index ab63b432899..b9c60c00bd4 100644 --- a/Mage.Sets/src/mage/cards/v/VictorysHerald.java +++ b/Mage.Sets/src/mage/cards/v/VictorysHerald.java @@ -1,20 +1,18 @@ - - package mage.cards.v; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; -import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.effects.common.continuous.GainAbilityAllEffect; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.LifelinkAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; -import mage.filter.common.FilterAttackingCreature; +import mage.constants.SubType; +import mage.filter.StaticFilters; /** * @@ -33,8 +31,8 @@ public final class VictorysHerald extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever Victory's Herald attacks, attacking creatures gain flying and lifelink until end of turn. - Ability ability = new AttacksTriggeredAbility(new GainAbilityControlledEffect(FlyingAbility.getInstance(), Duration.EndOfTurn, new FilterAttackingCreature()), false); - ability.addEffect(new GainAbilityControlledEffect(LifelinkAbility.getInstance(), Duration.EndOfTurn, new FilterAttackingCreature())); + Ability ability = new AttacksTriggeredAbility(new GainAbilityAllEffect(FlyingAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_ATTACKING_CREATURES, "attacking creatures gain flying"), false); + ability.addEffect(new GainAbilityAllEffect(LifelinkAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_ATTACKING_CREATURES, "and lifelink until end of turn")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/v/VictualSliver.java b/Mage.Sets/src/mage/cards/v/VictualSliver.java index 392f7d0e069..84edf3f8f78 100644 --- a/Mage.Sets/src/mage/cards/v/VictualSliver.java +++ b/Mage.Sets/src/mage/cards/v/VictualSliver.java @@ -37,7 +37,7 @@ public final class VictualSliver extends CardImpl { 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: You gain 4 life.\""))); } diff --git a/Mage.Sets/src/mage/cards/v/VigeanGraftmage.java b/Mage.Sets/src/mage/cards/v/VigeanGraftmage.java index 6a8ace28879..7bd45ad6eda 100644 --- a/Mage.Sets/src/mage/cards/v/VigeanGraftmage.java +++ b/Mage.Sets/src/mage/cards/v/VigeanGraftmage.java @@ -13,8 +13,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; -import mage.counters.CounterType; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; /** @@ -22,11 +21,6 @@ import mage.target.common.TargetCreaturePermanent; * @author JotaPeRL */ public final class VigeanGraftmage extends CardImpl { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature with a +1/+1 counter on it"); - static { - filter.add(CounterType.P1P1.getPredicate()); - } public VigeanGraftmage(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{U}"); @@ -41,7 +35,7 @@ public final class VigeanGraftmage extends CardImpl { // {1}{U}: Untap target creature with a +1/+1 counter on it. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new UntapTargetEffect(), new ManaCostsImpl("{1}{U}")); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_P1P1)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/v/Vigilance.java b/Mage.Sets/src/mage/cards/v/Vigilance.java index 53df2148114..f2ef42770ef 100644 --- a/Mage.Sets/src/mage/cards/v/Vigilance.java +++ b/Mage.Sets/src/mage/cards/v/Vigilance.java @@ -1,11 +1,10 @@ - - package mage.cards.v; import java.util.UUID; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.EnchantAbility; import mage.abilities.keyword.VigilanceAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -26,6 +25,7 @@ public final class Vigilance extends CardImpl { TargetPermanent auraTarget = new TargetCreaturePermanent(); this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.AddAbility)); + this.addAbility(new EnchantAbility(auraTarget.getTargetName())); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(VigilanceAbility.getInstance(), AttachmentType.AURA))); } diff --git a/Mage.Sets/src/mage/cards/v/VigilantMartyr.java b/Mage.Sets/src/mage/cards/v/VigilantMartyr.java index bad0fef4536..cf6745acbf9 100644 --- a/Mage.Sets/src/mage/cards/v/VigilantMartyr.java +++ b/Mage.Sets/src/mage/cards/v/VigilantMartyr.java @@ -31,7 +31,7 @@ public final class VigilantMartyr extends CardImpl { private static final FilterSpell filter = new FilterSpell("spell that targets an enchantment"); static { - filter.add(new TargetsPermanentPredicate(StaticFilters.FILTER_ENCHANTMENT_PERMANENT)); + filter.add(new TargetsPermanentPredicate(StaticFilters.FILTER_PERMANENT_ENCHANTMENT)); } public VigilantMartyr(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/v/VildinPackAlpha.java b/Mage.Sets/src/mage/cards/v/VildinPackAlpha.java index a567911d8a4..37bbb7367be 100644 --- a/Mage.Sets/src/mage/cards/v/VildinPackAlpha.java +++ b/Mage.Sets/src/mage/cards/v/VildinPackAlpha.java @@ -8,6 +8,7 @@ import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; +import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; @@ -20,6 +21,8 @@ import java.util.UUID; */ public final class VildinPackAlpha extends CardImpl { + private static final FilterPermanent filter = new FilterCreaturePermanent(SubType.WEREWOLF, "a Werewolf"); + public VildinPackAlpha(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); this.subtype.add(SubType.WEREWOLF); @@ -27,11 +30,13 @@ public final class VildinPackAlpha extends CardImpl { this.toughness = new MageInt(3); this.color.setRed(true); - this.transformable = true; this.nightCard = true; // Whenever a Werewolf enters the battlefield under your control, you may transform it. - this.addAbility(new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD, new VildinPackAlphaEffect(), new FilterCreaturePermanent(SubType.WEREWOLF, "a Werewolf"), true, SetTargetPointer.PERMANENT, null)); + this.addAbility(new EntersBattlefieldControlledTriggeredAbility( + Zone.BATTLEFIELD, new VildinPackAlphaEffect(), filter, + true, SetTargetPointer.PERMANENT, null + )); // At the beginning of each upkeep, if a player cast two or more spells last turn, transform Vildin-Pack Alpha. this.addAbility(new WerewolfBackTriggeredAbility()); @@ -66,13 +71,13 @@ class VildinPackAlphaEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Permanent werewolf = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (werewolf != null && werewolf.isTransformable()) { - werewolf.transform(game); - } - return true; + if (controller == null) { + return false; } - return false; + Permanent werewolf = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (werewolf != null) { + werewolf.transform(source, game); + } + return true; } } diff --git a/Mage.Sets/src/mage/cards/v/VildinPackOutcast.java b/Mage.Sets/src/mage/cards/v/VildinPackOutcast.java index 67e4cdb4377..60678b9ff21 100644 --- a/Mage.Sets/src/mage/cards/v/VildinPackOutcast.java +++ b/Mage.Sets/src/mage/cards/v/VildinPackOutcast.java @@ -29,7 +29,6 @@ public final class VildinPackOutcast extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(4); - this.transformable = true; this.secondSideCardClazz = mage.cards.d.DronepackKindred.class; // Trample @@ -40,7 +39,7 @@ public final class VildinPackOutcast extends CardImpl { // {5}{R}{R}: Transform Vildin-Pack Outcast. this.addAbility(new TransformAbility()); - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new TransformSourceEffect(true), new ManaCostsImpl("{5}{R}{R}"))); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new TransformSourceEffect(), new ManaCostsImpl("{5}{R}{R}"))); } private VildinPackOutcast(final VildinPackOutcast card) { diff --git a/Mage.Sets/src/mage/cards/v/VileConsumption.java b/Mage.Sets/src/mage/cards/v/VileConsumption.java index e20c97a0dd7..580a3f854b8 100644 --- a/Mage.Sets/src/mage/cards/v/VileConsumption.java +++ b/Mage.Sets/src/mage/cards/v/VileConsumption.java @@ -1,4 +1,3 @@ - package mage.cards.v; import java.util.UUID; @@ -14,7 +13,7 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.TargetController; import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; /** * @@ -29,9 +28,8 @@ public final class VileConsumption extends CardImpl { Effect effect = new SacrificeSourceUnlessPaysEffect(new PayLifeCost(1)); effect.setText("sacrifice this creature unless you pay 1 life"); Effect effect2 = new GainAbilityAllEffect(new BeginningOfUpkeepTriggeredAbility(effect, TargetController.YOU, false), - Duration.WhileOnBattlefield, new FilterCreaturePermanent("all creatures")); - effect2.setText("All creatures have \"At the beginning of your upkeep, sacrifice this creature unless you pay 1 life.\""); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect2)); + Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_ALL_CREATURES); + this.addAbility(new SimpleStaticAbility(effect2)); } private VileConsumption(final VileConsumption card) { diff --git a/Mage.Sets/src/mage/cards/v/VilespawnSpider.java b/Mage.Sets/src/mage/cards/v/VilespawnSpider.java new file mode 100644 index 00000000000..6e1d9948eda --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VilespawnSpider.java @@ -0,0 +1,71 @@ +package mage.cards.v; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.MillCardsControllerEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.ReachAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.StaticFilters; +import mage.game.permanent.token.InsectToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class VilespawnSpider extends CardImpl { + + private static final DynamicValue xValue = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_CREATURE); + private static final Hint hint = new ValueHint( + "Creature cards in your graveyard", xValue + ); + + public VilespawnSpider(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}{U}"); + + this.subtype.add(SubType.SPIDER); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Reach + this.addAbility(ReachAbility.getInstance()); + + // At the beginning of your upkeep, mill a card. + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new MillCardsControllerEffect(1), TargetController.YOU, false + )); + + // {2}{G}{U}, {T}, Sacrifice Vilespawn Spider: Create a 1/1 green Insect creature token for each creature card in your graveyard. Activate only as a sorcery. + Ability ability = new ActivateAsSorceryActivatedAbility( + new CreateTokenEffect(new InsectToken(), xValue) + .setText("create a 1/1 green Insect creature token for each creature card in your graveyard"), + new ManaCostsImpl<>("{2}{G}{U}") + ); + ability.addCost(new TapSourceCost()); + ability.addCost(new SacrificeSourceCost()); + this.addAbility(ability.addHint(hint)); + } + + private VilespawnSpider(final VilespawnSpider card) { + super(card); + } + + @Override + public VilespawnSpider copy() { + return new VilespawnSpider(this); + } +} diff --git a/Mage.Sets/src/mage/cards/v/VillageIronsmith.java b/Mage.Sets/src/mage/cards/v/VillageIronsmith.java index 7bbf0b9366f..408f637c9d1 100644 --- a/Mage.Sets/src/mage/cards/v/VillageIronsmith.java +++ b/Mage.Sets/src/mage/cards/v/VillageIronsmith.java @@ -21,7 +21,6 @@ public final class VillageIronsmith extends CardImpl { this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.WEREWOLF); - this.transformable = true; this.secondSideCardClazz = mage.cards.i.Ironfang.class; this.power = new MageInt(1); diff --git a/Mage.Sets/src/mage/cards/v/VillageMessenger.java b/Mage.Sets/src/mage/cards/v/VillageMessenger.java index 72d6bc10310..c1f2f7b5aa5 100644 --- a/Mage.Sets/src/mage/cards/v/VillageMessenger.java +++ b/Mage.Sets/src/mage/cards/v/VillageMessenger.java @@ -23,7 +23,6 @@ public final class VillageMessenger extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); - this.transformable = true; this.secondSideCardClazz = mage.cards.m.MoonriseIntruder.class; // Haste diff --git a/Mage.Sets/src/mage/cards/v/VillageReavers.java b/Mage.Sets/src/mage/cards/v/VillageReavers.java index efa4fe5e3ff..ccdf2918732 100644 --- a/Mage.Sets/src/mage/cards/v/VillageReavers.java +++ b/Mage.Sets/src/mage/cards/v/VillageReavers.java @@ -38,7 +38,6 @@ public final class VillageReavers extends CardImpl { this.color.setRed(true); this.nightCard = true; - this.transformable = true; this.power = new MageInt(5); this.toughness = new MageInt(4); diff --git a/Mage.Sets/src/mage/cards/v/VillageWatch.java b/Mage.Sets/src/mage/cards/v/VillageWatch.java index c620c73046f..8efa14cdf32 100644 --- a/Mage.Sets/src/mage/cards/v/VillageWatch.java +++ b/Mage.Sets/src/mage/cards/v/VillageWatch.java @@ -3,7 +3,6 @@ package mage.cards.v; import mage.MageInt; import mage.abilities.keyword.DayboundAbility; import mage.abilities.keyword.HasteAbility; -import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -24,14 +23,12 @@ public final class VillageWatch extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(3); - this.transformable = true; this.secondSideCardClazz = mage.cards.v.VillageReavers.class; // Haste this.addAbility(HasteAbility.getInstance()); // Daybound - this.addAbility(new TransformAbility()); this.addAbility(new DayboundAbility()); } diff --git a/Mage.Sets/src/mage/cards/v/VillagersOfEstwald.java b/Mage.Sets/src/mage/cards/v/VillagersOfEstwald.java index ed3cdb7f6aa..ce821011a2f 100644 --- a/Mage.Sets/src/mage/cards/v/VillagersOfEstwald.java +++ b/Mage.Sets/src/mage/cards/v/VillagersOfEstwald.java @@ -20,7 +20,6 @@ public final class VillagersOfEstwald extends CardImpl { this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.WEREWOLF); - this.transformable = true; this.secondSideCardClazz = mage.cards.h.HowlpackOfEstwald.class; this.power = new MageInt(2); diff --git a/Mage.Sets/src/mage/cards/v/VineDryad.java b/Mage.Sets/src/mage/cards/v/VineDryad.java index 8f89cdeb36b..4ee18644434 100644 --- a/Mage.Sets/src/mage/cards/v/VineDryad.java +++ b/Mage.Sets/src/mage/cards/v/VineDryad.java @@ -1,7 +1,5 @@ - package mage.cards.v; -import java.util.UUID; import mage.MageInt; import mage.ObjectColor; import mage.abilities.costs.AlternativeCostSourceAbility; @@ -13,19 +11,25 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterOwnedCard; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardIdPredicate; import mage.filter.predicate.mageobject.ColorPredicate; import mage.target.common.TargetCardInHand; +import java.util.UUID; + /** - * * @author fireshoes */ public final class VineDryad extends CardImpl { + private static final FilterOwnedCard filter + = new FilterOwnedCard("a green card from your hand"); + + static { + filter.add(new ColorPredicate(ObjectColor.GREEN)); + } + public VineDryad(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}"); this.subtype.add(SubType.DRYAD); this.power = new MageInt(1); this.toughness = new MageInt(3); @@ -37,10 +41,6 @@ public final class VineDryad extends CardImpl { this.addAbility(new ForestwalkAbility()); // You may exile a green card from your hand rather than pay Vine Dryad's mana cost. - FilterOwnedCard filter = new FilterOwnedCard("a green card from your hand"); - filter.add(new ColorPredicate(ObjectColor.GREEN)); - filter.add(Predicates.not(new CardIdPredicate(this.getId()))); // the exile cost can never be paid with the card itself - this.addAbility(new AlternativeCostSourceAbility(new ExileFromHandCost(new TargetCardInHand(filter)))); } diff --git a/Mage.Sets/src/mage/cards/v/ViolentEruption.java b/Mage.Sets/src/mage/cards/v/ViolentEruption.java index 75b88a36ce0..9adee177633 100644 --- a/Mage.Sets/src/mage/cards/v/ViolentEruption.java +++ b/Mage.Sets/src/mage/cards/v/ViolentEruption.java @@ -1,4 +1,3 @@ - package mage.cards.v; import java.util.UUID; @@ -19,8 +18,7 @@ public final class ViolentEruption extends CardImpl { public ViolentEruption(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{R}{R}{R}"); - - // Violent Eruption deals 4 damage divided as you choose among any number of target creatures and/or players. + // Violent Eruption deals 4 damage divided as you choose among any number of targets. this.getSpellAbility().addEffect(new DamageMultiEffect(4)); this.getSpellAbility().addTarget(new TargetAnyTargetAmount(4)); diff --git a/Mage.Sets/src/mage/cards/v/VioletPall.java b/Mage.Sets/src/mage/cards/v/VioletPall.java index e662ea4ab46..358c084609a 100644 --- a/Mage.Sets/src/mage/cards/v/VioletPall.java +++ b/Mage.Sets/src/mage/cards/v/VioletPall.java @@ -1,17 +1,13 @@ - package mage.cards.v; import java.util.UUID; -import mage.ObjectColor; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ColorPredicate; +import mage.filter.StaticFilters; import mage.game.permanent.token.FaerieRogueToken; import mage.target.common.TargetCreaturePermanent; @@ -21,18 +17,12 @@ import mage.target.common.TargetCreaturePermanent; */ public final class VioletPall extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - public VioletPall(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.TRIBAL,CardType.INSTANT},"{4}{B}"); this.subtype.add(SubType.FAERIE); this.getSpellAbility().addEffect(new DestroyTargetEffect()); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK)); this.getSpellAbility().addEffect(new CreateTokenEffect(new FaerieRogueToken(), 1)); } diff --git a/Mage.Sets/src/mage/cards/v/VirulentWound.java b/Mage.Sets/src/mage/cards/v/VirulentWound.java index 4ca35000282..0382d91fc08 100644 --- a/Mage.Sets/src/mage/cards/v/VirulentWound.java +++ b/Mage.Sets/src/mage/cards/v/VirulentWound.java @@ -50,7 +50,7 @@ class VirulentWoundEffect extends OneShotEffect { public VirulentWoundEffect() { super(Outcome.UnboostCreature); - this.staticText = "Put a -1/-1 counter on target creature. When that creature dies this turn, its controller gets a poison counter"; + this.staticText = "When that creature dies this turn, its controller gets a poison counter"; } public VirulentWoundEffect(final VirulentWoundEffect effect) { diff --git a/Mage.Sets/src/mage/cards/v/VirusBeetle.java b/Mage.Sets/src/mage/cards/v/VirusBeetle.java new file mode 100644 index 00000000000..8d5466fee3b --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VirusBeetle.java @@ -0,0 +1,38 @@ +package mage.cards.v; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.discard.DiscardEachPlayerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class VirusBeetle extends CardImpl { + + public VirusBeetle(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{1}{B}"); + + this.subtype.add(SubType.INSECT); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // When Virus Beetle enters the battlefield, each opponent discards a card. + this.addAbility(new EntersBattlefieldTriggeredAbility(new DiscardEachPlayerEffect(TargetController.OPPONENT))); + } + + private VirusBeetle(final VirusBeetle card) { + super(card); + } + + @Override + public VirusBeetle copy() { + return new VirusBeetle(this); + } +} diff --git a/Mage.Sets/src/mage/cards/v/VisionOfTheUnspeakable.java b/Mage.Sets/src/mage/cards/v/VisionOfTheUnspeakable.java new file mode 100644 index 00000000000..4d8752569c1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VisionOfTheUnspeakable.java @@ -0,0 +1,53 @@ +package mage.cards.v; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.common.CardsInControllerHandCount; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class VisionOfTheUnspeakable extends CardImpl { + + public VisionOfTheUnspeakable(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, ""); + + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(0); + this.toughness = new MageInt(0); + this.color.setBlue(true); + this.nightCard = true; + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Vision of the Unspeakable gets +1/+1 for each card in your hand. + this.addAbility(new SimpleStaticAbility(new BoostSourceEffect( + CardsInControllerHandCount.instance, + CardsInControllerHandCount.instance, + Duration.WhileOnBattlefield + ))); + } + + private VisionOfTheUnspeakable(final VisionOfTheUnspeakable card) { + super(card); + } + + @Override + public VisionOfTheUnspeakable copy() { + return new VisionOfTheUnspeakable(this); + } +} diff --git a/Mage.Sets/src/mage/cards/v/VivienArkbowRanger.java b/Mage.Sets/src/mage/cards/v/VivienArkbowRanger.java index 1bab419d820..97f6c5c36d6 100644 --- a/Mage.Sets/src/mage/cards/v/VivienArkbowRanger.java +++ b/Mage.Sets/src/mage/cards/v/VivienArkbowRanger.java @@ -2,7 +2,6 @@ package mage.cards.v; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.common.DamageWithPowerFromOneToAnotherTargetEffect; import mage.abilities.effects.common.WishEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; @@ -35,7 +34,7 @@ public final class VivienArkbowRanger extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.VIVIEN); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Distribute two +1/+1 counters among up to two target creatures. They gain trample until end of turn. Ability ability = new LoyaltyAbility(new DistributeCountersEffect( @@ -58,8 +57,8 @@ public final class VivienArkbowRanger extends CardImpl { ability.addTarget(new TargetCreatureOrPlaneswalker()); this.addAbility(ability); - // −5: You may choose a creature card you own from outside the game, reveal it, and put it into your hand. - this.addAbility(new LoyaltyAbility(new WishEffect(StaticFilters.FILTER_CARD_CREATURE_A), -5) + // −5: You may reveal a creature card you own from outside the game and put it into your hand. + this.addAbility(new LoyaltyAbility(new WishEffect(StaticFilters.FILTER_CARD_CREATURE), -5) .addHint(OpenSideboardHint.instance)); } diff --git a/Mage.Sets/src/mage/cards/v/VivienChampionOfTheWilds.java b/Mage.Sets/src/mage/cards/v/VivienChampionOfTheWilds.java index 2549312f7ee..1b2432a6767 100644 --- a/Mage.Sets/src/mage/cards/v/VivienChampionOfTheWilds.java +++ b/Mage.Sets/src/mage/cards/v/VivienChampionOfTheWilds.java @@ -2,7 +2,6 @@ package mage.cards.v; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.ContinuousEffect; @@ -40,7 +39,7 @@ public final class VivienChampionOfTheWilds extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.VIVIEN); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // You may cast creature spells as though they had flash. this.addAbility(new SimpleStaticAbility(new CastAsThoughItHadFlashAllEffect( diff --git a/Mage.Sets/src/mage/cards/v/VivienMonstersAdvocate.java b/Mage.Sets/src/mage/cards/v/VivienMonstersAdvocate.java index 1985e62cf9a..5349862629b 100644 --- a/Mage.Sets/src/mage/cards/v/VivienMonstersAdvocate.java +++ b/Mage.Sets/src/mage/cards/v/VivienMonstersAdvocate.java @@ -3,7 +3,6 @@ package mage.cards.v; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; @@ -46,7 +45,7 @@ public final class VivienMonstersAdvocate extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.VIVIEN); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // You may look at the top card of your library any time. this.addAbility(new SimpleStaticAbility(new LookAtTopCardOfLibraryAnyTimeEffect())); diff --git a/Mage.Sets/src/mage/cards/v/VivienNaturesAvenger.java b/Mage.Sets/src/mage/cards/v/VivienNaturesAvenger.java index 09e97030e53..3280173988a 100644 --- a/Mage.Sets/src/mage/cards/v/VivienNaturesAvenger.java +++ b/Mage.Sets/src/mage/cards/v/VivienNaturesAvenger.java @@ -2,7 +2,6 @@ package mage.cards.v; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.common.RevealCardsFromLibraryUntilEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; @@ -27,7 +26,7 @@ public final class VivienNaturesAvenger extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.VIVIEN); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +1: Put three +1/+1 counters on up to one target creature. Ability ability = new LoyaltyAbility(new AddCountersTargetEffect( diff --git a/Mage.Sets/src/mage/cards/v/VivienOfTheArkbow.java b/Mage.Sets/src/mage/cards/v/VivienOfTheArkbow.java index 34c82d0c40c..c48c7ff6b9f 100644 --- a/Mage.Sets/src/mage/cards/v/VivienOfTheArkbow.java +++ b/Mage.Sets/src/mage/cards/v/VivienOfTheArkbow.java @@ -2,7 +2,6 @@ package mage.cards.v; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.common.DamageWithPowerFromOneToAnotherTargetEffect; import mage.abilities.effects.common.continuous.BoostControlledEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; @@ -31,7 +30,7 @@ public final class VivienOfTheArkbow extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.VIVIEN); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +2: Put two +1/+1 counters on up to one target creature. Ability ability = new LoyaltyAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance(2)), 2); diff --git a/Mage.Sets/src/mage/cards/v/VivienReid.java b/Mage.Sets/src/mage/cards/v/VivienReid.java index 74e03a599d3..f104b84fcda 100644 --- a/Mage.Sets/src/mage/cards/v/VivienReid.java +++ b/Mage.Sets/src/mage/cards/v/VivienReid.java @@ -3,7 +3,6 @@ package mage.cards.v; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.GetEmblemEffect; @@ -51,7 +50,7 @@ public final class VivienReid extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.VIVIEN); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +1: Look at the top four cards of your library. You may reveal a creature or land card from among them and put it into your hand. Put the rest on the bottom of your library in any order. this.addAbility(new LoyaltyAbility( diff --git a/Mage.Sets/src/mage/cards/v/VizierOfTheTrue.java b/Mage.Sets/src/mage/cards/v/VizierOfTheTrue.java index 949ec3deb45..cc08c426867 100644 --- a/Mage.Sets/src/mage/cards/v/VizierOfTheTrue.java +++ b/Mage.Sets/src/mage/cards/v/VizierOfTheTrue.java @@ -10,12 +10,10 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.target.common.TargetCreaturePermanent; /** @@ -51,15 +49,9 @@ public final class VizierOfTheTrue extends CardImpl { class VizierOfTheTrueAbility extends TriggeredAbilityImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public VizierOfTheTrueAbility() { super(Zone.BATTLEFIELD, new TapTargetEffect()); - addTarget(new TargetCreaturePermanent(filter)); + addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); } public VizierOfTheTrueAbility(final VizierOfTheTrueAbility ability) { diff --git a/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java b/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java index f178d60313a..ff4b32081b8 100644 --- a/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java +++ b/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java @@ -1,11 +1,6 @@ package mage.cards.v; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.UUID; import mage.MageInt; import mage.MageObjectReference; import mage.abilities.Ability; @@ -19,25 +14,20 @@ import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.keyword.DefenderAbility; 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.WatcherScope; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.permanent.TappedPredicate; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; import mage.game.stack.StackAbility; import mage.target.common.TargetControlledCreaturePermanent; import mage.watchers.Watcher; +import java.util.*; + /** - * * @author L_J */ public final class VodalianWarMachine extends CardImpl { @@ -66,7 +56,7 @@ public final class VodalianWarMachine extends CardImpl { this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(2, 1, Duration.EndOfTurn), new TapTargetCost(new TargetControlledCreaturePermanent(1, 1, filter, true)))); // When Vodalian War Machine dies, destroy all Merfolk tapped this turn to pay for its abilities. - this.addAbility(new VodalianWarMachineTriggeredAbility(), new VodalianWarMachineWatcher()); + this.addAbility(new DiesSourceTriggeredAbility(new VodalianWarMachineEffect()), new VodalianWarMachineWatcher()); } private VodalianWarMachine(final VodalianWarMachine card) { @@ -79,41 +69,6 @@ public final class VodalianWarMachine extends CardImpl { } } -class VodalianWarMachineTriggeredAbility extends DiesSourceTriggeredAbility { - - public VodalianWarMachineTriggeredAbility() { - super(new VodalianWarMachineEffect(), false); - } - - public VodalianWarMachineTriggeredAbility(VodalianWarMachineTriggeredAbility ability) { - super(ability); - } - - @Override - public VodalianWarMachineTriggeredAbility copy() { - return new VodalianWarMachineTriggeredAbility(this); - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent before = ((ZoneChangeEvent) event).getTarget(); - if (before == null) { - return false; - } - if (super.checkTrigger(event, game)) { - ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - if (zEvent.getTarget().isTransformable()) { - if (!zEvent.getTarget().getAbilities().contains(this)) { - return false; - } - } - return true; - } - return false; - } - -} - class VodalianWarMachineEffect extends OneShotEffect { private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Merfolk tapped this turn to pay for its abilities"); diff --git a/Mage.Sets/src/mage/cards/v/VoiceOfTheBlessed.java b/Mage.Sets/src/mage/cards/v/VoiceOfTheBlessed.java new file mode 100644 index 00000000000..e7a08e1dfdb --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VoiceOfTheBlessed.java @@ -0,0 +1,88 @@ +package mage.cards.v; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.GainLifeControllerTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.IndestructibleAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.Permanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class VoiceOfTheBlessed extends CardImpl { + + public VoiceOfTheBlessed(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}{W}"); + + this.subtype.add(SubType.SPIRIT); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Whenever you gain life, put a +1/+1 counter on Voice of the Blessed. + this.addAbility(new GainLifeControllerTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()) + )); + + // As long as Voice of the Blessed has four or more +1/+1 counters on it, it has flying and vigilance. + Ability ability = new SimpleStaticAbility(new ConditionalContinuousEffect( + new GainAbilitySourceEffect( + FlyingAbility.getInstance(), Duration.WhileOnBattlefield + ), VoiceOfTheBlessedCondition.FOUR, "as long as {this} has " + + "four or more +1/+1 counters on it, it has flying" + )); + ability.addEffect(new ConditionalContinuousEffect(new GainAbilitySourceEffect( + VigilanceAbility.getInstance(), Duration.WhileOnBattlefield + ), VoiceOfTheBlessedCondition.FOUR, "and vigilance")); + this.addAbility(ability); + + // As long as Voice of the Blessed has ten or more +1/+1 counters on it, it has indestructible. + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new GainAbilitySourceEffect( + IndestructibleAbility.getInstance(), Duration.WhileOnBattlefield + ), VoiceOfTheBlessedCondition.TEN, "as long as {this} has " + + "ten or more +1/+1 counters on it, it has indestructible" + ))); + } + + private VoiceOfTheBlessed(final VoiceOfTheBlessed card) { + super(card); + } + + @Override + public VoiceOfTheBlessed copy() { + return new VoiceOfTheBlessed(this); + } +} + +enum VoiceOfTheBlessedCondition implements Condition { + FOUR(4), + TEN(10); + private final int counters; + + VoiceOfTheBlessedCondition(int counters) { + this.counters = counters; + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + return permanent != null && permanent.getCounters(game).getCount(CounterType.P1P1) >= counters; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/v/VoidMirror.java b/Mage.Sets/src/mage/cards/v/VoidMirror.java index 045d13519a5..41872508bb5 100644 --- a/Mage.Sets/src/mage/cards/v/VoidMirror.java +++ b/Mage.Sets/src/mage/cards/v/VoidMirror.java @@ -1,6 +1,5 @@ package mage.cards.v; -import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.common.SpellCastAllTriggeredAbility; import mage.abilities.effects.OneShotEffect; @@ -73,7 +72,7 @@ class VoidMirrorEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Spell spell = (Spell) getValue("spellCast"); if (spell != null) { - spell.counter(source, game); + game.getStack().counter(spell.getId(), source, game); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/v/VolatileArsonist.java b/Mage.Sets/src/mage/cards/v/VolatileArsonist.java new file mode 100644 index 00000000000..17e4322bcf9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VolatileArsonist.java @@ -0,0 +1,60 @@ +package mage.cards.v; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.keyword.DayboundAbility; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.target.TargetPlayer; +import mage.target.common.TargetCreaturePermanent; +import mage.target.common.TargetPlaneswalkerPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class VolatileArsonist extends CardImpl { + + public VolatileArsonist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{R}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WEREWOLF); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + this.secondSideCardClazz = mage.cards.d.DireStrainAnarchist.class; + + // Menace + this.addAbility(new MenaceAbility(false)); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // Whenever Volatile Arsonist attacks, it deals 1 damage to each of up to one target creature, up to one target player, and/or up to one target planeswalker. + Ability ability = new AttacksTriggeredAbility(new DamageTargetEffect(1) + .setText("it deals 1 damage to each of up to one target creature, up to one target player, and/or up to one target planeswalker")); + ability.addTarget(new TargetCreaturePermanent(0, 1)); + ability.addTarget(new TargetPlayer(0, 1, false)); + ability.addTarget(new TargetPlaneswalkerPermanent(0, 1)); + this.addAbility(ability); + + // Daybound + this.addAbility(new DayboundAbility()); + } + + private VolatileArsonist(final VolatileArsonist card) { + super(card); + } + + @Override + public VolatileArsonist copy() { + return new VolatileArsonist(this); + } +} diff --git a/Mage.Sets/src/mage/cards/v/VolcanicRush.java b/Mage.Sets/src/mage/cards/v/VolcanicRush.java index bba22177e64..4b37605618e 100644 --- a/Mage.Sets/src/mage/cards/v/VolcanicRush.java +++ b/Mage.Sets/src/mage/cards/v/VolcanicRush.java @@ -1,4 +1,3 @@ - package mage.cards.v; import java.util.UUID; @@ -10,7 +9,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.common.FilterAttackingCreature; +import mage.filter.StaticFilters; /** * @@ -22,10 +21,10 @@ public final class VolcanicRush extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{4}{R}"); // Attacking creatures get +2/+0 and gain trample until end of turn. - Effect effect = new BoostAllEffect(2, 0, Duration.EndOfTurn, new FilterAttackingCreature(), false); + Effect effect = new BoostAllEffect(2, 0, Duration.EndOfTurn, StaticFilters.FILTER_ATTACKING_CREATURES, false); effect.setText("Attacking creatures get +2/+0"); this.getSpellAbility().addEffect(effect); - effect = new GainAbilityAllEffect(TrampleAbility.getInstance(), Duration.EndOfTurn, new FilterAttackingCreature(), false); + effect = new GainAbilityAllEffect(TrampleAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_ATTACKING_CREATURES); effect.setText("and gain trample until end of turn"); this.getSpellAbility().addEffect(effect); } diff --git a/Mage.Sets/src/mage/cards/v/VolcanicWind.java b/Mage.Sets/src/mage/cards/v/VolcanicWind.java index b4a115b4d79..495b083e130 100644 --- a/Mage.Sets/src/mage/cards/v/VolcanicWind.java +++ b/Mage.Sets/src/mage/cards/v/VolcanicWind.java @@ -1,4 +1,3 @@ - package mage.cards.v; import java.util.UUID; @@ -16,14 +15,10 @@ import mage.target.common.TargetCreaturePermanentAmount; * @author tcontis */ public final class VolcanicWind extends CardImpl { - - static final private FilterCreaturePermanent filter = new FilterCreaturePermanent("creatures"); - static { - filter.add(CardType.CREATURE.getPredicate()); - } + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creatures"); - static final private String rule = "{this} deals X damage divided as you choose among any number of target creatures, where X is the number of creatures on the battlefield as you cast {this}"; + private static final String rule = "{this} deals X damage divided as you choose among any number of target creatures, where X is the number of creatures on the battlefield as you cast this spell"; public VolcanicWind(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{4}{R}{R}"); @@ -34,7 +29,6 @@ public final class VolcanicWind extends CardImpl { effect.setText(rule); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount(creatures)); - } private VolcanicWind(final VolcanicWind card) { diff --git a/Mage.Sets/src/mage/cards/v/VoldarenBloodcaster.java b/Mage.Sets/src/mage/cards/v/VoldarenBloodcaster.java new file mode 100644 index 00000000000..9620ea4b8bb --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VoldarenBloodcaster.java @@ -0,0 +1,117 @@ +package mage.cards.v; + +import mage.MageInt; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.DiesThisOrAnotherCreatureTriggeredAbility; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.TransformAbility; +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.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.BloodToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class VoldarenBloodcaster extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("nontoken creature you control"); + + static { + filter.add(TokenPredicate.FALSE); + } + + public VoldarenBloodcaster(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}"); + + this.subtype.add(SubType.VAMPIRE); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + this.secondSideCardClazz = mage.cards.b.BloodbatSummoner.class; + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever Voldaren Bloodcaster or another nontoken creature you control dies, create a Blood token. + this.addAbility(new DiesThisOrAnotherCreatureTriggeredAbility( + new CreateTokenEffect(new BloodToken()), false, filter + )); + + // Whenever you create a Blood token, if you control five or more Blood tokens, transform Voldaren Bloodcaster. + this.addAbility(new TransformAbility()); + this.addAbility(new VoldarenBloodcasterTriggeredAbility()); + } + + private VoldarenBloodcaster(final VoldarenBloodcaster card) { + super(card); + } + + @Override + public VoldarenBloodcaster copy() { + return new VoldarenBloodcaster(this); + } +} + +class VoldarenBloodcasterTriggeredAbility extends TriggeredAbilityImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.BLOOD); + + static { + filter.add(TokenPredicate.TRUE); + } + + private static final Hint hint = new ValueHint("Blood tokens you control", new PermanentsOnBattlefieldCount(filter)); + + VoldarenBloodcasterTriggeredAbility() { + super(Zone.BATTLEFIELD, new TransformSourceEffect()); + this.addHint(hint); + } + + private VoldarenBloodcasterTriggeredAbility(final VoldarenBloodcasterTriggeredAbility ability) { + super(ability); + } + + @Override + public VoldarenBloodcasterTriggeredAbility copy() { + return new VoldarenBloodcasterTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.CREATED_TOKEN; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent permanent = game.getPermanent(event.getTargetId()); + return permanent != null && filter.match(permanent, game) && isControlledBy(event.getPlayerId()); + } + + @Override + public boolean checkInterveningIfClause(Game game) { + return game.getBattlefield().count(filter, getSourceId(), getControllerId(), game) >= 5; + } + + @Override + public String getRule() { + return "Whenever you create a Blood token, if you control five or more Blood tokens, transform {this}."; + } +} diff --git a/Mage.Sets/src/mage/cards/v/VoldarenEpicure.java b/Mage.Sets/src/mage/cards/v/VoldarenEpicure.java new file mode 100644 index 00000000000..378ce7c376b --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VoldarenEpicure.java @@ -0,0 +1,45 @@ +package mage.cards.v; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DamagePlayersEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.game.permanent.token.BloodToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class VoldarenEpicure extends CardImpl { + + public VoldarenEpicure(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}"); + + this.subtype.add(SubType.VAMPIRE); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // When Voldaren Epicure enters the battlefield, it deals 1 damage to each opponent. Create a Blood token. + Ability ability = new EntersBattlefieldTriggeredAbility(new DamagePlayersEffect( + 1, TargetController.OPPONENT, "it" + )); + ability.addEffect(new CreateTokenEffect(new BloodToken())); + this.addAbility(ability); + } + + private VoldarenEpicure(final VoldarenEpicure card) { + super(card); + } + + @Override + public VoldarenEpicure copy() { + return new VoldarenEpicure(this); + } +} diff --git a/Mage.Sets/src/mage/cards/v/VoldarenEstate.java b/Mage.Sets/src/mage/cards/v/VoldarenEstate.java new file mode 100644 index 00000000000..0637f52e233 --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VoldarenEstate.java @@ -0,0 +1,138 @@ +package mage.cards.v; + +import java.util.UUID; + +import mage.ConditionalMana; +import mage.MageObject; +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.condition.Condition; +import mage.abilities.costs.Cost; +import mage.abilities.costs.CostAdjuster; +import mage.abilities.costs.common.PayLifeCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.mana.ColorlessManaAbility; +import mage.abilities.mana.ConditionalAnyColorManaAbility; +import mage.abilities.mana.builder.ConditionalManaBuilder; +import mage.abilities.mana.conditional.ManaCondition; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.common.FilterControlledPermanent; +import mage.game.Game; +import mage.game.permanent.token.BloodToken; +import mage.players.Player; +import mage.util.CardUtil; + +/** + * + * @author weirddan455 + */ +public final class VoldarenEstate extends CardImpl { + + public VoldarenEstate(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // {T}: Add {C}. + this.addAbility(new ColorlessManaAbility()); + + // {T}, Pay 1 life: Add one mana of any color. Spend this mana only to cast a Vampire spell. + Ability ability = new ConditionalAnyColorManaAbility(new TapSourceCost(), 1, new VoldarenEstateManaBuilder(), true); + ability.addCost(new PayLifeCost(1)); + this.addAbility(ability); + + // {5}, {T}: Create a Blood token. This ability costs {1} less to activate for each Vampire you control. + ability = new SimpleActivatedAbility(new CreateTokenEffect(new BloodToken() + ).setText("Create a Blood token. This ability costs {1} less to activate for each Vampire you control"), + new GenericManaCost(5)); + ability.addCost(new TapSourceCost()); + ability.setCostAdjuster(VoldarenEstateCostAdjuster.instance); + ability.addHint(VoldarenEstateCostAdjuster.getHint()); + this.addAbility(ability); + } + + private VoldarenEstate(final VoldarenEstate card) { + super(card); + } + + @Override + public VoldarenEstate copy() { + return new VoldarenEstate(this); + } +} + +class VoldarenEstateManaBuilder extends ConditionalManaBuilder { + + @Override + public ConditionalManaBuilder setMana(Mana mana, Ability source, Game game) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = game.getObject(source.getSourceId()); + if (controller != null && sourceObject != null && mana.getAny() == 0) { + game.informPlayers(controller.getLogName() + " produces " + mana.toString() + " with " + sourceObject.getLogName() + + " (can only be spent to cast a Vampire spell"); + } + return super.setMana(mana, source, game); + } + + @Override + public ConditionalMana build(Object... options) { + return new VoldarenEstateConditionalMana(this.mana); + } + + @Override + public String getRule() { + return "Spend this mana only to cast a Vampire spell"; + } +} + +class VoldarenEstateConditionalMana extends ConditionalMana { + + public VoldarenEstateConditionalMana(Mana mana) { + super(mana); + staticText = "Spend this mana only to cast a Vampire spell"; + addCondition(new VoldarenEstateManaCondition()); + } +} + +class VoldarenEstateManaCondition extends ManaCondition implements Condition { + + @Override + public boolean apply(Game game, Ability source) { + if (source instanceof SpellAbility) { + MageObject object = game.getObject(source.getSourceId()); + return object != null && object.hasSubtype(SubType.VAMPIRE, game); + } + return false; + } + + @Override + public boolean apply(Game game, Ability source, UUID originalId, Cost costToPay) { + return apply(game, source); + } +} + +enum VoldarenEstateCostAdjuster implements CostAdjuster { + instance; + + private static final FilterControlledPermanent filter = new FilterControlledPermanent(SubType.VAMPIRE); + private static final DynamicValue vampireCount = new PermanentsOnBattlefieldCount(filter); + private static final Hint hint = new ValueHint("Vampires you control", vampireCount); + + public static Hint getHint() { + return hint; + } + + @Override + public void adjustCosts(Ability ability, Game game) { + CardUtil.reduceCost(ability, vampireCount.calculate(game, ability, null)); + } +} diff --git a/Mage.Sets/src/mage/cards/v/VoldarenPariah.java b/Mage.Sets/src/mage/cards/v/VoldarenPariah.java index 09dbf6d09aa..d5ead032805 100644 --- a/Mage.Sets/src/mage/cards/v/VoldarenPariah.java +++ b/Mage.Sets/src/mage/cards/v/VoldarenPariah.java @@ -38,7 +38,6 @@ public final class VoldarenPariah extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(3); - this.transformable = true; this.secondSideCardClazz = mage.cards.a.AbolisherOfBloodlines.class; // Flying @@ -46,7 +45,7 @@ public final class VoldarenPariah extends CardImpl { // Sacrifice three other creatures: Transform Voldaren Pariah. this.addAbility(new TransformAbility()); - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new TransformSourceEffect(true), + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new TransformSourceEffect(), new SacrificeTargetCost(new TargetControlledPermanent(3, 3, filter, false)))); // Madness {B}{B}{B} diff --git a/Mage.Sets/src/mage/cards/v/VolleyOfBoulders.java b/Mage.Sets/src/mage/cards/v/VolleyOfBoulders.java index 2918562e23c..3901e5c299d 100644 --- a/Mage.Sets/src/mage/cards/v/VolleyOfBoulders.java +++ b/Mage.Sets/src/mage/cards/v/VolleyOfBoulders.java @@ -1,4 +1,3 @@ - package mage.cards.v; import java.util.UUID; @@ -20,8 +19,7 @@ public final class VolleyOfBoulders extends CardImpl { public VolleyOfBoulders(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{8}{R}"); - - // Volley of Boulders deals 6 damage divided as you choose among any number of target creatures and/or players. + // Volley of Boulders deals 6 damage divided as you choose among any number of targets. this.getSpellAbility().addEffect(new DamageMultiEffect(6)); this.getSpellAbility().addTarget(new TargetAnyTargetAmount(6)); // Flashback {R}{R}{R}{R}{R}{R} diff --git a/Mage.Sets/src/mage/cards/v/VolrathsShapeshifter.java b/Mage.Sets/src/mage/cards/v/VolrathsShapeshifter.java index 73fca45fdab..b670ec41915 100644 --- a/Mage.Sets/src/mage/cards/v/VolrathsShapeshifter.java +++ b/Mage.Sets/src/mage/cards/v/VolrathsShapeshifter.java @@ -78,7 +78,7 @@ class VolrathsShapeshifterEffect extends ContinuousEffectImpl { permanent.getToughness().setValue(card.getToughness().getValue()); permanent.getColor(game).setColor(card.getColor(game)); permanent.getManaCost().clear(); - permanent.getManaCost().add(card.getManaCost()); + permanent.getManaCost().add(card.getManaCost().copy()); permanent.removeAllCardTypes(game); permanent.setName(card.getName()); diff --git a/Mage.Sets/src/mage/cards/v/VoltChargedBerserker.java b/Mage.Sets/src/mage/cards/v/VoltChargedBerserker.java new file mode 100644 index 00000000000..0663ca39e0a --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VoltChargedBerserker.java @@ -0,0 +1,39 @@ +package mage.cards.v; + +import mage.MageInt; +import mage.abilities.common.CantBlockAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class VoltChargedBerserker extends CardImpl { + + public VoltChargedBerserker(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.BERSERKER); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + this.color.setRed(true); + this.nightCard = true; + + // Volt-Charged Berserker can't block. + this.addAbility(new CantBlockAbility()); + } + + private VoltChargedBerserker(final VoltChargedBerserker card) { + super(card); + } + + @Override + public VoltChargedBerserker copy() { + return new VoltChargedBerserker(this); + } +} diff --git a/Mage.Sets/src/mage/cards/v/VoltageSurge.java b/Mage.Sets/src/mage/cards/v/VoltageSurge.java new file mode 100644 index 00000000000..ca2d7c391c2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VoltageSurge.java @@ -0,0 +1,78 @@ +package mage.cards.v; + +import mage.abilities.Ability; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetControlledPermanent; +import mage.target.common.TargetCreatureOrPlaneswalker; +import mage.util.CardUtil; + +import java.util.Collection; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class VoltageSurge extends CardImpl { + + public VoltageSurge(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{R}"); + + // As an additional cost to cast this spell, you may sacrifice an artifact. + this.getSpellAbility().addCost(new SacrificeTargetCost(new TargetControlledPermanent( + 0, 1, StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT_AN, true + )).setText("you may sacrifice an artifact")); + + // Voltage Surge deals 2 damage to target creature or planeswalker. If this spell's additional cost was paid, Voltage Surge deals 4 damage instead. + this.getSpellAbility().addEffect(new VoltageSurgeEffect()); + this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalker()); + } + + private VoltageSurge(final VoltageSurge card) { + super(card); + } + + @Override + public VoltageSurge copy() { + return new VoltageSurge(this); + } +} + +class VoltageSurgeEffect extends OneShotEffect { + + VoltageSurgeEffect() { + super(Outcome.Benefit); + staticText = "{this} deals 2 damage to target creature or planeswalker. " + + "If this spell's additional cost was paid, {this} deals 4 damage instead"; + } + + private VoltageSurgeEffect(final VoltageSurgeEffect effect) { + super(effect); + } + + @Override + public VoltageSurgeEffect copy() { + return new VoltageSurgeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent == null) { + return false; + } + boolean wasPaid = CardUtil.castStream(source.getCosts().stream(), SacrificeTargetCost.class) + .map(SacrificeTargetCost::getPermanents) + .flatMap(Collection::stream) + .findFirst() + .isPresent(); + return permanent.damage(wasPaid ? 4 : 2, source, game) > 0; + } +} diff --git a/Mage.Sets/src/mage/cards/v/VoltaicVisionary.java b/Mage.Sets/src/mage/cards/v/VoltaicVisionary.java new file mode 100644 index 00000000000..b1892b10664 --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VoltaicVisionary.java @@ -0,0 +1,145 @@ +package mage.cards.v; + +import mage.MageInt; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.DamageControllerEffect; +import mage.abilities.effects.common.ExileTopXMayPlayUntilEndOfTurnEffect; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.keyword.TransformAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.WatcherScope; +import mage.constants.Zone; +import mage.game.ExileZone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.util.CardUtil; +import mage.watchers.Watcher; + +import java.util.*; + +/** + * @author TheElk801 + */ +public final class VoltaicVisionary extends CardImpl { + + public VoltaicVisionary(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(3); + this.toughness = new MageInt(1); + this.secondSideCardClazz = mage.cards.v.VoltChargedBerserker.class; + + // {T}: Voltaic Visionary deals 2 damage to you. Exile the top card of your library. You may play that card this turn. Activate only as a sorcery. + Ability ability = new ActivateAsSorceryActivatedAbility( + new DamageControllerEffect(2), new TapSourceCost() + ); + ability.addEffect(new ExileTopXMayPlayUntilEndOfTurnEffect(1)); + this.addAbility(ability); + + // When you play a card exiled with Voltaic Visionary, transform Voltaic Visionary. + this.addAbility(new TransformAbility()); + this.addAbility(new VoltaicVisionaryTriggeredAbility()); + } + + private VoltaicVisionary(final VoltaicVisionary card) { + super(card); + } + + @Override + public VoltaicVisionary copy() { + return new VoltaicVisionary(this); + } +} + +class VoltaicVisionaryTriggeredAbility extends TriggeredAbilityImpl { + + VoltaicVisionaryTriggeredAbility() { + super(Zone.BATTLEFIELD, new TransformSourceEffect()); + this.addWatcher(new VoltaicVisionaryWatcher()); + } + + private VoltaicVisionaryTriggeredAbility(final VoltaicVisionaryTriggeredAbility ability) { + super(ability); + } + + @Override + public VoltaicVisionaryTriggeredAbility copy() { + return new VoltaicVisionaryTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.SPELL_CAST + || event.getType() == GameEvent.EventType.LAND_PLAYED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (!this.isControlledBy(event.getPlayerId())) { + return false; + } + Card card = game.getCard(event.getTargetId()); + return VoltaicVisionaryWatcher.checkCard(card, this, game); + } + + @Override + public String getTriggerPhrase() { + return "When you play a card exiled with {this}, "; + } +} + +class VoltaicVisionaryWatcher extends Watcher { + + private final Map> map = new HashMap<>(); + private static final Set emptySet = Collections.unmodifiableSet(new HashSet<>()); + + VoltaicVisionaryWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() != GameEvent.EventType.ZONE_CHANGE + || ((ZoneChangeEvent) event).getToZone() != Zone.EXILED) { + return; + } + Card card = game.getCard(event.getTargetId()); + UUID exileId = game + .getExile() + .getExileZones() + .stream().filter(exileZone -> exileZone.contains(card)) + .map(ExileZone::getId) + .findFirst() + .orElse(null); + if (exileId == null) { + return; + } + map.computeIfAbsent(exileId, x -> new HashSet<>()).add(new MageObjectReference(card, game)); + } + + @Override + public void reset() { + map.clear(); + super.reset(); + } + + static boolean checkCard(Card card, Ability source, Game game) { + return card != null + && game.getState() + .getWatcher(VoltaicVisionaryWatcher.class) + .map + .getOrDefault(CardUtil.getCardExileZoneId(game, source), emptySet) + .contains(new MageObjectReference(card, game, -1)); + } +} diff --git a/Mage.Sets/src/mage/cards/v/VoodooDoll.java b/Mage.Sets/src/mage/cards/v/VoodooDoll.java index 5cbc1565b80..50b63d6c081 100644 --- a/Mage.Sets/src/mage/cards/v/VoodooDoll.java +++ b/Mage.Sets/src/mage/cards/v/VoodooDoll.java @@ -44,7 +44,7 @@ public final class VoodooDoll extends CardImpl { Ability ability = new ConditionalInterveningIfTriggeredAbility( new BeginningOfEndStepTriggeredAbility( new DestroySourceEffect(), TargetController.YOU, false - ), new InvertCondition(SourceTappedCondition.instance), "At the beginning of your end step, " + + ), SourceTappedCondition.UNTAPPED, "At the beginning of your end step, " + "if {this} is untapped, destroy {this} and it deals damage to you equal to the number of pin counters on it." ); ability.addEffect(new DamageControllerEffect(new CountersSourceCount(CounterType.PIN))); diff --git a/Mage.Sets/src/mage/cards/v/VoraciousVampire.java b/Mage.Sets/src/mage/cards/v/VoraciousVampire.java index a663a0c5cbd..20b8ecc9403 100644 --- a/Mage.Sets/src/mage/cards/v/VoraciousVampire.java +++ b/Mage.Sets/src/mage/cards/v/VoraciousVampire.java @@ -32,7 +32,7 @@ public final class VoraciousVampire extends CardImpl { this.toughness = new MageInt(2); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // When Voracious Vampire enters the battlefield, target Vampire you control gets +1/+1 and gains menace until end of turn. Ability ability = new EntersBattlefieldTriggeredAbility(new BoostTargetEffect(1, 1, Duration.EndOfTurn), false); diff --git a/Mage.Sets/src/mage/cards/v/VowOfTorment.java b/Mage.Sets/src/mage/cards/v/VowOfTorment.java index d7882594507..1e87bcd9cf8 100644 --- a/Mage.Sets/src/mage/cards/v/VowOfTorment.java +++ b/Mage.Sets/src/mage/cards/v/VowOfTorment.java @@ -39,7 +39,8 @@ public final class VowOfTorment extends CardImpl { new MenaceAbility(), AttachmentType.AURA, Duration.WhileOnBattlefield ).setText(", has menace")); ability.addEffect(new CantAttackControllerAttachedEffect(AttachmentType.AURA) - .setText(", and can't attack you or planeswalkers you control")); + .setText(", and can't attack you or planeswalkers you control. " + + "(It can't be blocked except by two or more creatures.)")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/v/VraskaGolgariQueen.java b/Mage.Sets/src/mage/cards/v/VraskaGolgariQueen.java index 209f27da528..c69f628b9b1 100644 --- a/Mage.Sets/src/mage/cards/v/VraskaGolgariQueen.java +++ b/Mage.Sets/src/mage/cards/v/VraskaGolgariQueen.java @@ -3,7 +3,6 @@ package mage.cards.v; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.DoIfCostPaid; @@ -46,7 +45,7 @@ public final class VraskaGolgariQueen extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.VRASKA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +2: You may sacrifice another permanent. If you do, you gain 1 life and draw a card. DoIfCostPaid effect = new DoIfCostPaid( diff --git a/Mage.Sets/src/mage/cards/v/VraskaRegalGorgon.java b/Mage.Sets/src/mage/cards/v/VraskaRegalGorgon.java index d04676476da..0f008121a26 100644 --- a/Mage.Sets/src/mage/cards/v/VraskaRegalGorgon.java +++ b/Mage.Sets/src/mage/cards/v/VraskaRegalGorgon.java @@ -3,7 +3,6 @@ package mage.cards.v; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; @@ -34,7 +33,7 @@ public final class VraskaRegalGorgon extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.VRASKA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +2: Put a +1/+1 counter on up to one target creature. That creature gains menace until end of turn. Ability ability = new LoyaltyAbility(new AddCountersTargetEffect( diff --git a/Mage.Sets/src/mage/cards/v/VraskaRelicSeeker.java b/Mage.Sets/src/mage/cards/v/VraskaRelicSeeker.java index 39ef218f4c2..e38e6e63a96 100644 --- a/Mage.Sets/src/mage/cards/v/VraskaRelicSeeker.java +++ b/Mage.Sets/src/mage/cards/v/VraskaRelicSeeker.java @@ -1,29 +1,27 @@ - package mage.cards.v; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; -import mage.constants.SubType; -import mage.constants.SuperType; +import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; import mage.filter.StaticFilters; import mage.game.Game; -import mage.game.permanent.Permanent; import mage.game.permanent.token.PirateToken; import mage.game.permanent.token.TreasureToken; import mage.players.Player; import mage.target.TargetPermanent; import mage.target.TargetPlayer; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class VraskaRelicSeeker extends CardImpl { @@ -34,13 +32,14 @@ public final class VraskaRelicSeeker extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.VRASKA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(6)); + this.setStartingLoyalty(6); //+2: Create a 2/2 black Pirate creature token with menace. this.addAbility(new LoyaltyAbility(new CreateTokenEffect(new PirateToken()), 2)); //-3: Destroy target artifact, creature, or enchantment. Create a colorless Treasure artifact token with "T, Sacrfice this artifact. Add one mana of any color." - Ability ability = new LoyaltyAbility(new VraskaRelicSeekerDestroyEffect(), -3); + Ability ability = new LoyaltyAbility(new DestroyTargetEffect(), -3); + ability.addEffect(new CreateTokenEffect(new TreasureToken())); ability.addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_CREATURE_OR_ENCHANTMENT)); this.addAbility(ability); @@ -60,34 +59,6 @@ public final class VraskaRelicSeeker extends CardImpl { } } -class VraskaRelicSeekerDestroyEffect extends OneShotEffect { - - VraskaRelicSeekerDestroyEffect() { - super(Outcome.Benefit); - this.staticText = "Destroy target artifact, creature, or enchantment. Create a colorless Treasure artifact token with \"{T}, Sacrifice this artifact. Add one mana of any color.\""; - } - - VraskaRelicSeekerDestroyEffect(final VraskaRelicSeekerDestroyEffect effect) { - super(effect); - } - - @Override - public VraskaRelicSeekerDestroyEffect copy() { - return new VraskaRelicSeekerDestroyEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - for (UUID permanentId : targetPointer.getTargets(game, source)) { - Permanent permanent = game.getPermanent(permanentId); - if (permanent != null) { - permanent.destroy(source, game, false); - } - } - return new CreateTokenEffect(new TreasureToken()).apply(game, source); - } -} - class VraskaRelicSeekerLifeTotalEffect extends OneShotEffect { public VraskaRelicSeekerLifeTotalEffect() { diff --git a/Mage.Sets/src/mage/cards/v/VraskaSchemingGorgon.java b/Mage.Sets/src/mage/cards/v/VraskaSchemingGorgon.java index 6a7e023b2be..cc97be26051 100644 --- a/Mage.Sets/src/mage/cards/v/VraskaSchemingGorgon.java +++ b/Mage.Sets/src/mage/cards/v/VraskaSchemingGorgon.java @@ -3,7 +3,6 @@ package mage.cards.v; import mage.abilities.LoyaltyAbility; import mage.abilities.TriggeredAbility; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.LoseGameTargetPlayerEffect; import mage.abilities.effects.common.continuous.BoostControlledEffect; @@ -29,7 +28,7 @@ public final class VraskaSchemingGorgon extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.VRASKA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +2: Creatures you control get +1/+0 until end of turn. this.addAbility(new LoyaltyAbility(new BoostControlledEffect(1, 0, Duration.EndOfTurn), 2)); diff --git a/Mage.Sets/src/mage/cards/v/VraskaSwarmsEminence.java b/Mage.Sets/src/mage/cards/v/VraskaSwarmsEminence.java index 2c0746be50a..5f556ed1e54 100644 --- a/Mage.Sets/src/mage/cards/v/VraskaSwarmsEminence.java +++ b/Mage.Sets/src/mage/cards/v/VraskaSwarmsEminence.java @@ -2,7 +2,6 @@ package mage.cards.v; import mage.abilities.LoyaltyAbility; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.counter.AddCountersTargetEffect; @@ -41,7 +40,7 @@ public final class VraskaSwarmsEminence extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.VRASKA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // Whenever a creature you control with deathtouch deals damage to a player or planeswalker, put a +1/+1 counter on that creature. this.addAbility(new VraskaSwarmsEminenceTriggeredAbility(Zone.BATTLEFIELD, diff --git a/Mage.Sets/src/mage/cards/v/VraskaTheUnseen.java b/Mage.Sets/src/mage/cards/v/VraskaTheUnseen.java index d4bebd570b7..c55e5e1c357 100644 --- a/Mage.Sets/src/mage/cards/v/VraskaTheUnseen.java +++ b/Mage.Sets/src/mage/cards/v/VraskaTheUnseen.java @@ -3,7 +3,6 @@ package mage.cards.v; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.Effect; import mage.abilities.effects.common.CreateTokenEffect; @@ -37,7 +36,7 @@ public final class VraskaTheUnseen extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.VRASKA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +1: Until your next turn, whenever a creature deals combat damage to Vraska the Unseen, destroy that creature. this.addAbility(new LoyaltyAbility(new VraskaTheUnseenGainAbilityEffect(new VraskaTheUnseenTriggeredAbility()), 1)); @@ -123,7 +122,7 @@ class VraskaTheUnseenTriggeredAbility extends TriggeredAbilityImpl { Permanent sourceOfDamage = game.getPermanent(event.getSourceId()); if (sourceOfDamage != null && sourceOfDamage.isCreature(game)) { Effect effect = this.getEffects().get(0); - effect.setTargetPointer(new FixedTarget(sourceOfDamage.getId())); + effect.setTargetPointer(new FixedTarget(sourceOfDamage.getId(), game)); return true; } } diff --git a/Mage.Sets/src/mage/cards/w/Waildrifter.java b/Mage.Sets/src/mage/cards/w/Waildrifter.java index 14dde19604a..dec610a203d 100644 --- a/Mage.Sets/src/mage/cards/w/Waildrifter.java +++ b/Mage.Sets/src/mage/cards/w/Waildrifter.java @@ -24,7 +24,6 @@ public final class Waildrifter extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); this.color.setBlue(true); - this.transformable = true; this.nightCard = true; // Flying diff --git a/Mage.Sets/src/mage/cards/w/WakeTheDead.java b/Mage.Sets/src/mage/cards/w/WakeTheDead.java index 69b9add820e..2826698f6d8 100644 --- a/Mage.Sets/src/mage/cards/w/WakeTheDead.java +++ b/Mage.Sets/src/mage/cards/w/WakeTheDead.java @@ -17,8 +17,7 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.TurnPhase; import mage.constants.Zone; -import mage.filter.FilterCard; -import mage.filter.common.FilterCreatureCard; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -58,12 +57,11 @@ public final class WakeTheDead extends CardImpl { enum WakeTheDeadAdjuster implements TargetAdjuster { instance; - private static final FilterCard filter = new FilterCreatureCard("creature cards from your graveyard"); @Override public void adjustTargets(Ability ability, Game game) { ability.getTargets().clear(); - ability.addTarget(new TargetCardInYourGraveyard(ability.getManaCostsToPay().getX(), filter)); + ability.addTarget(new TargetCardInYourGraveyard(ability.getManaCostsToPay().getX(), StaticFilters.FILTER_CARD_CREATURES_YOUR_GRAVEYARD)); } } diff --git a/Mage.Sets/src/mage/cards/w/WakingTheTrolls.java b/Mage.Sets/src/mage/cards/w/WakingTheTrolls.java index 44cd1ecea7f..e8753fa5934 100644 --- a/Mage.Sets/src/mage/cards/w/WakingTheTrolls.java +++ b/Mage.Sets/src/mage/cards/w/WakingTheTrolls.java @@ -34,7 +34,7 @@ public final class WakingTheTrolls extends CardImpl { this.subtype.add(SubType.SAGA); - SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); + SagaAbility sagaAbility = new SagaAbility(this); // I — Destroy target land. sagaAbility.addChapterEffect( diff --git a/Mage.Sets/src/mage/cards/w/WalkerOfSecretWays.java b/Mage.Sets/src/mage/cards/w/WalkerOfSecretWays.java index 9f7addb5f67..c7c6d388991 100644 --- a/Mage.Sets/src/mage/cards/w/WalkerOfSecretWays.java +++ b/Mage.Sets/src/mage/cards/w/WalkerOfSecretWays.java @@ -43,7 +43,7 @@ public final class WalkerOfSecretWays extends CardImpl { this.toughness = new MageInt(2); // Ninjutsu {1}{U} ({1}{U}, Return an unblocked attacker you control to hand: Put this card onto the battlefield from your hand tapped and attacking.) - this.addAbility(new NinjutsuAbility(new ManaCostsImpl("{1}{U}"))); + this.addAbility(new NinjutsuAbility("{1}{U}")); // Whenever Walker of Secret Ways deals combat damage to a player, look at that player's hand. this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new WalkerOfSecretWaysEffect(), false, true)); diff --git a/Mage.Sets/src/mage/cards/w/WalkingSkyscraper.java b/Mage.Sets/src/mage/cards/w/WalkingSkyscraper.java new file mode 100644 index 00000000000..b8c12ef4adb --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WalkingSkyscraper.java @@ -0,0 +1,71 @@ +package mage.cards.w; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.SourceTappedCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.HexproofAbility; +import mage.abilities.keyword.TrampleAbility; +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.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.permanent.ModifiedPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WalkingSkyscraper extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("modified creature you control"); + + static { + filter.add(ModifiedPredicate.instance); + } + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter); + private static final Hint hint = new ValueHint("Modified creatures you control", xValue); + + public WalkingSkyscraper(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{8}"); + + this.subtype.add(SubType.CONSTRUCT); + this.power = new MageInt(8); + this.toughness = new MageInt(8); + + // This spell costs {1} less to cast for each modified creature you control. + this.addAbility(new SimpleStaticAbility(Zone.ALL, + new SpellCostReductionForEachSourceEffect(1, xValue) + ).addHint(hint)); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Walking Skyscraper has hexproof as long as it's untapped. + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new GainAbilitySourceEffect(HexproofAbility.getInstance()), + SourceTappedCondition.UNTAPPED, "{this} has hexproof as long as it's untapped" + ))); + } + + private WalkingSkyscraper(final WalkingSkyscraper card) { + super(card); + } + + @Override + public WalkingSkyscraper copy() { + return new WalkingSkyscraper(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WalkingWall.java b/Mage.Sets/src/mage/cards/w/WalkingWall.java index 1c4c0bbe9b6..0a76eee5909 100644 --- a/Mage.Sets/src/mage/cards/w/WalkingWall.java +++ b/Mage.Sets/src/mage/cards/w/WalkingWall.java @@ -1,4 +1,3 @@ - package mage.cards.w; import java.util.UUID; @@ -33,7 +32,7 @@ public final class WalkingWall extends CardImpl { // {3}: Walking Wall gets +3/-1 until end of turn and can attack this turn as though it didn't have defender. Activate this ability only once each turn. Ability ability = new LimitedTimesPerTurnActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(3, -1, Duration.EndOfTurn), new GenericManaCost(3)); - ability.addEffect(new CanAttackAsThoughItDidntHaveDefenderSourceEffect(Duration.EndOfTurn)); + ability.addEffect(new CanAttackAsThoughItDidntHaveDefenderSourceEffect(Duration.EndOfTurn, "and")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/w/WallOfMourning.java b/Mage.Sets/src/mage/cards/w/WallOfMourning.java index c97593d0a7c..6f0812e05ec 100644 --- a/Mage.Sets/src/mage/cards/w/WallOfMourning.java +++ b/Mage.Sets/src/mage/cards/w/WallOfMourning.java @@ -85,7 +85,7 @@ class WallOfMourningExileEffect extends OneShotEffect { player.moveCardsToExile( cards, source, game, false, CardUtil.getExileZoneId(game, source), - CardUtil.getSourceLogName(game, source) + CardUtil.getSourceName(game, source) ); for (Card card : cards) { card.setFaceDown(true, game); diff --git a/Mage.Sets/src/mage/cards/w/WallOfStolenIdentity.java b/Mage.Sets/src/mage/cards/w/WallOfStolenIdentity.java index 9e8ebf28902..fb50a9c4fc2 100644 --- a/Mage.Sets/src/mage/cards/w/WallOfStolenIdentity.java +++ b/Mage.Sets/src/mage/cards/w/WallOfStolenIdentity.java @@ -4,14 +4,11 @@ import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.condition.Condition; -import mage.abilities.decorator.ConditionalContinuousEffect; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.ContinuousRuleModifyingEffect; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.effects.EntersBattlefieldEffect; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.DontUntapInControllersUntapStepSourceEffect; -import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.DontUntapInControllersUntapStepTargetEffect; +import mage.abilities.effects.common.TapTargetEffect; import mage.abilities.keyword.DefenderAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -35,8 +32,8 @@ import java.util.UUID; */ public final class WallOfStolenIdentity extends CardImpl { - final static private String rule = "You may have Wall of Stolen Identity enter the battlefield as a copy of any " - + "creature on the battlefield, except it's a wall in addition to its other types and it has defender. " + final static private String rule = "You may have {this} enter the battlefield as a copy of any " + + "creature on the battlefield, except it's a Wall in addition to its other types and it has defender. " + "When you do, tap the copied creature and it doesn't untap during its " + "controller's untap step for as long as you control {this}"; @@ -49,9 +46,9 @@ public final class WallOfStolenIdentity extends CardImpl { this.toughness = new MageInt(0); // You may have Wall of Stolen Identity enter the battlefield as a copy of any creature on the battlefield, except it's a wall in addition to its other types and it has defender. When you do, tap the copied creature and it doesn't untap during its controller's untap step for as long as you control Wall of Stolen Identity. - Ability ability = new SimpleStaticAbility( - new EntersBattlefieldEffect(new WallOfStolenIdentityCopyEffect(), rule, true) - ); + Ability ability = new SimpleStaticAbility(new EntersBattlefieldEffect( + new WallOfStolenIdentityCopyEffect(), rule, true + )); this.addAbility(ability); } @@ -67,8 +64,7 @@ public final class WallOfStolenIdentity extends CardImpl { class WallOfStolenIdentityCopyEffect extends OneShotEffect { - private static final String rule2 = "When you do, tap the copied creature and it doesn't untap during its " - + "controller's untap step for as long as you control {this}."; + private static final String rule2 = "When you do, ."; public WallOfStolenIdentityCopyEffect() { super(Outcome.Copy); @@ -115,18 +111,13 @@ class WallOfStolenIdentityCopyEffect extends OneShotEffect { } }); - copyFromPermanent.tap(source, game); - // Incredibly, you can't just add a fixed target to a continuousrulemodifyingeffect, thus the workaround. - ContinuousRuleModifyingEffect effect = new DontUntapInControllersUntapStepSourceEffect(); - Ability ability = new SimpleStaticAbility(effect); - ContinuousEffect effect2 = new GainAbilityTargetEffect(ability, Duration.Custom); - ConditionalContinuousEffect conditionalEffect = new ConditionalContinuousEffect( - effect2, new WallOfStolenIdentityCondition( - source, - source.getControllerId(), - sourcePermanent.getZoneChangeCounter(game)), ""); - conditionalEffect.setTargetPointer(new FixedTarget(target.getFirstTarget())); - game.addEffect(conditionalEffect, source); + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new TapTargetEffect(), false, "tap the copied creature " + + "and it doesn't untap during its controller's untap step for as long as you control {this}" + ); + ability.addEffect(new DontUntapInControllersUntapStepTargetEffect(Duration.WhileControlled)); + ability.getEffects().setTargetPointer(new FixedTarget(copyFromPermanent, game)); + game.fireReflexiveTriggeredAbility(ability, source); return true; } @@ -135,27 +126,3 @@ class WallOfStolenIdentityCopyEffect extends OneShotEffect { return new WallOfStolenIdentityCopyEffect(this); } } - -class WallOfStolenIdentityCondition implements Condition { - - // Checks for when it leaves play or changes control - private final Ability ability; - private final UUID controllerId; - private final int zcc; - - public WallOfStolenIdentityCondition(Ability ability, UUID controllerId, int zcc) { - this.ability = ability; - this.controllerId = controllerId; - this.zcc = zcc; - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanentSource = game.getPermanent(ability.getSourceId()); - if (permanentSource != null) { - return permanentSource.getZoneChangeCounter(game) == zcc + 1 - && permanentSource.getControllerId() == controllerId; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/w/WallOfWonder.java b/Mage.Sets/src/mage/cards/w/WallOfWonder.java index ccdc5b62ead..e4c67d6ca42 100644 --- a/Mage.Sets/src/mage/cards/w/WallOfWonder.java +++ b/Mage.Sets/src/mage/cards/w/WallOfWonder.java @@ -34,7 +34,7 @@ public final class WallOfWonder extends CardImpl { // {2}{U}{U}: Wall of Wonder gets +4/-4 until end of turn and can attack this turn as though it didn't have defender. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(4, -4, Duration.EndOfTurn), new ManaCostsImpl("{2}{U}{U}")); - ability.addEffect(new CanAttackAsThoughItDidntHaveDefenderSourceEffect(Duration.EndOfTurn)); + ability.addEffect(new CanAttackAsThoughItDidntHaveDefenderSourceEffect(Duration.EndOfTurn, "and")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/w/WanderInDeath.java b/Mage.Sets/src/mage/cards/w/WanderInDeath.java index f7550907403..4001fb4f2be 100644 --- a/Mage.Sets/src/mage/cards/w/WanderInDeath.java +++ b/Mage.Sets/src/mage/cards/w/WanderInDeath.java @@ -1,14 +1,13 @@ - package mage.cards.w; import java.util.UUID; -import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; import mage.abilities.keyword.CyclingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterCreatureCard; +import mage.filter.StaticFilters; import mage.target.common.TargetCardInYourGraveyard; /** @@ -21,11 +20,11 @@ public final class WanderInDeath extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}"); // Return up to two target creature cards from your graveyard to your hand. - getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, 2, new FilterCreatureCard("creature cards from your graveyard"))); + getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, 2, StaticFilters.FILTER_CARD_CREATURES_YOUR_GRAVEYARD)); getSpellAbility().addEffect(new ReturnFromGraveyardToHandTargetEffect()); // Cycling {2} - this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}"))); + this.addAbility(new CyclingAbility(new GenericManaCost(2))); } private WanderInDeath(final WanderInDeath card) { diff --git a/Mage.Sets/src/mage/cards/w/WanderersIntervention.java b/Mage.Sets/src/mage/cards/w/WanderersIntervention.java new file mode 100644 index 00000000000..2693c750a18 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WanderersIntervention.java @@ -0,0 +1,32 @@ +package mage.cards.w; + +import mage.abilities.effects.common.DamageTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.target.common.TargetAttackingOrBlockingCreature; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WanderersIntervention extends CardImpl { + + public WanderersIntervention(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}"); + + // Wanderer's Intervention deals 4 damage to target attacking or blocking creature. + this.getSpellAbility().addEffect(new DamageTargetEffect(4)); + this.getSpellAbility().addTarget(new TargetAttackingOrBlockingCreature()); + } + + private WanderersIntervention(final WanderersIntervention card) { + super(card); + } + + @Override + public WanderersIntervention copy() { + return new WanderersIntervention(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WanderingMind.java b/Mage.Sets/src/mage/cards/w/WanderingMind.java new file mode 100644 index 00000000000..a1eb906b85d --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WanderingMind.java @@ -0,0 +1,57 @@ +package mage.cards.w; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.common.LookLibraryAndPickControllerEffect; +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.FilterCard; +import mage.filter.common.FilterNonlandCard; +import mage.filter.predicate.Predicates; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WanderingMind extends CardImpl { + + private static final FilterCard filter = new FilterNonlandCard("noncreature, nonland card"); + + static { + filter.add(Predicates.not(CardType.CREATURE.getPredicate())); + } + + public WanderingMind(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{R}"); + + this.subtype.add(SubType.HORROR); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When Wandering Mind enters the battlefield, look at the top six cards of your library. You may reveal a noncreature, nonland card from among them and put it into your hand. Put the rest on the bottom of your library in a random order. + this.addAbility(new EntersBattlefieldTriggeredAbility(new LookLibraryAndPickControllerEffect( + StaticValue.get(6), false, StaticValue.get(1), filter, Zone.LIBRARY, false, + true, false, Zone.HAND, true, false, false + ).setBackInRandomOrder(true).setText("look at the top six cards of your library. " + + "You may reveal a noncreature, nonland card from among them and put it into your hand. " + + "Put the rest on the bottom of your library in a random order"))); + } + + private WanderingMind(final WanderingMind card) { + super(card); + } + + @Override + public WanderingMind copy() { + return new WanderingMind(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WanderlightSpirit.java b/Mage.Sets/src/mage/cards/w/WanderlightSpirit.java new file mode 100644 index 00000000000..ba32a4bbc4a --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WanderlightSpirit.java @@ -0,0 +1,40 @@ +package mage.cards.w; + +import mage.MageInt; +import mage.abilities.common.CanBlockOnlyFlyingAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WanderlightSpirit extends CardImpl { + + public WanderlightSpirit(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); + + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Wanderlight Spirit can block only creatures with flying. + this.addAbility(new CanBlockOnlyFlyingAbility()); + } + + private WanderlightSpirit(final WanderlightSpirit card) { + super(card); + } + + @Override + public WanderlightSpirit copy() { + return new WanderlightSpirit(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WarHorn.java b/Mage.Sets/src/mage/cards/w/WarHorn.java index 9e47661ee2e..643d07e0204 100644 --- a/Mage.Sets/src/mage/cards/w/WarHorn.java +++ b/Mage.Sets/src/mage/cards/w/WarHorn.java @@ -1,4 +1,3 @@ - package mage.cards.w; import java.util.UUID; @@ -8,8 +7,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Zone; -import mage.filter.common.FilterAttackingCreature; +import mage.filter.StaticFilters; /** * @@ -22,8 +20,7 @@ public final class WarHorn extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}"); // Attacking creatures you control get +1/+0. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(1, 0, - Duration.WhileOnBattlefield, new FilterAttackingCreature(), false))); + this.addAbility(new SimpleStaticAbility(new BoostControlledEffect(1, 0, Duration.WhileOnBattlefield, StaticFilters.FILTER_ATTACKING_CREATURES))); } private WarHorn(final WarHorn card) { diff --git a/Mage.Sets/src/mage/cards/w/WarbriarBlessing.java b/Mage.Sets/src/mage/cards/w/WarbriarBlessing.java index b5f49038538..69174fca26b 100644 --- a/Mage.Sets/src/mage/cards/w/WarbriarBlessing.java +++ b/Mage.Sets/src/mage/cards/w/WarbriarBlessing.java @@ -59,7 +59,8 @@ class WarbriarBlessingEffect extends OneShotEffect { WarbriarBlessingEffect() { super(Outcome.Benefit); - staticText = "enchanted creature fights up to one target creature you don't control"; + staticText = "enchanted creature fights up to one target creature you don't control. " + + "(Each deals damage equal to its power to the other.)"; } private WarbriarBlessingEffect(final WarbriarBlessingEffect effect) { diff --git a/Mage.Sets/src/mage/cards/w/WarchanterOfMogis.java b/Mage.Sets/src/mage/cards/w/WarchanterOfMogis.java index d369a8a8d7d..db87371f9c0 100644 --- a/Mage.Sets/src/mage/cards/w/WarchanterOfMogis.java +++ b/Mage.Sets/src/mage/cards/w/WarchanterOfMogis.java @@ -1,9 +1,9 @@ - package mage.cards.w; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; +import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.keyword.InspiredAbility; import mage.abilities.keyword.IntimidateAbility; @@ -29,7 +29,9 @@ public final class WarchanterOfMogis extends CardImpl { this.toughness = new MageInt(3); // Inspired — Whenever Warchanter of Mogis becomes untapped, target creature you control gains intimidate until end of turn. - Ability ability = new InspiredAbility(new GainAbilityTargetEffect(IntimidateAbility.getInstance(), Duration.EndOfTurn), false); + Effect effect = new GainAbilityTargetEffect(IntimidateAbility.getInstance(), Duration.EndOfTurn); + effect.setText("target creature you control gains intimidate until end of turn. (A creature with intimidate can't be blocked except by artifact creatures and/or creatures that share a color with it.)"); + Ability ability = new InspiredAbility(effect); ability.addTarget(new TargetControlledCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/w/WardOfBones.java b/Mage.Sets/src/mage/cards/w/WardOfBones.java index 04e2a740934..5b7ce8d7744 100644 --- a/Mage.Sets/src/mage/cards/w/WardOfBones.java +++ b/Mage.Sets/src/mage/cards/w/WardOfBones.java @@ -103,8 +103,8 @@ class WardOfBonesEffect extends ContinuousRuleModifyingEffectImpl { return true; } if (card.isEnchantment(game) - && game.getBattlefield().countAll(StaticFilters.FILTER_ENCHANTMENT_PERMANENT, opponent.getId(), game) - > game.getBattlefield().countAll(StaticFilters.FILTER_ENCHANTMENT_PERMANENT, source.getControllerId(), game)) { + && game.getBattlefield().countAll(StaticFilters.FILTER_PERMANENT_ENCHANTMENT, opponent.getId(), game) + > game.getBattlefield().countAll(StaticFilters.FILTER_PERMANENT_ENCHANTMENT, source.getControllerId(), game)) { return true; } final int yourLands = game.getBattlefield().countAll(new FilterLandPermanent(), source.getControllerId(), game); diff --git a/Mage.Sets/src/mage/cards/w/WardSliver.java b/Mage.Sets/src/mage/cards/w/WardSliver.java index a0417bcf7af..a4d02eb8fed 100644 --- a/Mage.Sets/src/mage/cards/w/WardSliver.java +++ b/Mage.Sets/src/mage/cards/w/WardSliver.java @@ -14,6 +14,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; import mage.game.Game; @@ -56,17 +57,11 @@ public final class WardSliver extends CardImpl { class WardSliverGainAbilityControlledEffect extends ContinuousEffectImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Slivers"); - - static { - filter.add(SubType.SLIVER.getPredicate()); - } - protected FilterPermanent protectionFilter; public WardSliverGainAbilityControlledEffect() { super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); - staticText = "Slivers have protection from the chosen color"; + staticText = "all Slivers have protection from the chosen color"; } public WardSliverGainAbilityControlledEffect(final WardSliverGainAbilityControlledEffect effect) { @@ -92,7 +87,7 @@ class WardSliverGainAbilityControlledEffect extends ContinuousEffectImpl { } } if (protectionFilter != null) { - for (Permanent perm: game.getBattlefield().getAllActivePermanents(filter, game)) { + for (Permanent perm: game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_ALL_SLIVERS, game)) { perm.addAbility(new ProtectionAbility(protectionFilter), source.getSourceId(), game); } return true; diff --git a/Mage.Sets/src/mage/cards/w/WardenOfTheWoods.java b/Mage.Sets/src/mage/cards/w/WardenOfTheWoods.java index 4091621422b..001903736c0 100644 --- a/Mage.Sets/src/mage/cards/w/WardenOfTheWoods.java +++ b/Mage.Sets/src/mage/cards/w/WardenOfTheWoods.java @@ -33,8 +33,7 @@ public final class WardenOfTheWoods extends CardImpl { new DrawCardSourceControllerEffect(2), StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, SetTargetPointer.NONE, true - )); - + ).setTriggerPhrase("Whenever {this} becomes the target of a spell or ability an opponent controls, ")); } private WardenOfTheWoods(final WardenOfTheWoods card) { diff --git a/Mage.Sets/src/mage/cards/w/WarmongerHellkite.java b/Mage.Sets/src/mage/cards/w/WarmongerHellkite.java index 5fdd519484b..87cc9ce01d9 100644 --- a/Mage.Sets/src/mage/cards/w/WarmongerHellkite.java +++ b/Mage.Sets/src/mage/cards/w/WarmongerHellkite.java @@ -1,4 +1,3 @@ - package mage.cards.w; import java.util.UUID; @@ -13,11 +12,9 @@ import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.Zone; -import static mage.filter.StaticFilters.FILTER_PERMANENT_CREATURES; -import mage.filter.common.FilterAttackingCreature; +import mage.constants.SubType; +import mage.filter.StaticFilters; /** * @@ -36,12 +33,10 @@ public final class WarmongerHellkite extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // All creatures attack each combat if able. - Effect effect = new AttacksIfAbleAllEffect(FILTER_PERMANENT_CREATURES, Duration.WhileOnBattlefield, true); - effect.setText("All creatures attack each combat if able"); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); + this.addAbility(new SimpleStaticAbility(new AttacksIfAbleAllEffect(StaticFilters.FILTER_PERMANENT_ALL_CREATURES, Duration.WhileOnBattlefield, true))); // {1}{R}: Attacking creatures get +1/+0 until end of turn. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostAllEffect(1, 0, Duration.EndOfTurn, new FilterAttackingCreature("Attacking creatures"), false), new ManaCostsImpl("{1}{R}"))); + this.addAbility(new SimpleActivatedAbility(new BoostAllEffect(1, 0, Duration.EndOfTurn, StaticFilters.FILTER_ATTACKING_CREATURES, false), new ManaCostsImpl("{1}{R}"))); } diff --git a/Mage.Sets/src/mage/cards/w/WarsToll.java b/Mage.Sets/src/mage/cards/w/WarsToll.java index 6a91e6e0f58..234e999ebbc 100644 --- a/Mage.Sets/src/mage/cards/w/WarsToll.java +++ b/Mage.Sets/src/mage/cards/w/WarsToll.java @@ -9,7 +9,6 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.StaticFilters; -import mage.filter.common.FilterCreaturePermanent; import mage.filter.common.FilterLandPermanent; import mage.game.Game; import mage.game.permanent.Permanent; @@ -21,12 +20,9 @@ import java.util.UUID; * @author jeffwadsworth */ public final class WarsToll extends CardImpl { - - private static final FilterCreaturePermanent filterOpponentCreature = new FilterCreaturePermanent("creature an opponent controls"); private static final FilterLandPermanent filterOpponentLand = new FilterLandPermanent("an opponent taps a land"); static { - filterOpponentCreature.add(TargetController.OPPONENT.getControllerPredicate()); filterOpponentLand.add(TargetController.OPPONENT.getControllerPredicate()); } @@ -100,13 +96,13 @@ class WarsTollAttackRestrictionEffect extends RestrictionEffect { @Override public boolean canAttackCheckAfter(int numberOfAttackers, Ability source, Game game, boolean canUseChooseDialogs) { - int creaturesAbleToAttack = 0; + if (numberOfAttackers == 0) return true; for (Permanent creaturePermanent : game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURES, game.getActivePlayerId(), game)) { - if (creaturePermanent.canAttackInPrinciple(null, game)) { - creaturesAbleToAttack++; + if (creaturePermanent.canAttack(null, game) && !creaturePermanent.isAttacking()) { + return false; } } - return numberOfAttackers == 0 || numberOfAttackers == creaturesAbleToAttack; + return true; } @Override diff --git a/Mage.Sets/src/mage/cards/w/WarteyeWitch.java b/Mage.Sets/src/mage/cards/w/WarteyeWitch.java index 6249cb2d981..bd3406b324f 100644 --- a/Mage.Sets/src/mage/cards/w/WarteyeWitch.java +++ b/Mage.Sets/src/mage/cards/w/WarteyeWitch.java @@ -34,7 +34,7 @@ public final class WarteyeWitch extends CardImpl { // Whenever Warteye Witch or another creature you control dies, scry 1. this.addAbility(new DiesThisOrAnotherCreatureTriggeredAbility( - new ScryEffect(1), false, filter + new ScryEffect(1, false), false, filter )); } diff --git a/Mage.Sets/src/mage/cards/w/WashAway.java b/Mage.Sets/src/mage/cards/w/WashAway.java new file mode 100644 index 00000000000..3f9507797a0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WashAway.java @@ -0,0 +1,49 @@ +package mage.cards.w; + +import mage.abilities.Ability; +import mage.abilities.effects.common.CounterTargetEffect; +import mage.abilities.keyword.CleaveAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.filter.FilterSpell; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.card.CastFromZonePredicate; +import mage.target.TargetSpell; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WashAway extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("spell [that wasn't cast from its owner's hand]"); + + static { + filter.add(Predicates.not(new CastFromZonePredicate(Zone.HAND))); + } + + public WashAway(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{U}"); + + // Cleave {1}{U}{U} + Ability ability = new CleaveAbility(this, new CounterTargetEffect(), "{1}{U}{U}"); + ability.addTarget(new TargetSpell()); + this.addAbility(ability); + + // Counter target spell [that wasn't cast from its owner's hand]. + this.getSpellAbility().addEffect(new CounterTargetEffect()); + this.getSpellAbility().addTarget(new TargetSpell(filter)); + } + + private WashAway(final WashAway card) { + super(card); + } + + @Override + public WashAway copy() { + return new WashAway(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WashOut.java b/Mage.Sets/src/mage/cards/w/WashOut.java index 78674b76efa..de7251549bd 100644 --- a/Mage.Sets/src/mage/cards/w/WashOut.java +++ b/Mage.Sets/src/mage/cards/w/WashOut.java @@ -12,10 +12,8 @@ import mage.cards.CardSetInfo; import mage.choices.ChoiceColor; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.FilterPermanent; -import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; import mage.game.Game; import mage.game.permanent.Permanent; @@ -27,18 +25,11 @@ import mage.players.Player; */ public final class WashOut extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public WashOut(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{U}"); // Return all permanents of the color of your choice to their owners' hands. this.getSpellAbility().addEffect(new WashOutEffect()); - } private WashOut(final WashOut card) { diff --git a/Mage.Sets/src/mage/cards/w/Watchdog.java b/Mage.Sets/src/mage/cards/w/Watchdog.java index 155ae8d897d..f32ef7b8d4a 100644 --- a/Mage.Sets/src/mage/cards/w/Watchdog.java +++ b/Mage.Sets/src/mage/cards/w/Watchdog.java @@ -36,7 +36,7 @@ public final class Watchdog extends CardImpl { addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BlocksIfAbleSourceEffect(Duration.WhileOnBattlefield))); // As long as Watchdog is untapped, all creatures attacking you get -1/-0. addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect( - new BoostAllEffect(-1, 0, Duration.WhileOnBattlefield, new WatchdogFilter(), false), new InvertCondition(SourceTappedCondition.instance), "As long as {this} is untapped, all creatures attacking you get -1/-0"))); + new BoostAllEffect(-1, 0, Duration.WhileOnBattlefield, new WatchdogFilter(), false), SourceTappedCondition.UNTAPPED, "As long as {this} is untapped, all creatures attacking you get -1/-0"))); } private Watchdog(final Watchdog card) { diff --git a/Mage.Sets/src/mage/cards/w/WatertrapWeaver.java b/Mage.Sets/src/mage/cards/w/WatertrapWeaver.java index bf9feae114b..69117a4272b 100644 --- a/Mage.Sets/src/mage/cards/w/WatertrapWeaver.java +++ b/Mage.Sets/src/mage/cards/w/WatertrapWeaver.java @@ -10,8 +10,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; /** @@ -20,12 +19,6 @@ import mage.target.common.TargetCreaturePermanent; */ public final class WatertrapWeaver extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public WatertrapWeaver(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); @@ -37,7 +30,7 @@ public final class WatertrapWeaver extends CardImpl { // When Watertrap Weaver enters the battlefield, tap target creature an opponent controls. That creature doesn't untap during its controller's next untap step. EntersBattlefieldTriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new TapTargetEffect()); ability.addEffect(new DontUntapInControllersNextUntapStepTargetEffect("that creature")); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/w/WavecrashTriton.java b/Mage.Sets/src/mage/cards/w/WavecrashTriton.java index 4c177e12753..902c3bfb60d 100644 --- a/Mage.Sets/src/mage/cards/w/WavecrashTriton.java +++ b/Mage.Sets/src/mage/cards/w/WavecrashTriton.java @@ -11,8 +11,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; /** @@ -20,11 +19,6 @@ import mage.target.common.TargetCreaturePermanent; * @author LevelX2 */ public final class WavecrashTriton extends CardImpl { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - static{ - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } public WavecrashTriton(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{U}"); @@ -36,7 +30,7 @@ public final class WavecrashTriton extends CardImpl { // Heroic - Whenever you cast a spell that targets Wavecrash Triton, tap target creature an opponent controls. That creature doesn't untap during its controller's next untap step. Ability ability = new HeroicAbility(new TapTargetEffect()); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); ability.addEffect(new DontUntapInControllersNextUntapStepTargetEffect("That creature")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/w/WaxingMoon.java b/Mage.Sets/src/mage/cards/w/WaxingMoon.java index 2d6a8a3e8f6..d3b293fb03f 100644 --- a/Mage.Sets/src/mage/cards/w/WaxingMoon.java +++ b/Mage.Sets/src/mage/cards/w/WaxingMoon.java @@ -1,45 +1,43 @@ - package mage.cards.w; -import java.util.UUID; -import mage.abilities.effects.Effect; -import mage.abilities.effects.common.TransformTargetEffect; -import mage.abilities.effects.common.continuous.GainAbilityAllEffect; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.abilities.keyword.TrampleAbility; 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.TargetController; -import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.common.FilterCreaturePermanent; -import mage.target.common.TargetCreaturePermanent; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledPermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class WaxingMoon extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Werewolf you control"); - - static { - filter.add(SubType.WEREWOLF.getPredicate()); - filter.add(TargetController.YOU.getControllerPredicate()); - } + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.WEREWOLF); public WaxingMoon(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}"); // Transform up to one target Werewolf you control. - Effect effect = new TransformTargetEffect(false); - effect.setText("Transform up to one target Werewolf you control"); - this.getSpellAbility().addEffect(effect); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(0, 1, filter, false)); + this.getSpellAbility().addEffect(new WaxingMoonEffect()); + this.getSpellAbility().addTarget(new TargetPermanent(0, 1, filter)); // Creatures you control gain trample until end of turn. - this.getSpellAbility().addEffect(new GainAbilityAllEffect(TrampleAbility.getInstance(), Duration.EndOfTurn, new FilterControlledCreaturePermanent(), "Creatures you control gain trample until end of turn")); + this.getSpellAbility().addEffect(new GainAbilityControlledEffect( + TrampleAbility.getInstance(), Duration.EndOfTurn, + StaticFilters.FILTER_PERMANENT_CREATURES + )); } private WaxingMoon(final WaxingMoon card) { @@ -51,3 +49,26 @@ public final class WaxingMoon extends CardImpl { return new WaxingMoon(this); } } + +class WaxingMoonEffect extends OneShotEffect { + + WaxingMoonEffect() { + super(Outcome.Benefit); + staticText = "transform up to one target Werewolf you control"; + } + + private WaxingMoonEffect(final WaxingMoonEffect effect) { + super(effect); + } + + @Override + public WaxingMoonEffect copy() { + return new WaxingMoonEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + return permanent != null && permanent.transform(source, game); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WaxmaneBaku.java b/Mage.Sets/src/mage/cards/w/WaxmaneBaku.java index 57810d4765a..adc0c455924 100644 --- a/Mage.Sets/src/mage/cards/w/WaxmaneBaku.java +++ b/Mage.Sets/src/mage/cards/w/WaxmaneBaku.java @@ -1,8 +1,5 @@ - - package mage.cards.w; -import java.util.List; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; @@ -11,21 +8,18 @@ import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.costs.Cost; import mage.abilities.costs.common.RemoveVariableCountersSourceCost; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.TapTargetEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Outcome; -import mage.constants.Zone; import mage.counters.CounterType; -import mage.filter.FilterPermanent; import mage.filter.StaticFilters; -import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetadjustment.TargetAdjuster; + /** * @author LevelX2 @@ -40,11 +34,12 @@ public final class WaxmaneBaku extends CardImpl { this.toughness = new MageInt(2); // Whenever you cast a Spirit or Arcane spell, you may put a ki counter on Waxmane Baku. - this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.KI.createInstance()), StaticFilters.SPIRIT_OR_ARCANE_CARD, true)); + this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.KI.createInstance()), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true)); // {1}, Remove X ki counters from Waxmane Baku: Tap X target creatures. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new WaxmaneBakuTapEffect(), new GenericManaCost(1)); - ability.addCost(new RemoveVariableCountersSourceCost(CounterType.KI.createInstance(1))); + Ability ability = new SimpleActivatedAbility(new TapTargetEffect("tap X target creatures"), new GenericManaCost(1)); + ability.addCost(new RemoveVariableCountersSourceCost(CounterType.KI.createInstance())); + ability.setTargetAdjuster(WaxmaneBakuAdjuster.instance); this.addAbility(ability); } @@ -58,45 +53,18 @@ public final class WaxmaneBaku extends CardImpl { } } -class WaxmaneBakuTapEffect extends OneShotEffect { - - private static final FilterPermanent filter = new FilterCreaturePermanent(); - - public WaxmaneBakuTapEffect() { - super(Outcome.Tap); - staticText = "Tap X target creatures"; - } - - public WaxmaneBakuTapEffect(final WaxmaneBakuTapEffect effect) { - super(effect); - } +enum WaxmaneBakuAdjuster implements TargetAdjuster { + instance; @Override - public boolean apply(Game game, Ability source) { - int numberToTap = 0; - for (Cost cost : source.getCosts()) { + public void adjustTargets(Ability ability, Game game) { + int xValue = 0; + for (Cost cost : ability.getCosts()) { if (cost instanceof RemoveVariableCountersSourceCost) { - numberToTap = ((RemoveVariableCountersSourceCost) cost).getAmount(); + xValue = ((RemoveVariableCountersSourceCost) cost).getAmount(); } } - TargetPermanent target = new TargetPermanent(numberToTap, filter); - if (target.canChoose(source.getSourceId(), source.getControllerId(), game) && target.choose(Outcome.Tap, source.getControllerId(), source.getSourceId(), game)) { - if (!target.getTargets().isEmpty()) { - List targets = target.getTargets(); - for (UUID targetId : targets) { - Permanent permanent = game.getPermanent(targetId); - if (permanent != null) { - permanent.tap(source, game); - } - } - } - return true; - } - return false; - } - - @Override - public WaxmaneBakuTapEffect copy() { - return new WaxmaneBakuTapEffect(this); + ability.getTargets().clear(); + ability.addTarget(new TargetCreaturePermanent(xValue)); } } diff --git a/Mage.Sets/src/mage/cards/w/WaywardGiant.java b/Mage.Sets/src/mage/cards/w/WaywardGiant.java index 5e1cf130685..05cc2039fb3 100644 --- a/Mage.Sets/src/mage/cards/w/WaywardGiant.java +++ b/Mage.Sets/src/mage/cards/w/WaywardGiant.java @@ -22,7 +22,7 @@ public final class WaywardGiant extends CardImpl { this.toughness = new MageInt(5); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); } private WaywardGiant(final WaywardGiant card) { diff --git a/Mage.Sets/src/mage/cards/w/Weakstone.java b/Mage.Sets/src/mage/cards/w/Weakstone.java index 1e3dc102246..fa8b4ad1c4e 100644 --- a/Mage.Sets/src/mage/cards/w/Weakstone.java +++ b/Mage.Sets/src/mage/cards/w/Weakstone.java @@ -8,9 +8,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AttackingPredicate; +import mage.filter.StaticFilters; /** * @@ -18,17 +16,11 @@ import mage.filter.predicate.permanent.AttackingPredicate; */ public final class Weakstone extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("attacking creatures"); - - static { - filter.add(AttackingPredicate.instance); - } - public Weakstone(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{4}"); // Attacking creatures get -1/-0. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostAllEffect( -1, 0, Duration.WhileOnBattlefield, filter, false))); + this.addAbility(new SimpleStaticAbility(new BoostAllEffect(-1, 0, Duration.WhileOnBattlefield, StaticFilters.FILTER_ATTACKING_CREATURES, false))); } private Weakstone(final Weakstone card) { diff --git a/Mage.Sets/src/mage/cards/w/WearyPrisoner.java b/Mage.Sets/src/mage/cards/w/WearyPrisoner.java new file mode 100644 index 00000000000..83b8d2469ac --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WearyPrisoner.java @@ -0,0 +1,43 @@ +package mage.cards.w; + +import mage.MageInt; +import mage.abilities.keyword.DayboundAbility; +import mage.abilities.keyword.DefenderAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WearyPrisoner extends CardImpl { + + public WearyPrisoner(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WEREWOLF); + this.power = new MageInt(2); + this.toughness = new MageInt(6); + + this.secondSideCardClazz = mage.cards.w.WrathfulJailbreaker.class; + + // Defender + this.addAbility(DefenderAbility.getInstance()); + + // Daybound + this.addAbility(new DayboundAbility()); + } + + private WearyPrisoner(final WearyPrisoner card) { + super(card); + } + + @Override + public WearyPrisoner copy() { + return new WearyPrisoner(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WeaverOfBlossoms.java b/Mage.Sets/src/mage/cards/w/WeaverOfBlossoms.java new file mode 100644 index 00000000000..3f00c090a34 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WeaverOfBlossoms.java @@ -0,0 +1,42 @@ +package mage.cards.w; + +import mage.MageInt; +import mage.abilities.keyword.DayboundAbility; +import mage.abilities.mana.AnyColorManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WeaverOfBlossoms extends CardImpl { + + public WeaverOfBlossoms(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WEREWOLF); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + this.secondSideCardClazz = mage.cards.b.BlossomCladWerewolf.class; + + // {T}: Add one mana of any color. + this.addAbility(new AnyColorManaAbility()); + + // Daybound + this.addAbility(new DayboundAbility()); + } + + private WeaverOfBlossoms(final WeaverOfBlossoms card) { + super(card); + } + + @Override + public WeaverOfBlossoms copy() { + return new WeaverOfBlossoms(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WeaverOfHarmony.java b/Mage.Sets/src/mage/cards/w/WeaverOfHarmony.java new file mode 100644 index 00000000000..4285daf1d1c --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WeaverOfHarmony.java @@ -0,0 +1,108 @@ +package mage.cards.w; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +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.filter.FilterStackObject; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicate; +import mage.game.Game; +import mage.game.stack.StackAbility; +import mage.game.stack.StackObject; +import mage.target.TargetStackObject; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WeaverOfHarmony extends CardImpl { + + private static final FilterCreaturePermanent filter + = new FilterCreaturePermanent("enchantment creatures"); + private static final FilterStackObject filter2 + = new FilterStackObject("activated or triggered ability you control from an enchantment source"); + + static { + filter.add(CardType.ENCHANTMENT.getPredicate()); + filter2.add(WeaverOfHarmonyPredicate.instance); + } + + public WeaverOfHarmony(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{1}{G}"); + + this.subtype.add(SubType.SNAKE); + this.subtype.add(SubType.DRUID); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Other enchantment creatures you control get +1/+1. + this.addAbility(new SimpleStaticAbility(new BoostControlledEffect( + 1, 1, Duration.WhileOnBattlefield, filter, true + ))); + + // {G}, {T}: Copy target activated or triggered ability you control from an enchantment source. You may choose new targets for the copy. + Ability ability = new SimpleActivatedAbility(new WeaverOfHarmonyEffect(), new ManaCostsImpl<>("{G}")); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetStackObject(filter2)); + this.addAbility(ability); + } + + private WeaverOfHarmony(final WeaverOfHarmony card) { + super(card); + } + + @Override + public WeaverOfHarmony copy() { + return new WeaverOfHarmony(this); + } +} + +enum WeaverOfHarmonyPredicate implements Predicate { + instance; + + @Override + public boolean apply(StackObject input, Game game) { + return input instanceof StackAbility + && ((StackAbility) input).getSourceObject(game).isEnchantment(game); + } +} + +class WeaverOfHarmonyEffect extends OneShotEffect { + + WeaverOfHarmonyEffect() { + super(Outcome.Benefit); + staticText = "copy target activated or triggered ability you control " + + "from an enchantment source. You may choose new targets for the copy"; + } + + private WeaverOfHarmonyEffect(final WeaverOfHarmonyEffect effect) { + super(effect); + } + + @Override + public WeaverOfHarmonyEffect copy() { + return new WeaverOfHarmonyEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(targetPointer.getFirst(game, source)); + if (stackAbility == null) { + return false; + } + stackAbility.createCopyOnStack(game, source, source.getControllerId(), true); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/w/WeaverOfLightning.java b/Mage.Sets/src/mage/cards/w/WeaverOfLightning.java index 39e73a97c7b..20c29c29850 100644 --- a/Mage.Sets/src/mage/cards/w/WeaverOfLightning.java +++ b/Mage.Sets/src/mage/cards/w/WeaverOfLightning.java @@ -11,8 +11,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.filter.common.FilterInstantOrSorcerySpell; import mage.target.common.TargetCreaturePermanent; @@ -22,12 +21,6 @@ import mage.target.common.TargetCreaturePermanent; */ public final class WeaverOfLightning extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public WeaverOfLightning(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{R}"); this.subtype.add(SubType.HUMAN); @@ -39,7 +32,7 @@ public final class WeaverOfLightning extends CardImpl { this.addAbility(ReachAbility.getInstance()); // Whenever you cast an instant or sorcery spell, Weaver of Lightning deals 1 damage to target creature an opponent controls. Ability ability = new SpellCastControllerTriggeredAbility(new DamageTargetEffect(1), new FilterInstantOrSorcerySpell(), false); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/w/WebspinnerCuff.java b/Mage.Sets/src/mage/cards/w/WebspinnerCuff.java new file mode 100644 index 00000000000..b3fae27debe --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WebspinnerCuff.java @@ -0,0 +1,52 @@ +package mage.cards.w; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.continuous.BoostEquippedEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.ReachAbility; +import mage.abilities.keyword.ReconfigureAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WebspinnerCuff extends CardImpl { + + public WebspinnerCuff(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{2}{G}"); + + this.subtype.add(SubType.EQUIPMENT); + this.subtype.add(SubType.SPIDER); + this.power = new MageInt(1); + this.toughness = new MageInt(4); + + // Reach + this.addAbility(ReachAbility.getInstance()); + + // Equipped creature gets +1/+4 and has reach. + Ability ability = new SimpleStaticAbility(new BoostEquippedEffect(1, 4)); + ability.addEffect(new GainAbilityAttachedEffect( + ReachAbility.getInstance(), AttachmentType.EQUIPMENT + ).setText("and has reach")); + + // Reconfigure {4} + this.addAbility(new ReconfigureAbility("{4}")); + } + + private WebspinnerCuff(final WebspinnerCuff card) { + super(card); + } + + @Override + public WebspinnerCuff copy() { + return new WebspinnerCuff(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WeddingAnnouncement.java b/Mage.Sets/src/mage/cards/w/WeddingAnnouncement.java new file mode 100644 index 00000000000..b4c84d8afd7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WeddingAnnouncement.java @@ -0,0 +1,72 @@ +package mage.cards.w; + +import java.util.UUID; + +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfYourEndStepTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.SourceHasCounterCondition; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.TransformAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.token.HumanToken; +import mage.watchers.common.AttackedThisTurnWatcher; + +/** + * + * @author weirddan455 + */ +public final class WeddingAnnouncement extends CardImpl { + + public WeddingAnnouncement(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); + + this.secondSideCardClazz = mage.cards.w.WeddingFestivity.class; + + // At the beginning of your end step, put an invitation counter on Wedding Announcement. + // If you attacked with two or more creatures this turn, draw card. + // Otherwise, create a 1/1 white Human creature token. + // Then if Wedding Announcement has three or more invitation counters on it, transform it. + this.addAbility(new TransformAbility()); + Ability ability = new BeginningOfYourEndStepTriggeredAbility(new AddCountersSourceEffect(CounterType.INVITATION.createInstance()), false); + ability.addEffect(new ConditionalOneShotEffect( + new DrawCardSourceControllerEffect(1), + new CreateTokenEffect(new HumanToken()), + WeddingAnnouncementCondition.instance, + "If you attacked with two or more creatures this turn, draw a card. Otherwise, create a 1/1 white Human creature token" + )); + ability.addEffect(new ConditionalOneShotEffect( + new TransformSourceEffect(), + new SourceHasCounterCondition(CounterType.INVITATION, 3), + "Then if {this} has three or more invitation counters on it, transform it" + )); + this.addAbility(ability, new AttackedThisTurnWatcher()); + } + + private WeddingAnnouncement(final WeddingAnnouncement card) { + super(card); + } + + @Override + public WeddingAnnouncement copy() { + return new WeddingAnnouncement(this); + } +} + +enum WeddingAnnouncementCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + AttackedThisTurnWatcher watcher = game.getState().getWatcher(AttackedThisTurnWatcher.class); + return watcher != null && watcher.getAttackedThisTurnCreatures().size() >= 2; + } +} diff --git a/Mage.Sets/src/mage/cards/w/WeddingCrasher.java b/Mage.Sets/src/mage/cards/w/WeddingCrasher.java new file mode 100644 index 00000000000..4dfe68c8dee --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WeddingCrasher.java @@ -0,0 +1,57 @@ +package mage.cards.w; + +import mage.MageInt; +import mage.abilities.common.DiesThisOrAnotherCreatureTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.keyword.NightboundAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WeddingCrasher extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent("Wolf or Werewolf you control"); + + static { + filter.add(Predicates.or( + SubType.WOLF.getPredicate(), + SubType.WEREWOLF.getPredicate() + )); + } + + public WeddingCrasher(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); + + this.subtype.add(SubType.WEREWOLF); + this.power = new MageInt(6); + this.toughness = new MageInt(5); + this.color.setGreen(true); + this.nightCard = true; + + // Whenever Wedding Crasher or another Wolf or Werewolf you control dies, draw a card. + this.addAbility(new DiesThisOrAnotherCreatureTriggeredAbility( + new DrawCardSourceControllerEffect(1), false, filter + )); + + // Nightbound + this.addAbility(new NightboundAbility()); + } + + private WeddingCrasher(final WeddingCrasher card) { + super(card); + } + + @Override + public WeddingCrasher copy() { + return new WeddingCrasher(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WeddingFestivity.java b/Mage.Sets/src/mage/cards/w/WeddingFestivity.java new file mode 100644 index 00000000000..e456652aa21 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WeddingFestivity.java @@ -0,0 +1,36 @@ +package mage.cards.w; + +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; + +import java.util.UUID; + +/** + * + * @author weirddan455 + */ +public class WeddingFestivity extends CardImpl { + + public WeddingFestivity(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, ""); + + this.color.setWhite(true); + this.nightCard = true; + + // Creatures you control get +1/+1 + this.addAbility(new SimpleStaticAbility(new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield))); + } + + private WeddingFestivity(final WeddingFestivity card) { + super(card); + } + + @Override + public WeddingFestivity copy() { + return new WeddingFestivity(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WeddingInvitation.java b/Mage.Sets/src/mage/cards/w/WeddingInvitation.java new file mode 100644 index 00000000000..a05d10eaa68 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WeddingInvitation.java @@ -0,0 +1,80 @@ +package mage.cards.w; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.combat.CantBeBlockedTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WeddingInvitation extends CardImpl { + + public WeddingInvitation(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); + + // When Wedding Invitation enters the battlefield, draw a card. + this.addAbility(new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(1))); + + // {T}, Sacrifice Wedding Invitation: Target creature can't be blocked this turn. If it's a Vampire, it also gains lifelink until end of turn. + Ability ability = new SimpleActivatedAbility( + new CantBeBlockedTargetEffect(Duration.EndOfTurn), new TapSourceCost() + ); + ability.addCost(new SacrificeSourceCost()); + ability.addEffect(new WeddingInvitationEffect()); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + private WeddingInvitation(final WeddingInvitation card) { + super(card); + } + + @Override + public WeddingInvitation copy() { + return new WeddingInvitation(this); + } +} + +class WeddingInvitationEffect extends OneShotEffect { + + WeddingInvitationEffect() { + super(Outcome.Benefit); + staticText = "If it's a Vampire, it also gains lifelink until end of turn"; + } + + private WeddingInvitationEffect(final WeddingInvitationEffect effect) { + super(effect); + } + + @Override + public WeddingInvitationEffect copy() { + return new WeddingInvitationEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent == null) { + return false; + } + game.addEffect(new GainAbilityTargetEffect(LifelinkAbility.getInstance(), Duration.EndOfTurn), source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/w/WeddingRing.java b/Mage.Sets/src/mage/cards/w/WeddingRing.java new file mode 100644 index 00000000000..d0bf4debbf2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WeddingRing.java @@ -0,0 +1,145 @@ +package mage.cards.w; + +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.common.CastFromEverywhereSourceCondition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenCopyTargetEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledArtifactPermanent; +import mage.filter.predicate.mageobject.NamePredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetOpponent; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WeddingRing extends CardImpl { + + public WeddingRing(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{W}{W}"); + + // When Wedding Ring enters the battlefield, if it was cast, target opponent creates a token that's a copy of it. + Ability ability = new ConditionalInterveningIfTriggeredAbility( + new EntersBattlefieldTriggeredAbility(new WeddingRingEffect()), + CastFromEverywhereSourceCondition.instance, "When {this} enters the battlefield, " + + "if it was cast, target opponent creates a token that's a copy of it." + ); + ability.addTarget(new TargetOpponent()); + this.addAbility(ability); + + // Whenever an opponent who controls an artifact named Wedding Ring draws a card during their turn, you draw a card. + this.addAbility(new WeddingRingTriggeredAbility(true)); + + // Whenever an opponent who controls an artifact named Wedding Ring gains life during their turn, you gain that much life. + this.addAbility(new WeddingRingTriggeredAbility(false)); + } + + private WeddingRing(final WeddingRing card) { + super(card); + } + + @Override + public WeddingRing copy() { + return new WeddingRing(this); + } +} + +class WeddingRingEffect extends OneShotEffect { + + WeddingRingEffect() { + super(Outcome.Benefit); + } + + private WeddingRingEffect(final WeddingRingEffect effect) { + super(effect); + } + + @Override + public WeddingRingEffect copy() { + return new WeddingRingEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getFirstTarget()); + Permanent permanent = source.getSourcePermanentOrLKI(game); + if (player == null || permanent == null) { + return false; + } + return new CreateTokenCopyTargetEffect(player.getId()) + .setTargetPointer(new FixedTarget(permanent, game)) + .apply(game, source); + } +} + +class WeddingRingTriggeredAbility extends TriggeredAbilityImpl { + + private static final FilterPermanent filter = new FilterControlledArtifactPermanent(); + + static { + filter.add(new NamePredicate("Wedding Ring")); + } + + private final boolean drawCards; + + WeddingRingTriggeredAbility(boolean drawCards) { + super(Zone.BATTLEFIELD, null); + this.drawCards = drawCards; + } + + private WeddingRingTriggeredAbility(final WeddingRingTriggeredAbility ability) { + super(ability); + this.drawCards = ability.drawCards; + } + + @Override + public WeddingRingTriggeredAbility copy() { + return new WeddingRingTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return drawCards + ? event.getType() == GameEvent.EventType.DREW_CARD + : event.getType() == GameEvent.EventType.GAINED_LIFE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (!game.isActivePlayer(event.getPlayerId()) + || !game.getOpponents(getControllerId()).contains(event.getPlayerId()) + || !game.getBattlefield().contains(filter, getSourceId(), event.getPlayerId(), game, 1)) { + return false; + } + this.getEffects().clear(); + if (drawCards) { + this.addEffect(new DrawCardSourceControllerEffect(1)); + } else { + this.addEffect(new GainLifeEffect(event.getAmount())); + } + return true; + } + + @Override + public String getRule() { + return "Whenever an opponent who controls an artifact named Wedding Ring " + + (drawCards ? "draws a card during their turn, you draw a card." + : "gains life during their turn, you gain that much life."); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WeddingSecurity.java b/Mage.Sets/src/mage/cards/w/WeddingSecurity.java new file mode 100644 index 00000000000..afbd7fffea1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WeddingSecurity.java @@ -0,0 +1,55 @@ +package mage.cards.w; + +import mage.MageInt; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WeddingSecurity extends CardImpl { + + private static final FilterControlledPermanent filter = new FilterControlledPermanent("a Blood token"); + + static { + filter.add(TokenPredicate.TRUE); + filter.add(SubType.BLOOD.getPredicate()); + } + + public WeddingSecurity(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{B}"); + + this.subtype.add(SubType.VAMPIRE); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Whenever Wedding Security attacks, you may sacrifice a Blood token. If you do, put a +1/+1 counter on Wedding Security and draw a card. + this.addAbility(new AttacksTriggeredAbility(new DoIfCostPaid( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), + new SacrificeTargetCost(new TargetControlledPermanent(filter)) + ).addEffect(new DrawCardSourceControllerEffect(1).concatBy("and")))); + } + + private WeddingSecurity(final WeddingSecurity card) { + super(card); + } + + @Override + public WeddingSecurity copy() { + return new WeddingSecurity(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WeeDragonauts.java b/Mage.Sets/src/mage/cards/w/WeeDragonauts.java index f839dc43bd6..3c19e16aee0 100644 --- a/Mage.Sets/src/mage/cards/w/WeeDragonauts.java +++ b/Mage.Sets/src/mage/cards/w/WeeDragonauts.java @@ -1,7 +1,5 @@ - package mage.cards.w; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.effects.common.continuous.BoostSourceEffect; @@ -9,9 +7,11 @@ import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; -import mage.filter.common.FilterInstantOrSorcerySpell; +import mage.constants.SubType; +import mage.filter.StaticFilters; + +import java.util.UUID; /** * @author Loki @@ -19,14 +19,14 @@ import mage.filter.common.FilterInstantOrSorcerySpell; public final class WeeDragonauts extends CardImpl { public WeeDragonauts(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{R}"); this.subtype.add(SubType.FAERIE); this.subtype.add(SubType.WIZARD); this.power = new MageInt(1); this.toughness = new MageInt(3); this.addAbility(FlyingAbility.getInstance()); - this.addAbility(new SpellCastControllerTriggeredAbility(new BoostSourceEffect(2, 0, Duration.EndOfTurn), new FilterInstantOrSorcerySpell(), false)); + this.addAbility(new SpellCastControllerTriggeredAbility(new BoostSourceEffect(2, 0, Duration.EndOfTurn), StaticFilters.FILTER_SPELL_AN_INSTANT_OR_SORCERY, false)); } private WeeDragonauts(final WeeDragonauts card) { diff --git a/Mage.Sets/src/mage/cards/w/WeightOfConscience.java b/Mage.Sets/src/mage/cards/w/WeightOfConscience.java index ec39a5d89ae..e0ff6e12b24 100644 --- a/Mage.Sets/src/mage/cards/w/WeightOfConscience.java +++ b/Mage.Sets/src/mage/cards/w/WeightOfConscience.java @@ -5,8 +5,8 @@ import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.common.TapTargetCost; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.ExileAttachedEffect; import mage.abilities.effects.common.combat.CantAttackAttachedEffect; import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; @@ -46,7 +46,7 @@ public final class WeightOfConscience extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantAttackAttachedEffect(AttachmentType.AURA))); // Tap two untapped creatures you control that share a creature type: Exile enchanted creature. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new WeightOfConscienceEffect(), new TapTargetCost(new WeightOfConscienceTarget()))); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileAttachedEffect(), new TapTargetCost(new WeightOfConscienceTarget()))); } private WeightOfConscience(final WeightOfConscience card) { @@ -59,43 +59,6 @@ public final class WeightOfConscience extends CardImpl { } } -class WeightOfConscienceEffect extends OneShotEffect { - - WeightOfConscienceEffect() { - super(Outcome.Exile); - staticText = "Exile enchanted creature"; - } - - WeightOfConscienceEffect(final WeightOfConscienceEffect effect) { - super(effect); - } - - @Override - public WeightOfConscienceEffect copy() { - return new WeightOfConscienceEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - // In the case that the enchantment is blinked - Permanent enchantment = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); - if (enchantment == null) { - // It was not blinked, use the standard method - enchantment = game.getPermanentOrLKIBattlefield(source.getSourceId()); - } - if (controller != null - && enchantment != null - && enchantment.getAttachedTo() != null) { - Permanent creature = game.getPermanent(enchantment.getAttachedTo()); - if (creature != null) { - controller.moveCardsToExile(creature, source, game, true, null, ""); - } - } - return false; - } -} - class WeightOfConscienceTarget extends TargetControlledCreaturePermanent { private static final FilterControlledCreaturePermanent filterUntapped = new FilterControlledCreaturePermanent("untapped creatures you control that share a creature type"); diff --git a/Mage.Sets/src/mage/cards/w/WelcomingVampire.java b/Mage.Sets/src/mage/cards/w/WelcomingVampire.java new file mode 100644 index 00000000000..19d22413cd3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WelcomingVampire.java @@ -0,0 +1,52 @@ +package mage.cards.w; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.constants.ComparisonType; +import mage.constants.SubType; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.filter.predicate.mageobject.PowerPredicate; + +/** + * + * @author weirddan455 + */ +public final class WelcomingVampire extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("one or more other creatures with power 2 or less"); + + static { + filter.add(AnotherPredicate.instance); + filter.add(new PowerPredicate(ComparisonType.FEWER_THAN, 3)); + } + + public WelcomingVampire(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.subtype.add(SubType.VAMPIRE); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever one or more other creatures with power 2 or less enter the battlefield under your control, draw a card. This ability triggers only once each turn. + this.addAbility(new EntersBattlefieldControlledTriggeredAbility(new DrawCardSourceControllerEffect(1), filter).setTriggersOnce(true)); + } + + private WelcomingVampire(final WelcomingVampire card) { + super(card); + } + + @Override + public WelcomingVampire copy() { + return new WelcomingVampire(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WeldfastMonitor.java b/Mage.Sets/src/mage/cards/w/WeldfastMonitor.java index 4995f5e6bf2..4776a5919de 100644 --- a/Mage.Sets/src/mage/cards/w/WeldfastMonitor.java +++ b/Mage.Sets/src/mage/cards/w/WeldfastMonitor.java @@ -28,8 +28,10 @@ public final class WeldfastMonitor extends CardImpl { this.toughness = new MageInt(2); // {R}: Weldfast Monitor gains menace until end of turn - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, - new GainAbilitySourceEffect(new MenaceAbility(), Duration.EndOfTurn), new ColoredManaCost(ColoredManaSymbol.R))); + this.addAbility(new SimpleActivatedAbility( + Zone.BATTLEFIELD, + new GainAbilitySourceEffect(new MenaceAbility(false), Duration.EndOfTurn), + new ColoredManaCost(ColoredManaSymbol.R))); } diff --git a/Mage.Sets/src/mage/cards/w/WerewolfOfAncientHunger.java b/Mage.Sets/src/mage/cards/w/WerewolfOfAncientHunger.java index 511f969c939..9a834b909b7 100644 --- a/Mage.Sets/src/mage/cards/w/WerewolfOfAncientHunger.java +++ b/Mage.Sets/src/mage/cards/w/WerewolfOfAncientHunger.java @@ -31,7 +31,6 @@ public final class WerewolfOfAncientHunger extends CardImpl { this.color.setGreen(true); this.nightCard = true; - this.transformable = true; // Vigilance this.addAbility(VigilanceAbility.getInstance()); diff --git a/Mage.Sets/src/mage/cards/w/WerewolfRansacker.java b/Mage.Sets/src/mage/cards/w/WerewolfRansacker.java index bc1614ded9f..81e7164a5ce 100644 --- a/Mage.Sets/src/mage/cards/w/WerewolfRansacker.java +++ b/Mage.Sets/src/mage/cards/w/WerewolfRansacker.java @@ -2,7 +2,7 @@ package mage.cards.w; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.TransformIntoSourceTriggeredAbility; import mage.abilities.common.WerewolfBackTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; @@ -12,7 +12,6 @@ import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetArtifactPermanent; @@ -31,13 +30,14 @@ public final class WerewolfRansacker extends CardImpl { // this card is the second face of double-faced card this.nightCard = true; - this.transformable = true; this.power = new MageInt(5); this.toughness = new MageInt(4); // Whenever this creature transforms into Werewolf Ransacker, you may destroy target artifact. If that artifact is put into a graveyard this way, Werewolf Ransacker deals 3 damage to that artifact's controller. - this.addAbility(new WerewolfRansackerAbility()); + Ability ability = new TransformIntoSourceTriggeredAbility(new WerewolfRansackerEffect(), true, true); + ability.addTarget(new TargetArtifactPermanent()); + this.addAbility(ability); // At the beginning of each upkeep, if a player cast two or more spells last turn, transform Werewolf Ransacker. this.addAbility(new WerewolfBackTriggeredAbility()); @@ -53,47 +53,12 @@ public final class WerewolfRansacker extends CardImpl { } } -class WerewolfRansackerAbility extends TriggeredAbilityImpl { - - WerewolfRansackerAbility() { - super(Zone.BATTLEFIELD, new WerewolfRansackerEffect(), true); - this.addTarget(new TargetArtifactPermanent()); - } - - private WerewolfRansackerAbility(final WerewolfRansackerAbility ability) { - super(ability); - } - - @Override - public WerewolfRansackerAbility copy() { - return new WerewolfRansackerAbility(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); - return permanent != null && permanent.isTransformed(); - } - return false; - } - - @Override - public String getRule() { - return "Whenever this creature transforms into {this}, you may destroy target artifact. If that artifact " + - "is put into a graveyard this way, {this} deals 3 damage to that artifact's controller"; - } -} - class WerewolfRansackerEffect extends OneShotEffect { WerewolfRansackerEffect() { super(Outcome.DestroyPermanent); + staticText = "destroy target artifact. If that artifact is put into a graveyard this way, " + + "{this} deals 3 damage to that artifact's controller"; } private WerewolfRansackerEffect(final WerewolfRansackerEffect effect) { diff --git a/Mage.Sets/src/mage/cards/w/WernogRidersChaplain.java b/Mage.Sets/src/mage/cards/w/WernogRidersChaplain.java new file mode 100644 index 00000000000..47578b92509 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WernogRidersChaplain.java @@ -0,0 +1,90 @@ +package mage.cards.w; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldOrLeavesSourceTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.FriendsForeverAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.token.ClueArtifactToken; +import mage.game.permanent.token.Token; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WernogRidersChaplain extends CardImpl { + + public WernogRidersChaplain(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.power = new MageInt(1); + this.toughness = new MageInt(2); + + // When Will the Wise enters or leaves the battlefield, each opponent may investigate. Each opponent who doesn't loses 1 life. You investigate X times, where X is one plus the number of opponents who investigated this way. + this.addAbility(new EntersBattlefieldOrLeavesSourceTriggeredAbility(new WernogRidersChaplainEffect(), false)); + + // Friends forever + this.addAbility(FriendsForeverAbility.getInstance()); + } + + private WernogRidersChaplain(final WernogRidersChaplain card) { + super(card); + } + + @Override + public WernogRidersChaplain copy() { + return new WernogRidersChaplain(this); + } +} + +class WernogRidersChaplainEffect extends OneShotEffect { + + WernogRidersChaplainEffect() { + super(Outcome.Benefit); + staticText = "each opponent may investigate. Each opponent who doesn't loses 1 life. " + + "You investigate X times, where X is one plus the number of opponents who investigated this way"; + } + + private WernogRidersChaplainEffect(final WernogRidersChaplainEffect effect) { + super(effect); + } + + @Override + public WernogRidersChaplainEffect copy() { + return new WernogRidersChaplainEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + int count = 1; + Token token = new ClueArtifactToken(); + for (UUID playerId : game.getOpponents(source.getControllerId())) { + Player opponent = game.getPlayer(playerId); + if (opponent == null) { + continue; + } + if (opponent.chooseUse(outcome, "Investigate?", source, game)) { + count++; + token.putOntoBattlefield(1, game, source, playerId); + game.fireEvent(GameEvent.getEvent(GameEvent.EventType.INVESTIGATED, source.getSourceId(), source, playerId)); + } else { + opponent.loseLife(1, game, source, false); + } + } + token.putOntoBattlefield(count, game, source, source.getControllerId()); + game.fireEvent(GameEvent.getEvent(GameEvent.EventType.INVESTIGATED, source.getSourceId(), source, source.getControllerId())); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/w/WestvaleAbbey.java b/Mage.Sets/src/mage/cards/w/WestvaleAbbey.java index d3b45ed59f7..9e52eaf3d88 100644 --- a/Mage.Sets/src/mage/cards/w/WestvaleAbbey.java +++ b/Mage.Sets/src/mage/cards/w/WestvaleAbbey.java @@ -30,7 +30,6 @@ public final class WestvaleAbbey extends CardImpl { public WestvaleAbbey(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); - this.transformable = true; this.secondSideCardClazz = mage.cards.o.OrmendahlProfanePrince.class; // {T}: Add {C}. @@ -44,7 +43,7 @@ public final class WestvaleAbbey extends CardImpl { // {5}, {T}, Sacrifice five creatures: Transform Westvale Abbey and untap it. this.addAbility(new TransformAbility()); - ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new TransformSourceEffect(true), new GenericManaCost(5)); + ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new TransformSourceEffect(), new GenericManaCost(5)); ability.addCost(new TapSourceCost()); ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(5, 5, new FilterControlledCreaturePermanent("creatures"), true))); ability.addEffect(new UntapSourceEffect()); diff --git a/Mage.Sets/src/mage/cards/w/WhenWeWereYoung.java b/Mage.Sets/src/mage/cards/w/WhenWeWereYoung.java new file mode 100644 index 00000000000..480437d5b49 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WhenWeWereYoung.java @@ -0,0 +1,69 @@ +package mage.cards.w; + +import mage.abilities.Ability; +import mage.abilities.condition.common.ControlArtifactAndEnchantmentCondition; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.hint.common.ControlArtifactAndEnchantmentHint; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.game.Game; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WhenWeWereYoung extends CardImpl { + + public WhenWeWereYoung(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{W}"); + + // Up to two target creatures each get +2/+2 until end of turn. If you control an artifact and an enchantment, those creatures also gain lifelink until end of turn. + this.getSpellAbility().addEffect(new WhenWeWereYoungEffect()); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(0, 2)); + this.getSpellAbility().addHint(ControlArtifactAndEnchantmentHint.instance); + } + + private WhenWeWereYoung(final WhenWeWereYoung card) { + super(card); + } + + @Override + public WhenWeWereYoung copy() { + return new WhenWeWereYoung(this); + } +} + +class WhenWeWereYoungEffect extends OneShotEffect { + + WhenWeWereYoungEffect() { + super(Outcome.Benefit); + staticText = "up to two target creatures each get +2/+2 until end of turn. " + + "If you control an artifact and an enchantment, " + + "those creatures also gain lifelink until end of turn"; + } + + private WhenWeWereYoungEffect(final WhenWeWereYoungEffect effect) { + super(effect); + } + + @Override + public WhenWeWereYoungEffect copy() { + return new WhenWeWereYoungEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + game.addEffect(new BoostTargetEffect(2, 2), source); + if (ControlArtifactAndEnchantmentCondition.instance.apply(game, source)) { + game.addEffect(new GainAbilityTargetEffect(LifelinkAbility.getInstance()), source); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/w/WhiptongueHydra.java b/Mage.Sets/src/mage/cards/w/WhiptongueHydra.java index a37a3aaecef..f42b3b81156 100644 --- a/Mage.Sets/src/mage/cards/w/WhiptongueHydra.java +++ b/Mage.Sets/src/mage/cards/w/WhiptongueHydra.java @@ -60,7 +60,7 @@ class WhiptongueHydraEffect extends OneShotEffect { public WhiptongueHydraEffect() { super(Outcome.DestroyPermanent); this.staticText = "destroy all creatures with flying. " - + "Put a +1/+1 counter on {this} for each permanent destroyed this way"; + + "Put a +1/+1 counter on {this} for each creature destroyed this way"; } public WhiptongueHydraEffect(final WhiptongueHydraEffect effect) { diff --git a/Mage.Sets/src/mage/cards/w/WhisperingWizard.java b/Mage.Sets/src/mage/cards/w/WhisperingWizard.java new file mode 100644 index 00000000000..8695a5a2152 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WhisperingWizard.java @@ -0,0 +1,43 @@ +package mage.cards.w; + +import mage.MageInt; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +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.StaticFilters; +import mage.game.permanent.token.SpiritWhiteToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WhisperingWizard extends CardImpl { + + public WhisperingWizard(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // Whenever you cast a noncreature spell, create a 1/1 white Spirit creature token with flying. This ability triggers only once each turn. + this.addAbility(new SpellCastControllerTriggeredAbility( + new CreateTokenEffect(new SpiritWhiteToken()), + StaticFilters.FILTER_SPELL_A_NON_CREATURE, false + ).setTriggersOnce(true)); + } + + private WhisperingWizard(final WhisperingWizard card) { + super(card); + } + + @Override + public WhisperingWizard copy() { + return new WhisperingWizard(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WhispersteelDagger.java b/Mage.Sets/src/mage/cards/w/WhispersteelDagger.java index ee3e33bb529..7937707bcdc 100644 --- a/Mage.Sets/src/mage/cards/w/WhispersteelDagger.java +++ b/Mage.Sets/src/mage/cards/w/WhispersteelDagger.java @@ -15,6 +15,7 @@ import mage.constants.*; import mage.game.Game; import mage.game.events.GameEvent; import mage.players.ManaPoolItem; +import mage.util.CardUtil; import mage.watchers.Watcher; import java.util.HashMap; @@ -189,6 +190,6 @@ class WhispersteelDaggerWatcher extends Watcher { ); morMap.computeIfAbsent(mor, m -> new HashMap<>()) .computeIfAbsent(ownerId, m -> new HashMap<>()) - .compute(source.getControllerId(), (u, i) -> i == null ? 1 : Integer.sum(i, 1)); + .compute(source.getControllerId(), CardUtil::setOrIncrementValue); } } diff --git a/Mage.Sets/src/mage/cards/w/WhiteWard.java b/Mage.Sets/src/mage/cards/w/WhiteWard.java index 7d44b76e44a..b5bbdbddb47 100644 --- a/Mage.Sets/src/mage/cards/w/WhiteWard.java +++ b/Mage.Sets/src/mage/cards/w/WhiteWard.java @@ -1,10 +1,7 @@ - package mage.cards.w; -import java.util.UUID; import mage.ObjectColor; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; import mage.abilities.keyword.EnchantAbility; @@ -13,28 +10,20 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.AttachmentType; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; -import mage.constants.Zone; -import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.ColorPredicate; +import mage.constants.SubType; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LoneFox */ public final class WhiteWard extends CardImpl { - private static final FilterCard filter = new FilterCard("white"); - - static { - filter.add(new ColorPredicate(ObjectColor.WHITE)); - } - public WhiteWard(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}"); this.subtype.add(SubType.AURA); // Enchant creature @@ -42,12 +31,11 @@ public final class WhiteWard extends CardImpl { this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.Protect)); this.addAbility(new EnchantAbility(auraTarget.getTargetName())); + // Enchanted creature has protection from white. This effect doesn't remove White Ward. - ProtectionAbility gainedAbility = new ProtectionAbility(filter); - gainedAbility.setAuraIdNotToBeRemoved(this.getId()); - Effect effect = new GainAbilityAttachedEffect(gainedAbility, AttachmentType.AURA); - effect.setText("Enchanted creature has protection from white. This effect doesn't remove {this}."); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); + this.addAbility(new SimpleStaticAbility(new GainAbilityAttachedEffect( + ProtectionAbility.from(ObjectColor.WHITE), AttachmentType.AURA + ).setDoesntRemoveItself(true))); } private WhiteWard(final WhiteWard card) { diff --git a/Mage.Sets/src/mage/cards/w/WickedPact.java b/Mage.Sets/src/mage/cards/w/WickedPact.java index ce5980afd34..7af7624f848 100644 --- a/Mage.Sets/src/mage/cards/w/WickedPact.java +++ b/Mage.Sets/src/mage/cards/w/WickedPact.java @@ -1,16 +1,12 @@ - package mage.cards.w; import java.util.UUID; -import mage.ObjectColor; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.LoseLifeSourceControllerEffect; 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; /** @@ -18,19 +14,13 @@ import mage.target.common.TargetCreaturePermanent; * @author fireshoes */ public final class WickedPact extends CardImpl { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } public WickedPact(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{B}{B}"); // Destroy two target nonblack creatures. You lose 5 life. this.getSpellAbility().addEffect(new DestroyTargetEffect()); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(2, 2, filter, false)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(2, 2, StaticFilters.FILTER_PERMANENT_CREATURES_NON_BLACK, false)); this.getSpellAbility().addEffect(new LoseLifeSourceControllerEffect(5)); } diff --git a/Mage.Sets/src/mage/cards/w/WildDefiance.java b/Mage.Sets/src/mage/cards/w/WildDefiance.java index 1ec4341a2a5..472b3a60e16 100644 --- a/Mage.Sets/src/mage/cards/w/WildDefiance.java +++ b/Mage.Sets/src/mage/cards/w/WildDefiance.java @@ -1,4 +1,3 @@ - package mage.cards.w; import java.util.UUID; @@ -24,7 +23,7 @@ import mage.target.targetpointer.FixedTarget; public final class WildDefiance extends CardImpl { public WildDefiance(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}"); // Whenever a creature you control becomes the target of an instant or sorcery spell, that creature gets +3/+3 until end of turn. this.addAbility(new CreaturesYouControlBecomesTargetTriggeredAbility(new BoostTargetEffect(3, 3, Duration.EndOfTurn))); @@ -63,14 +62,16 @@ class CreaturesYouControlBecomesTargetTriggeredAbility extends TriggeredAbilityI @Override public boolean checkTrigger(GameEvent event, Game game) { Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null && permanent.isControlledBy(this.controllerId) && permanent.isCreature(game)) { + if (permanent != null + && permanent.isControlledBy(this.controllerId) + && permanent.isCreature(game)) { MageObject object = game.getObject(event.getSourceId()); if (object instanceof Spell) { Card c = (Spell) object; if (c.isInstantOrSorcery(game)) { if (getTargets().isEmpty()) { for (Effect effect : getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getTargetId())); + effect.setTargetPointer(new FixedTarget(event.getTargetId(), game)); } } return true; diff --git a/Mage.Sets/src/mage/cards/w/WildInstincts.java b/Mage.Sets/src/mage/cards/w/WildInstincts.java index b6b62fc6d18..eef26a7ebbf 100644 --- a/Mage.Sets/src/mage/cards/w/WildInstincts.java +++ b/Mage.Sets/src/mage/cards/w/WildInstincts.java @@ -10,6 +10,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.TargetController; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; @@ -20,23 +21,19 @@ import mage.target.common.TargetCreaturePermanent; */ public final class WildInstincts extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public WildInstincts(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{G}"); // Target creature you control gets +2/+2 until end of turn. It fights target creature an opponent controls. - Effect effect = new BoostTargetEffect(2, 2, Duration.EndOfTurn); - getSpellAbility().addEffect(effect); + Effect boostTargetEffect = new BoostTargetEffect(2, 2, Duration.EndOfTurn); + getSpellAbility().addEffect(boostTargetEffect); getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); - effect = new FightTargetsEffect(); - effect.setText("It fights target creature an opponent controls (Each deals damage equal to its power to each other)"); - getSpellAbility().addEffect(effect); - getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + Effect fightTargetsEffect = new FightTargetsEffect(); + fightTargetsEffect.setText("It fights target creature an opponent controls. " + + "(Each deals damage equal to its power to the other.)"); + getSpellAbility().addEffect(fightTargetsEffect); + + getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); } private WildInstincts(final WildInstincts card) { diff --git a/Mage.Sets/src/mage/cards/w/WildbloodPack.java b/Mage.Sets/src/mage/cards/w/WildbloodPack.java index 07981c1cdd0..6dd0d6f908a 100644 --- a/Mage.Sets/src/mage/cards/w/WildbloodPack.java +++ b/Mage.Sets/src/mage/cards/w/WildbloodPack.java @@ -26,7 +26,6 @@ public final class WildbloodPack extends CardImpl { // this card is the second face of double-faced card this.nightCard = true; - this.transformable = true; this.power = new MageInt(5); this.toughness = new MageInt(5); diff --git a/Mage.Sets/src/mage/cards/w/WildsongHowler.java b/Mage.Sets/src/mage/cards/w/WildsongHowler.java new file mode 100644 index 00000000000..39ef19a7e50 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WildsongHowler.java @@ -0,0 +1,50 @@ +package mage.cards.w; + +import mage.MageInt; +import mage.abilities.common.TransformsOrEntersTriggeredAbility; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.common.LookLibraryAndPickControllerEffect; +import mage.abilities.keyword.NightboundAbility; +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 java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WildsongHowler extends CardImpl { + + public WildsongHowler(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); + + this.subtype.add(SubType.WEREWOLF); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + this.color.setGreen(true); + this.nightCard = true; + + // Whenever this creature enters the battlefield or transforms into Wildsong Howler, look at the top six cards of your library. You may reveal a creature card from among them and put it into your hand. Put the rest on the bottom of your library in a random order. + this.addAbility(new TransformsOrEntersTriggeredAbility(new LookLibraryAndPickControllerEffect( + StaticValue.get(5), false, StaticValue.get(1), + StaticFilters.FILTER_CARD_CREATURE, Zone.LIBRARY, false, + true, false, Zone.HAND, true, false, false + ).setText("look at the top six cards of your library. You may reveal a creature card from among them and put it into your hand. Put the rest on the bottom of your library in a random order"), false)); + + // Nightbound + this.addAbility(new NightboundAbility()); + } + + private WildsongHowler(final WildsongHowler card) { + super(card); + } + + @Override + public WildsongHowler copy() { + return new WildsongHowler(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WildwoodRebirth.java b/Mage.Sets/src/mage/cards/w/WildwoodRebirth.java index 9bbb50d48e4..3d0922394b8 100644 --- a/Mage.Sets/src/mage/cards/w/WildwoodRebirth.java +++ b/Mage.Sets/src/mage/cards/w/WildwoodRebirth.java @@ -1,10 +1,10 @@ - package mage.cards.w; import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.filter.StaticFilters; import mage.target.common.TargetCardInYourGraveyard; import java.util.UUID; @@ -19,7 +19,7 @@ public final class WildwoodRebirth extends CardImpl { // Return target creature card from your graveyard to your hand. this.getSpellAbility().addEffect(new ReturnFromGraveyardToHandTargetEffect()); - this.getSpellAbility().addTarget(new TargetCardInYourGraveyard()); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); } private WildwoodRebirth(final WildwoodRebirth card) { diff --git a/Mage.Sets/src/mage/cards/w/WillKenrith.java b/Mage.Sets/src/mage/cards/w/WillKenrith.java index 5cf2c4e7713..acf8238397b 100644 --- a/Mage.Sets/src/mage/cards/w/WillKenrith.java +++ b/Mage.Sets/src/mage/cards/w/WillKenrith.java @@ -3,7 +3,6 @@ package mage.cards.w; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.common.CanBeYourCommanderAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DrawCardTargetEffect; @@ -34,7 +33,7 @@ public final class WillKenrith extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.WILL); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +2: Until your next turn, up to two target creatures each have base power and toughness 0/3 and lose all abilities. Ability ability = new LoyaltyAbility( diff --git a/Mage.Sets/src/mage/cards/w/Willbreaker.java b/Mage.Sets/src/mage/cards/w/Willbreaker.java index f75f3face03..5a75ce72f4b 100644 --- a/Mage.Sets/src/mage/cards/w/Willbreaker.java +++ b/Mage.Sets/src/mage/cards/w/Willbreaker.java @@ -1,11 +1,7 @@ package mage.cards.w; -import java.util.UUID; import mage.MageInt; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.condition.common.SourceOnBattlefieldControlUnchangedCondition; -import mage.abilities.decorator.ConditionalContinuousEffect; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.GainControlTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -16,12 +12,11 @@ import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.targetpointer.FixedTarget; -import mage.watchers.common.LostControlWatcher; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class Willbreaker extends CardImpl { @@ -34,11 +29,7 @@ public final class Willbreaker extends CardImpl { this.toughness = new MageInt(3); // Whenever a creature an opponent controls becomes the target of a spell or ability you control, gain control of that creature for as long as you control Willbreaker. - ConditionalContinuousEffect effect = new ConditionalContinuousEffect( - new GainControlTargetEffect(Duration.EndOfGame), - new SourceOnBattlefieldControlUnchangedCondition(), null); - effect.setText("gain control of that creature for as long as you control {this}"); - this.addAbility(new WillbreakerTriggeredAbility(effect), new LostControlWatcher()); + this.addAbility(new WillbreakerTriggeredAbility()); } private Willbreaker(final Willbreaker card) { @@ -53,11 +44,11 @@ public final class Willbreaker extends CardImpl { class WillbreakerTriggeredAbility extends TriggeredAbilityImpl { - public WillbreakerTriggeredAbility(Effect effect) { - super(Zone.BATTLEFIELD, effect); + WillbreakerTriggeredAbility() { + super(Zone.BATTLEFIELD, new GainControlTargetEffect(Duration.WhileControlled)); } - public WillbreakerTriggeredAbility(WillbreakerTriggeredAbility ability) { + private WillbreakerTriggeredAbility(final WillbreakerTriggeredAbility ability) { super(ability); } @@ -68,25 +59,26 @@ class WillbreakerTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - if (isControlledBy(event.getPlayerId())) { - Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null - && permanent.isCreature(game)) { - Player controller = game.getPlayer(getControllerId()); - if (controller != null - && controller.hasOpponent(permanent.getControllerId(), game)) { - // always call this method for FixedTargets in case it is blinked - getEffects().setTargetPointer(new FixedTarget(event.getTargetId(), game)); - return true; - } - } + if (!isControlledBy(event.getPlayerId())) { + return false; } - return false; + Permanent permanent = game.getPermanent(event.getTargetId()); + Permanent willbreaker = game.getPermanent(sourceId); + if (willbreaker == null // If you lose control of Willbreaker before its ability resolves, you won’t gain control of the creature at all. + || permanent == null + || !permanent.isCreature(game) + || !game.getOpponents(getControllerId()).contains(permanent.getControllerId())) { + return false; + } + // always call this method for FixedTargets in case it is blinked + this.getEffects().setTargetPointer(new FixedTarget(event.getTargetId(), game)); + return true; } @Override - public String getTriggerPhrase() { - return "Whenever a creature an opponent controls becomes the target of a spell or ability you control, "; + public String getRule() { + return "Whenever a creature an opponent controls becomes the target of a spell or ability you control, " + + "gain control of that creature for as long as you control {this}"; } @Override diff --git a/Mage.Sets/src/mage/cards/w/WillowSatyr.java b/Mage.Sets/src/mage/cards/w/WillowSatyr.java index 6a62d4d6e30..21cfb6aff04 100644 --- a/Mage.Sets/src/mage/cards/w/WillowSatyr.java +++ b/Mage.Sets/src/mage/cards/w/WillowSatyr.java @@ -1,12 +1,9 @@ package mage.cards.w; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SkipUntapOptionalAbility; -import mage.abilities.condition.CompoundCondition; -import mage.abilities.condition.common.SourceOnBattlefieldControlUnchangedCondition; import mage.abilities.condition.common.SourceTappedCondition; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.decorator.ConditionalContinuousEffect; @@ -17,13 +14,12 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; import mage.constants.SuperType; -import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.target.common.TargetCreaturePermanent; -import mage.watchers.common.LostControlWatcher; + +import java.util.UUID; /** - * * @author fireshoes */ public final class WillowSatyr extends CardImpl { @@ -42,13 +38,13 @@ public final class WillowSatyr extends CardImpl { // You may choose not to untap Willow Satyr during your untap step. this.addAbility(new SkipUntapOptionalAbility()); + // {tap}: Gain control of target legendary creature for as long as you control Willow Satyr and Willow Satyr remains tapped. - ConditionalContinuousEffect effect = new ConditionalContinuousEffect( - new GainControlTargetEffect(Duration.Custom), new CompoundCondition(SourceTappedCondition.instance, new SourceOnBattlefieldControlUnchangedCondition()), - "Gain control of target legendary creature for as long as you control {this} and {this} remains tapped"); - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new TapSourceCost()); + Ability ability = new SimpleActivatedAbility(new ConditionalContinuousEffect( + new GainControlTargetEffect(Duration.WhileControlled), SourceTappedCondition.TAPPED, + "Gain control of target legendary creature for as long as you control {this} and {this} remains tapped" + ), new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent(filter)); - ability.addWatcher(new LostControlWatcher()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/w/WindriderWizard.java b/Mage.Sets/src/mage/cards/w/WindriderWizard.java index aea24e0f759..ae53361c833 100644 --- a/Mage.Sets/src/mage/cards/w/WindriderWizard.java +++ b/Mage.Sets/src/mage/cards/w/WindriderWizard.java @@ -31,8 +31,11 @@ public final class WindriderWizard extends CardImpl { // Whenever you cast an instant, sorcery, or Wizard spell, you may draw a card. If you do, discard a card. this.addAbility(new SpellCastControllerTriggeredAbility( new DrawDiscardControllerEffect(1, 1, true), - StaticFilters.FILTER_SPELL_INSTANT_SORCERY_WIZARD, false - )); + StaticFilters.FILTER_SPELL_INSTANT_SORCERY_WIZARD, + false, + "Whenever you cast an instant, sorcery, or Wizard spell, you may draw a card. " + + "If you do, discard a card.") + ); } private WindriderWizard(final WindriderWizard card) { diff --git a/Mage.Sets/src/mage/cards/w/WingShredder.java b/Mage.Sets/src/mage/cards/w/WingShredder.java index e1946dd6e5b..daf016f1dd5 100644 --- a/Mage.Sets/src/mage/cards/w/WingShredder.java +++ b/Mage.Sets/src/mage/cards/w/WingShredder.java @@ -18,13 +18,11 @@ public final class WingShredder extends CardImpl { public WingShredder(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); - this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.WEREWOLF); this.power = new MageInt(3); this.toughness = new MageInt(5); this.color.setGreen(true); this.nightCard = true; - this.transformable = true; // Reach this.addAbility(ReachAbility.getInstance()); diff --git a/Mage.Sets/src/mage/cards/w/WingedPortent.java b/Mage.Sets/src/mage/cards/w/WingedPortent.java new file mode 100644 index 00000000000..b0e5d2c055b --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WingedPortent.java @@ -0,0 +1,58 @@ +package mage.cards.w; + +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.CleaveAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.mageobject.AbilityPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WingedPortent extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledCreaturePermanent(); + + static { + filter.add(new AbilityPredicate(FlyingAbility.class)); + } + + private static final DynamicValue xValue1 = new PermanentsOnBattlefieldCount(StaticFilters.FILTER_CONTROLLED_CREATURE); + private static final DynamicValue xValue2 = new PermanentsOnBattlefieldCount(filter); + private static final Hint hint1 = new ValueHint("Creatures you control", xValue1); + private static final Hint hint2 = new ValueHint("Creatures you control with flying", xValue2); + + public WingedPortent(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}{U}"); + + // Cleave {4}{G}{U} + this.addAbility(new CleaveAbility( + this, new DrawCardSourceControllerEffect(xValue1), "{4}{G}{U}" + ).addHint(hint1)); + + // Draw a card for each creature [with flying] you control. + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(xValue2) + .setText("draw a card for each creature [with flying] you control")); + this.getSpellAbility().addHint(hint2); + } + + private WingedPortent(final WingedPortent card) { + super(card); + } + + @Override + public WingedPortent copy() { + return new WingedPortent(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WingedSliver.java b/Mage.Sets/src/mage/cards/w/WingedSliver.java index b4d95a0adc5..8c6695dbf13 100644 --- a/Mage.Sets/src/mage/cards/w/WingedSliver.java +++ b/Mage.Sets/src/mage/cards/w/WingedSliver.java @@ -1,7 +1,5 @@ - package mage.cards.w; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.continuous.GainAbilityAllEffect; @@ -11,27 +9,25 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; + +import java.util.UUID; /** * @author Loki */ public final class WingedSliver extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("All sliver creatures"); - - static { - filter.add(SubType.SLIVER.getPredicate()); - } - public WingedSliver(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); this.subtype.add(SubType.SLIVER); this.power = new MageInt(1); this.toughness = new MageInt(1); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(FlyingAbility.getInstance(), Duration.WhileOnBattlefield, filter, false))); + this.addAbility(new SimpleStaticAbility(new GainAbilityAllEffect( + FlyingAbility.getInstance(), Duration.WhileOnBattlefield, + StaticFilters.FILTER_PERMANENT_ALL_SLIVERS, false + ))); } private WingedSliver(final WingedSliver card) { diff --git a/Mage.Sets/src/mage/cards/w/Wirecat.java b/Mage.Sets/src/mage/cards/w/Wirecat.java index c026b7ac14f..41e2d4317ea 100644 --- a/Mage.Sets/src/mage/cards/w/Wirecat.java +++ b/Mage.Sets/src/mage/cards/w/Wirecat.java @@ -69,7 +69,7 @@ public final class Wirecat extends CardImpl { @Override public boolean applies(Permanent permanent, Ability source, Game game) { if (permanent.getId().equals(source.getSourceId())) { - return game.getBattlefield().contains(StaticFilters.FILTER_ENCHANTMENT_PERMANENT, source, game, 1); + return game.getBattlefield().contains(StaticFilters.FILTER_PERMANENT_ENCHANTMENT, source, game, 1); } return false; } diff --git a/Mage.Sets/src/mage/cards/w/WispweaverAngel.java b/Mage.Sets/src/mage/cards/w/WispweaverAngel.java index cf28a17d89f..6ae0e572d8a 100644 --- a/Mage.Sets/src/mage/cards/w/WispweaverAngel.java +++ b/Mage.Sets/src/mage/cards/w/WispweaverAngel.java @@ -38,7 +38,7 @@ public final class WispweaverAngel extends CardImpl { // When Wispweaver Angel enters the battlefield, you may exile another target creature you control, then return that card to the battlefield under its owner's control. Ability ability = new EntersBattlefieldTriggeredAbility(new ExileTargetForSourceEffect(), true); - ability.addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false)); + ability.addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false).concatBy(",")); ability.addTarget(new TargetControlledCreaturePermanent(1, 1, filter, false)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/w/WitchHunter.java b/Mage.Sets/src/mage/cards/w/WitchHunter.java index 7dc9fd342fc..3bb5681a92d 100644 --- a/Mage.Sets/src/mage/cards/w/WitchHunter.java +++ b/Mage.Sets/src/mage/cards/w/WitchHunter.java @@ -13,9 +13,8 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; import mage.target.common.TargetPlayerOrPlaneswalker; @@ -25,12 +24,6 @@ import mage.target.common.TargetPlayerOrPlaneswalker; */ public final class WitchHunter extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public WitchHunter(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{W}"); this.subtype.add(SubType.HUMAN); @@ -46,7 +39,7 @@ public final class WitchHunter extends CardImpl { // {1}{W}{W}, {tap}: Return target creature an opponent controls to its owner's hand. Ability returnAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ReturnToHandTargetEffect(), new ManaCostsImpl("{1}{W}{W}")); returnAbility.addCost(new TapSourceCost()); - TargetCreaturePermanent target = new TargetCreaturePermanent(filter); + TargetCreaturePermanent target = new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE); returnAbility.addTarget(target); this.addAbility(returnAbility); } diff --git a/Mage.Sets/src/mage/cards/w/WitchesEye.java b/Mage.Sets/src/mage/cards/w/WitchesEye.java index 1b25f243f8c..edad0ea641a 100644 --- a/Mage.Sets/src/mage/cards/w/WitchesEye.java +++ b/Mage.Sets/src/mage/cards/w/WitchesEye.java @@ -30,7 +30,7 @@ public final class WitchesEye extends CardImpl { this.subtype.add(SubType.EQUIPMENT); // Equipped creature has "{1}, {T}: Scry 1." - Ability gainedAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ScryEffect(1), new GenericManaCost(1)); + Ability gainedAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ScryEffect(1, false), new GenericManaCost(1)); gainedAbility.addCost(new TapSourceCost()); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(gainedAbility, AttachmentType.EQUIPMENT, Duration.WhileOnBattlefield))); diff --git a/Mage.Sets/src/mage/cards/w/WitchsWeb.java b/Mage.Sets/src/mage/cards/w/WitchsWeb.java new file mode 100644 index 00000000000..04c4f319254 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WitchsWeb.java @@ -0,0 +1,42 @@ +package mage.cards.w; + +import mage.abilities.effects.common.UntapTargetEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.ReachAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WitchsWeb extends CardImpl { + + public WitchsWeb(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}"); + + // Target creature gets +3/+3 and gains reach until end of turn. Untap it. + this.getSpellAbility().addEffect(new BoostTargetEffect( + 3, 3, Duration.EndOfTurn + ).setText("Target creature gets +3/+3")); + this.getSpellAbility().addEffect(new GainAbilityTargetEffect( + ReachAbility.getInstance(), Duration.EndOfTurn + ).setText("and gains reach until end of turn")); + this.getSpellAbility().addEffect(new UntapTargetEffect().setText("Untap it")); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + private WitchsWeb(final WitchsWeb card) { + super(card); + } + + @Override + public WitchsWeb copy() { + return new WitchsWeb(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/Withdraw.java b/Mage.Sets/src/mage/cards/w/Withdraw.java index 322a228deab..1aede1c6f95 100644 --- a/Mage.Sets/src/mage/cards/w/Withdraw.java +++ b/Mage.Sets/src/mage/cards/w/Withdraw.java @@ -72,7 +72,7 @@ class WithdrawEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Effect effect = new ReturnToHandTargetEffect(); - effect.setTargetPointer(new FixedTarget(source.getFirstTarget())); + effect.setTargetPointer(new FixedTarget(source.getFirstTarget(), game)); effect.apply(game, source); Permanent secondCreature = game.getPermanent(source.getTargets().get(1).getFirstTarget()); if (secondCreature != null) { diff --git a/Mage.Sets/src/mage/cards/w/WithengarUnbound.java b/Mage.Sets/src/mage/cards/w/WithengarUnbound.java index e06bd24c180..523c7e10c9d 100644 --- a/Mage.Sets/src/mage/cards/w/WithengarUnbound.java +++ b/Mage.Sets/src/mage/cards/w/WithengarUnbound.java @@ -17,7 +17,6 @@ import mage.constants.Zone; import mage.counters.CounterType; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; /** * @@ -33,7 +32,6 @@ public final class WithengarUnbound extends CardImpl { // this card is the second face of double-faced card this.nightCard = true; - this.transformable = true; this.power = new MageInt(13); this.toughness = new MageInt(13); diff --git a/Mage.Sets/src/mage/cards/w/WitherbloomCampus.java b/Mage.Sets/src/mage/cards/w/WitherbloomCampus.java index 974751cc78d..bd573387cee 100644 --- a/Mage.Sets/src/mage/cards/w/WitherbloomCampus.java +++ b/Mage.Sets/src/mage/cards/w/WitherbloomCampus.java @@ -30,7 +30,7 @@ public final class WitherbloomCampus extends CardImpl { this.addAbility(new GreenManaAbility()); // {4}, {T}: Scry 1. - Ability ability = new SimpleActivatedAbility(new ScryEffect(1), new GenericManaCost(4)); + Ability ability = new SimpleActivatedAbility(new ScryEffect(1, false), new GenericManaCost(4)); ability.addCost(new TapSourceCost()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/w/WitherscaleWurm.java b/Mage.Sets/src/mage/cards/w/WitherscaleWurm.java index ca13c81fe63..2c5ab14081b 100644 --- a/Mage.Sets/src/mage/cards/w/WitherscaleWurm.java +++ b/Mage.Sets/src/mage/cards/w/WitherscaleWurm.java @@ -16,7 +16,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; import mage.counters.CounterType; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; /** * @@ -34,7 +34,7 @@ public final class WitherscaleWurm extends CardImpl { // Whenever Witherscale Wurm blocks or becomes blocked by a creature, that creature gains wither until end of turn. Effect effect = new GainAbilityTargetEffect(WitherAbility.getInstance(), Duration.EndOfTurn); effect.setText("that creature gains wither until end of turn"); - Ability ability = new BlocksOrBecomesBlockedSourceTriggeredAbility(effect, new FilterCreaturePermanent("a creature"), false, null, true); + Ability ability = new BlocksOrBecomesBlockedSourceTriggeredAbility(effect, StaticFilters.FILTER_PERMANENT_A_CREATURE, false, null, true); this.addAbility(ability); // Whenever Witherscale Wurm deals damage to an opponent, remove all -1/-1 counters from it. diff --git a/Mage.Sets/src/mage/cards/w/WitnessOfTomorrows.java b/Mage.Sets/src/mage/cards/w/WitnessOfTomorrows.java index c43caee131d..68c3bce0f15 100644 --- a/Mage.Sets/src/mage/cards/w/WitnessOfTomorrows.java +++ b/Mage.Sets/src/mage/cards/w/WitnessOfTomorrows.java @@ -28,7 +28,7 @@ public final class WitnessOfTomorrows extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // {3}{U}: Scry 1. - this.addAbility(new SimpleActivatedAbility(new ScryEffect(1), new ManaCostsImpl("{3}{U}"))); + this.addAbility(new SimpleActivatedAbility(new ScryEffect(1, false), new ManaCostsImpl("{3}{U}"))); } private WitnessOfTomorrows(final WitnessOfTomorrows card) { diff --git a/Mage.Sets/src/mage/cards/w/WitnessTheFuture.java b/Mage.Sets/src/mage/cards/w/WitnessTheFuture.java new file mode 100644 index 00000000000..c5d103a3730 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WitnessTheFuture.java @@ -0,0 +1,44 @@ +package mage.cards.w; + +import java.util.UUID; + +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.common.LookLibraryAndPickControllerEffect; +import mage.abilities.effects.common.TargetPlayerShufflesTargetCardsEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.target.TargetPlayer; +import mage.target.common.TargetCardInTargetPlayersGraveyard; + +/** + * + * @author weirddan455 + */ +public final class WitnessTheFuture extends CardImpl { + + public WitnessTheFuture(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{U}"); + + // Target player shuffles up to four target cards from their graveyard into their library. + // You look at the top four cards of your library, then put one of those cards into your hand and the rest on the bottom of your library in a random order. + this.getSpellAbility().addTarget(new TargetPlayer()); + this.getSpellAbility().addTarget(new TargetCardInTargetPlayersGraveyard(4)); + this.getSpellAbility().addEffect(new TargetPlayerShufflesTargetCardsEffect()); + this.getSpellAbility().addEffect(new LookLibraryAndPickControllerEffect( + StaticValue.get(4), false, StaticValue.get(1), StaticFilters.FILTER_CARD, + Zone.LIBRARY, false, false, false, Zone.HAND, false, false, false + ).setBackInRandomOrder(true)); + } + + private WitnessTheFuture(final WitnessTheFuture card) { + super(card); + } + + @Override + public WitnessTheFuture copy() { + return new WitnessTheFuture(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WizardsSpellbook.java b/Mage.Sets/src/mage/cards/w/WizardsSpellbook.java index 37f9375a765..2d38603e180 100644 --- a/Mage.Sets/src/mage/cards/w/WizardsSpellbook.java +++ b/Mage.Sets/src/mage/cards/w/WizardsSpellbook.java @@ -1,7 +1,6 @@ package mage.cards.w; import mage.ApprovingObject; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.SpellAbility; import mage.abilities.common.SimpleActivatedAbility; @@ -103,8 +102,7 @@ class WizardsSpellbookEffect extends OneShotEffect { return false; } UUID exileId = CardUtil.getExileZoneId(game, source); - MageObject sourceObject = source.getSourcePermanentOrLKI(game); - player.moveCardsToExile(card, source, game, true, exileId, sourceObject != null ? sourceObject.getName() : null); + player.moveCardsToExile(card, source, game, true, exileId, CardUtil.getSourceName(game, source)); if (level < 3) { Card copiedCard = game.copyCard(card, source, source.getControllerId()); if (!player.chooseUse( diff --git a/Mage.Sets/src/mage/cards/w/WoeStrider.java b/Mage.Sets/src/mage/cards/w/WoeStrider.java index 25828d07f55..5e22209de75 100644 --- a/Mage.Sets/src/mage/cards/w/WoeStrider.java +++ b/Mage.Sets/src/mage/cards/w/WoeStrider.java @@ -44,7 +44,7 @@ public final class WoeStrider extends CardImpl { // Sacrifice another creature: Scry 1. this.addAbility(new SimpleActivatedAbility( - new ScryEffect(1), new SacrificeTargetCost(new TargetControlledPermanent(filter)) + new ScryEffect(1, false), new SacrificeTargetCost(new TargetControlledPermanent(filter)) )); // Escape—{3}{B}{B}, Exile four other cards from your graveyard. diff --git a/Mage.Sets/src/mage/cards/w/WolfStrike.java b/Mage.Sets/src/mage/cards/w/WolfStrike.java new file mode 100644 index 00000000000..04d6a9891f5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WolfStrike.java @@ -0,0 +1,71 @@ +package mage.cards.w; + +import java.util.UUID; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DamageWithPowerFromOneToAnotherTargetEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author weirddan455 + */ +public final class WolfStrike extends CardImpl { + + public WolfStrike(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{G}"); + + // Target creature you control gets +2/+0 until end of turn if it's night. + // Then it deals damage equal to its power to target creature you don't control. + this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); + this.getSpellAbility().addEffect(new WolfStikeEffect()); + this.getSpellAbility().addEffect(new DamageWithPowerFromOneToAnotherTargetEffect("it").concatBy("Then")); + } + + private WolfStrike(final WolfStrike card) { + super(card); + } + + @Override + public WolfStrike copy() { + return new WolfStrike(this); + } +} + +class WolfStikeEffect extends OneShotEffect { + + public WolfStikeEffect() { + super(Outcome.BoostCreature); + staticText = "Target creature you control gets +2/+0 until end of turn if it's night"; + } + + private WolfStikeEffect(final WolfStikeEffect effect) { + super(effect); + } + + @Override + public WolfStikeEffect copy() { + return new WolfStikeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + if (game.checkDayNight(false)) { + BoostTargetEffect effect = new BoostTargetEffect(2, 0, Duration.EndOfTurn); + game.addEffect(effect, source); + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/w/WolfbittenCaptive.java b/Mage.Sets/src/mage/cards/w/WolfbittenCaptive.java index 692ca2c899b..947f1e86a90 100644 --- a/Mage.Sets/src/mage/cards/w/WolfbittenCaptive.java +++ b/Mage.Sets/src/mage/cards/w/WolfbittenCaptive.java @@ -28,7 +28,6 @@ public final class WolfbittenCaptive extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); - this.transformable = true; this.secondSideCardClazz = mage.cards.k.KrallenhordeKiller.class; // {1}{G}: Wolfbitten Captive gets +2/+2 until end of turn. Activate this ability only once each turn. diff --git a/Mage.Sets/src/mage/cards/w/WolfkinOutcast.java b/Mage.Sets/src/mage/cards/w/WolfkinOutcast.java new file mode 100644 index 00000000000..21595ce0f23 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WolfkinOutcast.java @@ -0,0 +1,67 @@ +package mage.cards.w; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.abilities.keyword.DayboundAbility; +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.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WolfkinOutcast extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent("you control a Wolf or Werewolf"); + + static { + filter.add(Predicates.or( + SubType.WOLF.getPredicate(), + SubType.WEREWOLF.getPredicate() + )); + } + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter); + private static final Hint hint = new ConditionHint( + condition, "You control a Wolf or Werewolf" + ); + + public WolfkinOutcast(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{G}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WEREWOLF); + this.power = new MageInt(5); + this.toughness = new MageInt(4); + this.secondSideCardClazz = mage.cards.w.WeddingCrasher.class; + + // This spell costs {2} less to cast if you control a Wolf or Werewolf. + this.addAbility(new SimpleStaticAbility( + Zone.ALL, new SpellCostReductionSourceEffect(2, condition).setCanWorksOnStackOnly(true) + ).setRuleAtTheTop(true)); + + // Daybound + this.addAbility(new DayboundAbility()); + } + + private WolfkinOutcast(final WolfkinOutcast card) { + super(card); + } + + @Override + public WolfkinOutcast copy() { + return new WolfkinOutcast(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WoodenStake.java b/Mage.Sets/src/mage/cards/w/WoodenStake.java index 19747ff4524..816d4c696ce 100644 --- a/Mage.Sets/src/mage/cards/w/WoodenStake.java +++ b/Mage.Sets/src/mage/cards/w/WoodenStake.java @@ -1,4 +1,3 @@ - package mage.cards.w; import java.util.UUID; @@ -27,7 +26,7 @@ import mage.target.targetpointer.FixedTarget; public final class WoodenStake extends CardImpl { public WoodenStake(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{2}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); this.subtype.add(SubType.EQUIPMENT); // Equip {1} @@ -73,7 +72,7 @@ class WoodenStakeBlocksOrBecomesBlockedTriggeredAbility extends TriggeredAbility Permanent blocks = game.getPermanent(event.getTargetId()); if (blocks != null && blocks.hasSubtype(SubType.VAMPIRE, game)) { for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getTargetId())); + effect.setTargetPointer(new FixedTarget(event.getTargetId(), game)); } return true; } @@ -83,7 +82,7 @@ class WoodenStakeBlocksOrBecomesBlockedTriggeredAbility extends TriggeredAbility Permanent blockedBy = game.getPermanent(event.getSourceId()); if (blockedBy != null && blockedBy.hasSubtype(SubType.VAMPIRE, game)) { for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getSourceId())); + effect.setTargetPointer(new FixedTarget(event.getSourceId(), game)); } return true; } diff --git a/Mage.Sets/src/mage/cards/w/WoodlurkerMimic.java b/Mage.Sets/src/mage/cards/w/WoodlurkerMimic.java index b8ad68085f4..93d15f85cbf 100644 --- a/Mage.Sets/src/mage/cards/w/WoodlurkerMimic.java +++ b/Mage.Sets/src/mage/cards/w/WoodlurkerMimic.java @@ -32,7 +32,7 @@ public final class WoodlurkerMimic extends CardImpl { filter.add(new ColorPredicate(ObjectColor.GREEN)); } - private String rule = "Whenever you cast a spell that's both black and green, {this} has base power and toughness 4/5 until end of turn and gains wither until end of turn."; + private static final String rule = "Whenever you cast a spell that's both black and green, {this} has base power and toughness 4/5 until end of turn and gains wither until end of turn."; public WoodlurkerMimic(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B/G}"); diff --git a/Mage.Sets/src/mage/cards/w/WormfangBehemoth.java b/Mage.Sets/src/mage/cards/w/WormfangBehemoth.java new file mode 100644 index 00000000000..a9df4888050 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WormfangBehemoth.java @@ -0,0 +1,82 @@ +package mage.cards.w; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.LeavesBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ReturnFromExileForSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WormfangBehemoth extends CardImpl { + + public WormfangBehemoth(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{U}"); + + this.subtype.add(SubType.NIGHTMARE); + this.subtype.add(SubType.FISH); + this.subtype.add(SubType.BEAST); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + + // When Wormfang Behemoth enters the battlefield, exile all cards from your hand. + this.addAbility(new EntersBattlefieldTriggeredAbility(new WormfangBehemothEffect())); + + // When Wormfang Behemoth leaves the battlefield, return the exiled cards to their owner's hand. + this.addAbility(new LeavesBattlefieldTriggeredAbility( + new ReturnFromExileForSourceEffect(Zone.HAND), false + )); + } + + private WormfangBehemoth(final WormfangBehemoth card) { + super(card); + } + + @Override + public WormfangBehemoth copy() { + return new WormfangBehemoth(this); + } +} + +class WormfangBehemothEffect extends OneShotEffect { + + WormfangBehemothEffect() { + super(Outcome.Benefit); + staticText = "exile all cards from your hand"; + } + + private WormfangBehemothEffect(final WormfangBehemothEffect effect) { + super(effect); + } + + @Override + public WormfangBehemothEffect copy() { + return new WormfangBehemothEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null || player.getHand().isEmpty()) { + return false; + } + return player.moveCardsToExile( + player.getHand().getCards(game), source, game, true, + CardUtil.getExileZoneId(game, source), + CardUtil.getSourceName(game, source) + ); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WortTheRaidmother.java b/Mage.Sets/src/mage/cards/w/WortTheRaidmother.java index f8bc5b1c0cd..89a60079743 100644 --- a/Mage.Sets/src/mage/cards/w/WortTheRaidmother.java +++ b/Mage.Sets/src/mage/cards/w/WortTheRaidmother.java @@ -35,10 +35,12 @@ public final class WortTheRaidmother extends CardImpl { this.toughness = new MageInt(3); // When Wort, the Raidmother enters the battlefield, create two 1/1 red and green Goblin Warrior creature tokens. - this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new GoblinWarriorToken(), 2), false)); + this.addAbility(new EntersBattlefieldTriggeredAbility( + new CreateTokenEffect(new GoblinWarriorToken(), 2) + )); // Each red or green instant or sorcery spell you cast has conspire. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new WortGainConspireEffect())); + this.addAbility(new SimpleStaticAbility(new WortGainConspireEffect())); } private WortTheRaidmother(final WortTheRaidmother card) { @@ -64,12 +66,12 @@ class WortGainConspireEffect extends ContinuousEffectImpl { public WortGainConspireEffect() { super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); staticText = "Each red or green instant or sorcery spell you cast has conspire. (As you cast the spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it and you may choose new targets for the copy.)"; - conspireAbility = new ConspireAbility(getId(), ConspireAbility.ConspireTargets.MORE); + this.conspireAbility = new ConspireAbility(ConspireAbility.ConspireTargets.MORE); } public WortGainConspireEffect(final WortGainConspireEffect effect) { super(effect); - this.conspireAbility = new ConspireAbility(getId(), ConspireAbility.ConspireTargets.MORE); + this.conspireAbility = effect.conspireAbility; } @Override @@ -81,11 +83,13 @@ class WortGainConspireEffect extends ContinuousEffectImpl { public boolean apply(Game game, Ability source) { for (StackObject stackObject : game.getStack()) { // only spells cast, so no copies of spells - if ((stackObject instanceof Spell) && !stackObject.isCopy() && stackObject.isControlledBy(source.getControllerId())) { - Spell spell = (Spell) stackObject; - if (filter.match(stackObject, game)) { - game.getState().addOtherAbility(spell.getCard(), conspireAbility); - } + if (!(stackObject instanceof Spell) || stackObject.isCopy() + || !stackObject.isControlledBy(source.getControllerId())) { + continue; + } + Spell spell = (Spell) stackObject; + if (filter.match(stackObject, game)) { + game.getState().addOtherAbility(spell.getCard(), conspireAbility.setAddedById(source.getSourceId())); } } return true; diff --git a/Mage.Sets/src/mage/cards/w/WrathfulJailbreaker.java b/Mage.Sets/src/mage/cards/w/WrathfulJailbreaker.java new file mode 100644 index 00000000000..3e0bf2c9f1b --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WrathfulJailbreaker.java @@ -0,0 +1,43 @@ +package mage.cards.w; + +import mage.MageInt; +import mage.abilities.common.AttacksEachCombatStaticAbility; +import mage.abilities.keyword.NightboundAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WrathfulJailbreaker extends CardImpl { + + public WrathfulJailbreaker(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); + + this.subtype.add(SubType.WEREWOLF); + this.power = new MageInt(6); + this.toughness = new MageInt(6); + this.color.setRed(true); + + this.nightCard = true; + + // Wrathful Jailbreaker attacks each combat if able. + this.addAbility(new AttacksEachCombatStaticAbility()); + + // Nightbound + this.addAbility(new NightboundAbility()); + } + + private WrathfulJailbreaker(final WrathfulJailbreaker card) { + super(card); + } + + @Override + public WrathfulJailbreaker copy() { + return new WrathfulJailbreaker(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WrennAndSeven.java b/Mage.Sets/src/mage/cards/w/WrennAndSeven.java index 6afb80df199..524b56c76a6 100644 --- a/Mage.Sets/src/mage/cards/w/WrennAndSeven.java +++ b/Mage.Sets/src/mage/cards/w/WrennAndSeven.java @@ -2,7 +2,6 @@ package mage.cards.w; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.GetEmblemEffect; @@ -14,7 +13,7 @@ import mage.constants.*; import mage.filter.StaticFilters; import mage.game.Game; import mage.game.command.emblems.WrennAndSevenEmblem; -import mage.game.permanent.token.WrennAndSevenToken; +import mage.game.permanent.token.WrennAndSevenTreefolkToken; import mage.players.Player; import mage.target.common.TargetCardInHand; @@ -30,7 +29,7 @@ public final class WrennAndSeven extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.WRENN); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + this.setStartingLoyalty(5); // +1: Reveal the top four cards of your library. Put all land cards revealed this way into your hand and the rest into your graveyard. this.addAbility(new LoyaltyAbility(new RevealLibraryPutIntoHandEffect( @@ -41,7 +40,7 @@ public final class WrennAndSeven extends CardImpl { this.addAbility(new LoyaltyAbility(new WrennAndSevenLandEffect(), 0)); // −3: Create a green Treefolk creature token with reach and "This creature's power and toughness are each equal to the number of lands you control." - this.addAbility(new LoyaltyAbility(new CreateTokenEffect(new WrennAndSevenToken()), -3)); + this.addAbility(new LoyaltyAbility(new CreateTokenEffect(new WrennAndSevenTreefolkToken()), -3)); // −8: Return all permanent cards from your graveyard to your hand. You get an emblem with "You have no maximum hand size." Ability ability = new LoyaltyAbility(new WrennAndSevenReturnEffect(), -8); diff --git a/Mage.Sets/src/mage/cards/w/WrennAndSix.java b/Mage.Sets/src/mage/cards/w/WrennAndSix.java index 8288e659d40..6a5a1cbadac 100644 --- a/Mage.Sets/src/mage/cards/w/WrennAndSix.java +++ b/Mage.Sets/src/mage/cards/w/WrennAndSix.java @@ -2,7 +2,6 @@ package mage.cards.w; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.GetEmblemEffect; import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; @@ -35,7 +34,7 @@ public final class WrennAndSix extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.WRENN); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +1: Return up to one target land card from your graveyard to your hand. Ability ability = new LoyaltyAbility(new ReturnFromGraveyardToHandTargetEffect(), 1); diff --git a/Mage.Sets/src/mage/cards/w/WretchedThrong.java b/Mage.Sets/src/mage/cards/w/WretchedThrong.java new file mode 100644 index 00000000000..d695e88c130 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WretchedThrong.java @@ -0,0 +1,49 @@ +package mage.cards.w; + +import mage.MageInt; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.NamePredicate; +import mage.target.common.TargetCardInLibrary; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WretchedThrong extends CardImpl { + + private static final FilterCard filter = new FilterCard("a card named Wretched Throng"); + + static { + filter.add(new NamePredicate("Wretched Throng")); + } + + public WretchedThrong(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); + + this.subtype.add(SubType.ZOMBIE); + this.subtype.add(SubType.HORROR); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // When Wretched Throng dies, you may search your library for a card named Wretched Throng, reveal it, put it into your hand, then shuffle. + this.addAbility(new DiesSourceTriggeredAbility(new SearchLibraryPutInHandEffect( + new TargetCardInLibrary(filter), true, true + ), true)); + } + + private WretchedThrong(final WretchedThrong card) { + super(card); + } + + @Override + public WretchedThrong copy() { + return new WretchedThrong(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WrexialTheRisenDeep.java b/Mage.Sets/src/mage/cards/w/WrexialTheRisenDeep.java index 2acdaa76228..94e598b7849 100644 --- a/Mage.Sets/src/mage/cards/w/WrexialTheRisenDeep.java +++ b/Mage.Sets/src/mage/cards/w/WrexialTheRisenDeep.java @@ -108,7 +108,7 @@ class WrexialTheRisenDeepTriggeredAbility extends TriggeredAbilityImpl { return "Whenever {this} deals combat damage to a player, " + "you may cast target instant or sorcery card " + "from that player's graveyard without paying its mana cost. " - + "If that spell would be put into a graveyard this turn, exile it instead"; + + "If that spell would be put into a graveyard this turn, exile it instead."; } } diff --git a/Mage.Sets/src/mage/cards/x/XandersLounge.java b/Mage.Sets/src/mage/cards/x/XandersLounge.java new file mode 100644 index 00000000000..76304ba21a8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/x/XandersLounge.java @@ -0,0 +1,48 @@ +package mage.cards.x; + +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.keyword.CyclingAbility; +import mage.abilities.mana.BlackManaAbility; +import mage.abilities.mana.BlueManaAbility; +import mage.abilities.mana.RedManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class XandersLounge extends CardImpl { + + public XandersLounge(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + this.subtype.add(SubType.ISLAND); + this.subtype.add(SubType.SWAMP); + this.subtype.add(SubType.MOUNTAIN); + + // ({T}: Add {U}, {B}, or {R}.) + this.addAbility(new BlueManaAbility()); + this.addAbility(new BlackManaAbility()); + this.addAbility(new RedManaAbility()); + + // Xander's Lounge enters the battlefield tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // Cycling {3} + this.addAbility(new CyclingAbility(new GenericManaCost(3))); + } + + private XandersLounge(final XandersLounge card) { + super(card); + } + + @Override + public XandersLounge copy() { + return new XandersLounge(this); + } +} diff --git a/Mage.Sets/src/mage/cards/x/XenagosTheReveler.java b/Mage.Sets/src/mage/cards/x/XenagosTheReveler.java index f78038fa4cf..05c76f1fd23 100644 --- a/Mage.Sets/src/mage/cards/x/XenagosTheReveler.java +++ b/Mage.Sets/src/mage/cards/x/XenagosTheReveler.java @@ -3,7 +3,6 @@ package mage.cards.x; import mage.Mana; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.hint.common.CreaturesYouControlHint; @@ -32,7 +31,7 @@ public final class XenagosTheReveler extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.XENAGOS); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +1: Add X mana in any combination of {R} and/or {G}, where X is the number of creatures you control. this.addAbility(new LoyaltyAbility(new XenagosManaEffect(), +1) diff --git a/Mage.Sets/src/mage/cards/y/YahenniUndyingPartisan.java b/Mage.Sets/src/mage/cards/y/YahenniUndyingPartisan.java index 11fb28c7604..d92b8d2815f 100644 --- a/Mage.Sets/src/mage/cards/y/YahenniUndyingPartisan.java +++ b/Mage.Sets/src/mage/cards/y/YahenniUndyingPartisan.java @@ -1,7 +1,5 @@ - package mage.cards.y; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.DiesCreatureTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; @@ -13,17 +11,16 @@ import mage.abilities.keyword.IndestructibleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; +import mage.constants.SubType; import mage.constants.SuperType; -import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.StaticFilters; -import mage.filter.common.FilterOpponentsCreaturePermanent; import mage.target.common.TargetControlledPermanent; +import java.util.UUID; + /** - * * @author fireshoes */ public final class YahenniUndyingPartisan extends CardImpl { @@ -41,14 +38,16 @@ public final class YahenniUndyingPartisan extends CardImpl { this.addAbility(HasteAbility.getInstance()); // Whenever a creature an opponent controls dies, put a +1/+1 counter on Yahenni, Undying Partisan. - this.addAbility(new DiesCreatureTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false, new FilterOpponentsCreaturePermanent())); + this.addAbility(new DiesCreatureTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), + false, StaticFilters.FILTER_OPPONENTS_PERMANENT_A_CREATURE + )); // Sacrifice another creature: Yahenni gains indestructible until end of turn. this.addAbility(new SimpleActivatedAbility( - Zone.BATTLEFIELD, new GainAbilitySourceEffect(IndestructibleAbility.getInstance(), Duration.EndOfTurn), - new SacrificeTargetCost(new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE))) - ); + new SacrificeTargetCost(new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE)) + )); } private YahenniUndyingPartisan(final YahenniUndyingPartisan card) { diff --git a/Mage.Sets/src/mage/cards/y/YennettCrypticSovereign.java b/Mage.Sets/src/mage/cards/y/YennettCrypticSovereign.java index c6e65bb05b1..0474434a546 100644 --- a/Mage.Sets/src/mage/cards/y/YennettCrypticSovereign.java +++ b/Mage.Sets/src/mage/cards/y/YennettCrypticSovereign.java @@ -41,7 +41,7 @@ public final class YennettCrypticSovereign extends CardImpl { this.addAbility(VigilanceAbility.getInstance()); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // Whenever Yennett, Cryptic Sovereign attacks, reveal the top card of your library. If that card's // converted mana cost is odd, you may cast it without paying its mana cost. Otherwise, draw a card. diff --git a/Mage.Sets/src/mage/cards/y/YixlidJailer.java b/Mage.Sets/src/mage/cards/y/YixlidJailer.java index 2f5bf3d3675..359ab2f0e51 100644 --- a/Mage.Sets/src/mage/cards/y/YixlidJailer.java +++ b/Mage.Sets/src/mage/cards/y/YixlidJailer.java @@ -4,12 +4,15 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.common.ZoneChangeTriggeredAbility; import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.game.Game; +import mage.game.events.GameEvent; import mage.players.Player; /** @@ -28,6 +31,7 @@ public final class YixlidJailer extends CardImpl { // Cards in graveyards lose all abilities. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new YixlidJailerEffect())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new YixlidJailerRulesEffect())); } private YixlidJailer(final YixlidJailer card) { @@ -38,54 +42,85 @@ public final class YixlidJailer extends CardImpl { public YixlidJailer copy() { return new YixlidJailer(this); } +} - static class YixlidJailerEffect extends ContinuousEffectImpl { +class YixlidJailerEffect extends ContinuousEffectImpl { - YixlidJailerEffect() { - super(Duration.WhileOnBattlefield, Outcome.LoseAbility); - staticText = "Cards in graveyards lose all abilities."; + public YixlidJailerEffect() { + super(Duration.WhileOnBattlefield, Outcome.LoseAbility); + staticText = "Cards in graveyards lose all abilities."; - this.dependencyTypes.add(DependencyType.AddingAbility); // Necrotic Ooze - } + this.dependencyTypes.add(DependencyType.AddingAbility); // Necrotic Ooze + } - YixlidJailerEffect(final YixlidJailerEffect effect) { - super(effect); - } + private YixlidJailerEffect(final YixlidJailerEffect effect) { + super(effect); + } - @Override - public YixlidJailerEffect copy() { - return new YixlidJailerEffect(this); - } + @Override + public YixlidJailerEffect copy() { + return new YixlidJailerEffect(this); + } - @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { - if (layer == Layer.AbilityAddingRemovingEffects_6) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - for (Card card : player.getGraveyard().getCards(game)) { - if (card != null) { - card.looseAllAbilities(game); - } + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + if (layer == Layer.AbilityAddingRemovingEffects_6) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { + Player player = game.getPlayer(playerId); + if (player != null) { + for (Card card : player.getGraveyard().getCards(game)) { + if (card != null) { + card.looseAllAbilities(game); } } } - return true; } + return true; } - return false; } + return false; + } - @Override - public boolean apply(Game game, Ability source) { - return false; - } + @Override + public boolean apply(Game game, Ability source) { + return false; + } - @Override - public boolean hasLayer(Layer layer) { - return layer == Layer.AbilityAddingRemovingEffects_6; - } + @Override + public boolean hasLayer(Layer layer) { + return layer == Layer.AbilityAddingRemovingEffects_6; + } +} + +class YixlidJailerRulesEffect extends ContinuousRuleModifyingEffectImpl { + + public YixlidJailerRulesEffect() { + super(Duration.WhileOnBattlefield, Outcome.Detriment, false, false); + } + + private YixlidJailerRulesEffect(final YixlidJailerRulesEffect effect) { + super(effect); + } + + @Override + public YixlidJailerRulesEffect copy() { + return new YixlidJailerRulesEffect(this); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + Object targetAbility = getValue("targetAbility"); + if (targetAbility instanceof ZoneChangeTriggeredAbility) { + ZoneChangeTriggeredAbility zoneAbility = (ZoneChangeTriggeredAbility) targetAbility; + return zoneAbility.getFromZone() != Zone.BATTLEFIELD && zoneAbility.getToZone() == Zone.GRAVEYARD; + } + return false; } } diff --git a/Mage.Sets/src/mage/cards/y/YodaJediMaster.java b/Mage.Sets/src/mage/cards/y/YodaJediMaster.java index f1d3a4b06da..9da18dcf9cc 100644 --- a/Mage.Sets/src/mage/cards/y/YodaJediMaster.java +++ b/Mage.Sets/src/mage/cards/y/YodaJediMaster.java @@ -4,7 +4,6 @@ package mage.cards.y; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.Effect; @@ -47,7 +46,7 @@ public final class YodaJediMaster extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.YODA); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3)); + this.setStartingLoyalty(3); // +1: Look at the top two cards of your library. Put one on the bottom of your library. Effect effect = new LookLibraryAndPickControllerEffect(StaticValue.get(2), false, StaticValue.get(1), diff --git a/Mage.Sets/src/mage/cards/y/YoshimaruEverFaithful.java b/Mage.Sets/src/mage/cards/y/YoshimaruEverFaithful.java new file mode 100644 index 00000000000..1e971fb7e9a --- /dev/null +++ b/Mage.Sets/src/mage/cards/y/YoshimaruEverFaithful.java @@ -0,0 +1,56 @@ +package mage.cards.y; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.PartnerAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.mageobject.AnotherPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class YoshimaruEverFaithful extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent("another legendary permanent"); + + static { + filter.add(AnotherPredicate.instance); + filter.add(SuperType.LEGENDARY.getPredicate()); + } + + public YoshimaruEverFaithful(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.DOG); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Whenever another legendary permanent enters the battlefield under your control, put a +1/+1 counter on Yoshimaru, Ever Faithful. + this.addAbility(new EntersBattlefieldControlledTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), filter + )); + + // Partner + this.addAbility(PartnerAbility.getInstance()); + } + + private YoshimaruEverFaithful(final YoshimaruEverFaithful card) { + super(card); + } + + @Override + public YoshimaruEverFaithful copy() { + return new YoshimaruEverFaithful(this); + } +} diff --git a/Mage.Sets/src/mage/cards/y/YouAreAlreadyDead.java b/Mage.Sets/src/mage/cards/y/YouAreAlreadyDead.java new file mode 100644 index 00000000000..450bd7676ee --- /dev/null +++ b/Mage.Sets/src/mage/cards/y/YouAreAlreadyDead.java @@ -0,0 +1,47 @@ +package mage.cards.y; + +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.WasDealtDamageThisTurnPredicate; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class YouAreAlreadyDead extends CardImpl { + + private static final FilterPermanent filter + = new FilterCreaturePermanent("creature that was dealt damage this turn"); + + static { + filter.add(WasDealtDamageThisTurnPredicate.instance); + } + + public YouAreAlreadyDead(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{B}"); + + // Destroy target creature that was dealt damage this turn. + this.getSpellAbility().addEffect(new DestroyTargetEffect()); + this.getSpellAbility().addTarget(new TargetPermanent(filter)); + + // Draw a card. + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("
")); + } + + private YouAreAlreadyDead(final YouAreAlreadyDead card) { + super(card); + } + + @Override + public YouAreAlreadyDead copy() { + return new YouAreAlreadyDead(this); + } +} +// NANI?? diff --git a/Mage.Sets/src/mage/cards/y/YuanShaosInfantry.java b/Mage.Sets/src/mage/cards/y/YuanShaosInfantry.java index c4499ecf532..5dd56dccef1 100644 --- a/Mage.Sets/src/mage/cards/y/YuanShaosInfantry.java +++ b/Mage.Sets/src/mage/cards/y/YuanShaosInfantry.java @@ -3,7 +3,7 @@ package mage.cards.y; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.AttacksAloneTriggeredAbility; +import mage.abilities.common.AttacksAloneSourceTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.combat.CantBeBlockedSourceEffect; import mage.cards.CardImpl; @@ -28,7 +28,7 @@ public final class YuanShaosInfantry extends CardImpl { // Whenever Yuan Shao's Infantry attacks alone, Yuan Shao's Infantry can't be blocked this combat. Effect effect = new CantBeBlockedSourceEffect(Duration.EndOfCombat); effect.setText("{this} can't be blocked this combat"); - this.addAbility(new AttacksAloneTriggeredAbility(effect)); + this.addAbility(new AttacksAloneSourceTriggeredAbility(effect)); } private YuanShaosInfantry(final YuanShaosInfantry card) { diff --git a/Mage.Sets/src/mage/cards/y/YukiOnna.java b/Mage.Sets/src/mage/cards/y/YukiOnna.java index 5f7e3a2b54a..cd765b28e73 100644 --- a/Mage.Sets/src/mage/cards/y/YukiOnna.java +++ b/Mage.Sets/src/mage/cards/y/YukiOnna.java @@ -32,7 +32,7 @@ public final class YukiOnna extends CardImpl { ability.addTarget(new TargetArtifactPermanent()); this.addAbility(ability); // Whenever you cast a Spirit or Arcane spell, you may return Yuki-Onna to its owner's hand. - this.addAbility(new SpellCastControllerTriggeredAbility(new ReturnToHandSourceEffect(true), StaticFilters.SPIRIT_OR_ARCANE_CARD, true)); + this.addAbility(new SpellCastControllerTriggeredAbility(new ReturnToHandSourceEffect(true), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true)); } private YukiOnna(final YukiOnna card) { diff --git a/Mage.Sets/src/mage/cards/z/ZagothTriome.java b/Mage.Sets/src/mage/cards/z/ZagothTriome.java index ba11f10e795..4c8d940d3a0 100644 --- a/Mage.Sets/src/mage/cards/z/ZagothTriome.java +++ b/Mage.Sets/src/mage/cards/z/ZagothTriome.java @@ -1,7 +1,7 @@ package mage.cards.z; import mage.abilities.common.EntersBattlefieldTappedAbility; -import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.keyword.CyclingAbility; import mage.abilities.mana.BlackManaAbility; import mage.abilities.mana.BlueManaAbility; @@ -34,7 +34,7 @@ public final class ZagothTriome extends CardImpl { this.addAbility(new EntersBattlefieldTappedAbility()); // Cycling {3} - this.addAbility(new CyclingAbility(new ManaCostsImpl("{3}"))); + this.addAbility(new CyclingAbility(new GenericManaCost(3))); } private ZagothTriome(final ZagothTriome card) { diff --git a/Mage.Sets/src/mage/cards/z/ZamWesell.java b/Mage.Sets/src/mage/cards/z/ZamWesell.java index 9ef1af0e350..1457b56d6a1 100644 --- a/Mage.Sets/src/mage/cards/z/ZamWesell.java +++ b/Mage.Sets/src/mage/cards/z/ZamWesell.java @@ -30,7 +30,8 @@ public final class ZamWesell extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); - // When you cast Zam Wessel, target opponent reveals their hand. You may choose a creature card from it and have Zam Wessel enter the battlefield as a copy of that creature card. + // When you cast Zam Wesell, target opponent reveals their hand. You may choose a creature card from it and have Zam Wesell enter the battlefield as a copy of that creature card. + // TODO: Zam Wesell must be reworked to use on cast + etb abilities Ability ability = new CastSourceTriggeredAbility(new RevealHandTargetEffect()); ability.addEffect(new ZamWesselEffect()); ability.addTarget(new TargetOpponent()); diff --git a/Mage.Sets/src/mage/cards/z/ZangiefTheRedCyclone.java b/Mage.Sets/src/mage/cards/z/ZangiefTheRedCyclone.java new file mode 100644 index 00000000000..de5330f1ead --- /dev/null +++ b/Mage.Sets/src/mage/cards/z/ZangiefTheRedCyclone.java @@ -0,0 +1,143 @@ +package mage.cards.z; + +import mage.MageInt; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.common.DealsDamageToACreatureTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.MyTurnCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.SacrificeEffect; +import mage.abilities.effects.common.combat.MustBeBlockedByAtLeastOneSourceEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.keyword.IndestructibleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.common.FilterNonlandPermanent; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.game.events.DamagedEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.targetpointer.FixedTarget; +import mage.watchers.Watcher; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ZangiefTheRedCyclone extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature"); + + static { + filter.add(ZangiefTheRedCycloneWatcher::checkPermanent); + } + + public ZangiefTheRedCyclone(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{R}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(7); + this.toughness = new MageInt(4); + + // Zangief, the Red Cyclone must be blocked if able. + this.addAbility(new SimpleStaticAbility(new MustBeBlockedByAtLeastOneSourceEffect(Duration.WhileOnBattlefield))); + + // Iron Muscle—As long as it's your turn, Zangief has indestructible. + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new GainAbilitySourceEffect(IndestructibleAbility.getInstance()), + MyTurnCondition.instance, "as long as it's your turn, {this} has indestructible" + )).withFlavorWord("Iron Muscle")); + + // Spinning Piledriver—Whenever Zangief deals damage to a creature, if that creature was dealt excess damage this turn, that creature's controller sacrifices a noncreature, nonland permanent. + this.addAbility(new DealsDamageToACreatureTriggeredAbility( + new ZangiefTheRedCycloneEffect(), false, false, true, filter + ).withFlavorWord("Spinning Piledriver"), new ZangiefTheRedCycloneWatcher()); + } + + private ZangiefTheRedCyclone(final ZangiefTheRedCyclone card) { + super(card); + } + + @Override + public ZangiefTheRedCyclone copy() { + return new ZangiefTheRedCyclone(this); + } +} + +class ZangiefTheRedCycloneEffect extends OneShotEffect { + + private static final FilterPermanent filter = new FilterNonlandPermanent("noncreature, nonland permanent"); + + static { + filter.add(Predicates.not(CardType.CREATURE.getPredicate())); + } + + ZangiefTheRedCycloneEffect() { + super(Outcome.Benefit); + staticText = "if that creature was dealt excess damage this turn, " + + "that creature's controller sacrifices a noncreature, nonland permanent"; + } + + private ZangiefTheRedCycloneEffect(final ZangiefTheRedCycloneEffect effect) { + super(effect); + } + + @Override + public ZangiefTheRedCycloneEffect copy() { + return new ZangiefTheRedCycloneEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); + if (permanent == null) { + return false; + } + return new SacrificeEffect(filter, 1, "") + .setTargetPointer(new FixedTarget(permanent.getControllerId(), game)) + .apply(game, source); + } +} + +class ZangiefTheRedCycloneWatcher extends Watcher { + + private final Set morSet = new HashSet<>(); + + ZangiefTheRedCycloneWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.DAMAGED_PERMANENT + && ((DamagedEvent) event).getExcess() >= 1) { + morSet.add(new MageObjectReference(event.getTargetId(), game)); + } + } + + @Override + public void reset() { + super.reset(); + morSet.clear(); + } + + static boolean checkPermanent(Permanent input, Game game) { + return game + .getState() + .getWatcher(ZangiefTheRedCycloneWatcher.class) + .morSet + .stream() + .anyMatch(mor -> mor.refersTo(input, game)); + } +} diff --git a/Mage.Sets/src/mage/cards/z/ZarielArchdukeOfAvernus.java b/Mage.Sets/src/mage/cards/z/ZarielArchdukeOfAvernus.java index 8a36922cf41..4e78a8c2156 100644 --- a/Mage.Sets/src/mage/cards/z/ZarielArchdukeOfAvernus.java +++ b/Mage.Sets/src/mage/cards/z/ZarielArchdukeOfAvernus.java @@ -2,7 +2,6 @@ package mage.cards.z; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.GetEmblemEffect; import mage.abilities.effects.common.continuous.BoostControlledEffect; @@ -30,7 +29,7 @@ public final class ZarielArchdukeOfAvernus extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.ZARIEL); - this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + this.setStartingLoyalty(4); // +1: Creatures you control get +1/+0 and gain haste until end of turn. Ability ability = new LoyaltyAbility(new BoostControlledEffect( diff --git a/Mage.Sets/src/mage/cards/z/ZeganaUtopianSpeaker.java b/Mage.Sets/src/mage/cards/z/ZeganaUtopianSpeaker.java index 3056f65c7a6..9909678c8c6 100644 --- a/Mage.Sets/src/mage/cards/z/ZeganaUtopianSpeaker.java +++ b/Mage.Sets/src/mage/cards/z/ZeganaUtopianSpeaker.java @@ -12,10 +12,7 @@ import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.counters.CounterType; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.filter.StaticFilters; import java.util.UUID; @@ -24,15 +21,6 @@ import java.util.UUID; */ public final class ZeganaUtopianSpeaker extends CardImpl { - private static final FilterPermanent filter = new FilterControlledCreaturePermanent(); - private static final FilterPermanent filter2 = new FilterControlledCreaturePermanent(); - - static { - filter.add(CounterType.P1P1.getPredicate()); - filter.add(AnotherPredicate.instance); - filter2.add(CounterType.P1P1.getPredicate()); - } - public ZeganaUtopianSpeaker(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{U}"); @@ -46,11 +34,12 @@ public final class ZeganaUtopianSpeaker extends CardImpl { this.addAbility(new ConditionalInterveningIfTriggeredAbility( new EntersBattlefieldTriggeredAbility( new DrawCardSourceControllerEffect(1), false - ), new PermanentsOnTheBattlefieldCondition(filter), + ), new PermanentsOnTheBattlefieldCondition(StaticFilters.FILTER_OTHER_CONTROLLED_CREATURE_P1P1), "When {this} enters the battlefield, " + "if you control another creature " + "with a +1/+1 counter on it, draw a card." - )); + ) + ); // {4}{G}{U}: Adapt 4. this.addAbility(new AdaptAbility(4, "{4}{G}{U}")); @@ -60,9 +49,9 @@ public final class ZeganaUtopianSpeaker extends CardImpl { Zone.BATTLEFIELD, new GainAbilityAllEffect( TrampleAbility.getInstance(), Duration.WhileOnBattlefield, - filter2, "Each creature you control with a +1/+1 counter on it has trample" + StaticFilters.FILTER_EACH_CONTROLLED_CREATURE_P1P1) ) - )); + ); } private ZeganaUtopianSpeaker(final ZeganaUtopianSpeaker card) { diff --git a/Mage.Sets/src/mage/cards/z/ZelyonSword.java b/Mage.Sets/src/mage/cards/z/ZelyonSword.java index cd0d2976ca5..a384b649601 100644 --- a/Mage.Sets/src/mage/cards/z/ZelyonSword.java +++ b/Mage.Sets/src/mage/cards/z/ZelyonSword.java @@ -30,7 +30,7 @@ public final class ZelyonSword extends CardImpl { this.addAbility(new SkipUntapOptionalAbility()); // {3}, {tap}: Target creature gets +2/+0 for as long as Zelyon Sword remains tapped. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect( - new BoostTargetEffect(2, 0, Duration.Custom), SourceTappedCondition.instance, + new BoostTargetEffect(2, 0, Duration.Custom), SourceTappedCondition.TAPPED, "target creature gets +2/+0 for as long as {this} remains tapped"), new ManaCostsImpl("{3}")); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent()); diff --git a/Mage.Sets/src/mage/cards/z/ZiatorasProvingGround.java b/Mage.Sets/src/mage/cards/z/ZiatorasProvingGround.java new file mode 100644 index 00000000000..f8700a160c5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/z/ZiatorasProvingGround.java @@ -0,0 +1,48 @@ +package mage.cards.z; + +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.keyword.CyclingAbility; +import mage.abilities.mana.BlackManaAbility; +import mage.abilities.mana.GreenManaAbility; +import mage.abilities.mana.RedManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ZiatorasProvingGround extends CardImpl { + + public ZiatorasProvingGround(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + this.subtype.add(SubType.SWAMP); + this.subtype.add(SubType.MOUNTAIN); + this.subtype.add(SubType.FOREST); + + // ({T}: Add {B}, {R}, or {G}.) + this.addAbility(new BlackManaAbility()); + this.addAbility(new RedManaAbility()); + this.addAbility(new GreenManaAbility()); + + // Ziatora's Proving Ground enters the battlefield tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // Cycling {3} + this.addAbility(new CyclingAbility(new GenericManaCost(3))); + } + + private ZiatorasProvingGround(final ZiatorasProvingGround card) { + super(card); + } + + @Override + public ZiatorasProvingGround copy() { + return new ZiatorasProvingGround(this); + } +} diff --git a/Mage.Sets/src/mage/cards/z/ZombieAssassin.java b/Mage.Sets/src/mage/cards/z/ZombieAssassin.java index 11c080bf0da..90b85873a7b 100644 --- a/Mage.Sets/src/mage/cards/z/ZombieAssassin.java +++ b/Mage.Sets/src/mage/cards/z/ZombieAssassin.java @@ -1,9 +1,7 @@ - package mage.cards.z; import java.util.UUID; import mage.MageInt; -import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.ExileFromGraveCost; @@ -15,10 +13,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.FilterCard; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ColorPredicate; +import mage.filter.StaticFilters; import mage.target.Target; import mage.target.common.TargetCardInYourGraveyard; import mage.target.common.TargetCreaturePermanent; @@ -28,13 +23,6 @@ import mage.target.common.TargetCreaturePermanent; * @author cbt33 */ public final class ZombieAssassin extends CardImpl { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); - static { - filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); - } - - public ZombieAssassin(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{B}"); @@ -45,9 +33,9 @@ public final class ZombieAssassin extends CardImpl { // {tap}, Exile two cards from your graveyard and Zombie Assassin: Destroy target nonblack creature. It can't be regenerated. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(true), new TapSourceCost()); - Target target = new TargetCreaturePermanent(filter); + Target target = new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK); ability.addTarget(target); - ability.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard(2,2,new FilterCard("cards from your graveyard")))); + ability.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard(2,2,StaticFilters.FILTER_CARDS_FROM_YOUR_GRAVEYARD))); ability.addCost(new ExileSourceCost()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/z/ZombieBoa.java b/Mage.Sets/src/mage/cards/z/ZombieBoa.java new file mode 100644 index 00000000000..c5843986d40 --- /dev/null +++ b/Mage.Sets/src/mage/cards/z/ZombieBoa.java @@ -0,0 +1,128 @@ +package mage.cards.z; + +import mage.MageInt; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.choices.ChoiceColor; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ZombieBoa extends CardImpl { + + public ZombieBoa(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}"); + + this.subtype.add(SubType.ZOMBIE); + this.subtype.add(SubType.SNAKE); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // {1}{B}: Choose a color. Whenever Zombie Boa becomes blocked by a creature of that color this turn, destroy that creature. Activate this ability only any time you could cast a sorcery. + this.addAbility(new ActivateAsSorceryActivatedAbility(new ZombieBoaEffect(), new ManaCostsImpl<>("{1}{B}"))); + } + + private ZombieBoa(final ZombieBoa card) { + super(card); + } + + @Override + public ZombieBoa copy() { + return new ZombieBoa(this); + } +} + +class ZombieBoaEffect extends OneShotEffect { + + ZombieBoaEffect() { + super(Outcome.Benefit); + staticText = "choose a color. Whenever {this} becomes blocked by " + + "a creature of that color this turn, destroy that creature"; + } + + private ZombieBoaEffect(final ZombieBoaEffect effect) { + super(effect); + } + + @Override + public ZombieBoaEffect copy() { + return new ZombieBoaEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + ChoiceColor choice = new ChoiceColor(); + if (player.choose(outcome, choice, game)) { + ObjectColor color = choice.getColor(); + game.informPlayers(player.getLogName() + " chooses " + color); + game.addDelayedTriggeredAbility(new ZombieBoaTriggeredAbility(color), source); + return true; + } + return false; + } +} + +class ZombieBoaTriggeredAbility extends DelayedTriggeredAbility { + + private final ObjectColor color; + + ZombieBoaTriggeredAbility(ObjectColor color) { + super(new DestroyTargetEffect(), Duration.EndOfTurn, false, false); + this.color = color; + } + + private ZombieBoaTriggeredAbility(final ZombieBoaTriggeredAbility ability) { + super(ability); + this.color = ability.color; + } + + @Override + public ZombieBoaTriggeredAbility copy() { + return new ZombieBoaTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.BLOCKER_DECLARED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (!event.getTargetId().equals(this.getSourceId())) { + return false; + } + Permanent permanent = game.getPermanent(event.getSourceId()); + if (permanent == null || !permanent.isCreature(game) || !permanent.getColor(game).contains(color)) { + return false; + } + this.getEffects().setTargetPointer(new FixedTarget(event.getSourceId(), game)); + return true; + } + + @Override + public String getRule() { + return "Whenever {this} becomes blocked by a creature of that color this turn, destroy that creature"; + } +} diff --git a/Mage.Sets/src/mage/sets/AdventuresInTheForgottenRealms.java b/Mage.Sets/src/mage/sets/AdventuresInTheForgottenRealms.java index 7eafe71f6ba..35848fd8e56 100644 --- a/Mage.Sets/src/mage/sets/AdventuresInTheForgottenRealms.java +++ b/Mage.Sets/src/mage/sets/AdventuresInTheForgottenRealms.java @@ -31,7 +31,7 @@ public final class AdventuresInTheForgottenRealms extends ExpansionSet { this.numBoosterCommon = 10; this.numBoosterUncommon = 3; this.numBoosterRare = 1; - this.maxCardNumberInBooster = 275; + this.maxCardNumberInBooster = 281; cards.add(new SetCardInfo("+2 Mace", 1, Rarity.COMMON, mage.cards.p.Plus2Mace.class)); cards.add(new SetCardInfo("Aberrant Mind Sorcerer", 44, Rarity.UNCOMMON, mage.cards.a.AberrantMindSorcerer.class)); diff --git a/Mage.Sets/src/mage/sets/AetherRevolt.java b/Mage.Sets/src/mage/sets/AetherRevolt.java index ae1c213c453..d09fcd6d805 100644 --- a/Mage.Sets/src/mage/sets/AetherRevolt.java +++ b/Mage.Sets/src/mage/sets/AetherRevolt.java @@ -4,6 +4,10 @@ import mage.cards.ExpansionSet; import mage.cards.repository.CardCriteria; import mage.cards.repository.CardInfo; import mage.cards.repository.CardRepository; +import mage.collation.BoosterCollator; +import mage.collation.BoosterStructure; +import mage.collation.CardRun; +import mage.collation.RarityConfiguration; import mage.constants.Rarity; import mage.constants.SetType; @@ -21,8 +25,6 @@ public final class AetherRevolt extends ExpansionSet { return instance; } - private final List savedSpecialLand = new ArrayList<>(); - private AetherRevolt() { super("Aether Revolt", "AER", ExpansionSet.buildDate(2017, 1, 20), SetType.EXPANSION); this.blockName = "Kaladesh"; @@ -34,8 +36,8 @@ public final class AetherRevolt extends ExpansionSet { this.numBoosterUncommon = 3; this.numBoosterRare = 1; this.ratioBoosterMythic = 8; + this.ratioBoosterSpecialCommon = 144; this.maxCardNumberInBooster = 184; - this.ratioBoosterSpecialLand = 144; cards.add(new SetCardInfo("Aegis Automaton", 141, Rarity.COMMON, mage.cards.a.AegisAutomaton.class)); cards.add(new SetCardInfo("Aerial Modification", 1, Rarity.UNCOMMON, mage.cards.a.AerialModification.class)); @@ -237,15 +239,128 @@ public final class AetherRevolt extends ExpansionSet { } @Override - public List getSpecialLand() { - if (savedSpecialLand.isEmpty()) { - CardCriteria criteria = new CardCriteria(); - criteria.setCodes("MPS"); - criteria.minCardNumber(31); - criteria.maxCardNumber(54); - savedSpecialLand.addAll(CardRepository.instance.findCards(criteria)); + protected List findCardsByRarity(Rarity rarity) { + List cardInfos = super.findCardsByRarity(rarity); + if (rarity == Rarity.SPECIAL) { + cardInfos.addAll(CardRepository.instance.findCards(new CardCriteria().setCodes("MPS").minCardNumber(31))); } + return cardInfos; + } - return new ArrayList<>(savedSpecialLand); + @Override + protected void generateBoosterMap() { + super.generateBoosterMap(); + CardRepository + .instance + .findCards(new CardCriteria().setCodes("MPS").minCardNumber(31)) + .stream() + .forEach(cardInfo -> inBoosterMap.put("MPS_" + cardInfo.getCardNumber(), cardInfo)); + } + + @Override + public BoosterCollator createCollator() { + return new AetherRevoltCollator(); + } +} + +// Booster collation info from https://www.lethe.xyz/mtg/collation/aer.html +// Using USA collation for all rarities +// Foil rare sheet used for regular rares as regular rare sheet is not known +class AetherRevoltCollator implements BoosterCollator { + private final CardRun commonA = new CardRun(true, "10", "92", "26", "6", "77", "39", "14", "79", "37", "16", "82", "32", "7", "98", "47", "20", "101", "8", "12", "76", "45", "10", "92", "34", "6", "77", "39", "7", "82", "32", "14", "79", "26", "16", "76", "37", "12", "98", "47", "20", "101", "34", "8", "77", "45", "7", "82", "39", "10", "92", "26", "6", "76", "37", "16", "79", "32", "14", "101", "34", "12", "20", "47", "8", "98", "45"); + private final CardRun commonB = new CardRun(true, "54", "111", "70", "106", "52", "126", "67", "102", "58", "125", "60", "120", "56", "124", "55", "118", "70", "113", "69", "111", "54", "126", "52", "106", "60", "102", "67", "125", "56", "120", "69", "113", "58", "118", "70", "106", "52", "124", "55", "120", "56", "111", "54", "126", "60", "113", "67", "102", "69", "125", "55", "118", "58", "124"); + private final CardRun commonC1 = new CardRun(true, "165", "147", "166", "51", "158", "103", "172", "30", "173", "183", "89", "161", "178", "166", "78", "151", "40", "172", "100", "173", "158", "51", "178", "30", "165", "89", "161", "40", "147", "103", "183", "151", "51", "166", "172", "165", "100", "173", "30", "178", "89", "158", "183", "103", "161", "147", "166", "40", "173", "78", "172", "51", "151", "158", "100", "147", "30", "183", "89", "165", "103", "178", "100", "161", "151", "40"); + private final CardRun commonC2 = new CardRun(true, "155", "180", "143", "3", "156", "141", "157", "13", "159", "174", "78", "150", "143", "155", "35", "180", "182", "141", "13", "157", "156", "3", "159", "174", "150", "35", "155", "141", "180", "13", "182", "143", "3", "157", "156", "174", "78", "150", "159", "141", "13", "155", "180", "35", "182", "143", "156", "3", "157", "174", "159", "150", "182", "35"); + private final CardRun uncommonA = new CardRun(true, "110", "59", "133", "86", "44", "21", "177", "135", "114", "140", "94", "48", "17", "132", "65", "149", "139", "83", "46", "2", "167", "61", "117", "133", "97", "43", "21", "145", "63", "115", "135", "86", "179", "15", "167", "59", "110", "140", "94", "44", "2", "177", "61", "115", "133", "97", "48", "17", "145", "65", "110", "132", "86", "43", "15", "149", "59", "114", "139", "83", "46", "2", "179", "61", "115", "135", "97", "48", "21", "177", "63", "117", "140", "94", "44", "149", "65", "114", "132", "83", "46", "17", "167", "59", "110", "133", "94", "44", "21", "145", "63", "117", "139", "177", "86", "43", "15", "179", "65", "115", "135", "83", "48", "17", "140", "149", "63", "114", "132", "97", "46", "2", "167", "61", "117", "139", "145", "43", "15", "179"); + private final CardRun uncommonB = new CardRun(true, "119", "130", "84", "36", "5", "148", "68", "116", "129", "80", "146", "1", "168", "72", "121", "138", "95", "42", "19", "176", "73", "112", "136", "99", "50", "144", "25", "72", "116", "134", "84", "33", "5", "148", "57", "119", "130", "95", "36", "25", "146", "68", "121", "129", "80", "42", "176", "5", "68", "116", "138", "99", "36", "144", "73", "136", "112", "84", "50", "1", "148", "57", "119", "130", "80", "42", "19", "168", "72", "121", "129", "146", "95", "33", "25", "176", "73", "112", "136", "99", "50", "1", "130", "144", "57", "119", "134", "84", "36", "19", "168", "68", "116", "129", "148", "33", "5", "176", "73", "112", "136", "95", "42", "1", "146", "138", "121", "134", "80", "33", "25", "138", "72", "168", "134", "99", "50", "19", "144", "57"); + private final CardRun rare = new CardRun(true, "93", "23", "184", "104", "41", "123", "53", "22", "9", "170", "62", "96", "4", "160", "18", "122", "128", "49", "24", "11", "171", "66", "105", "109", "162", "163", "169", "131", "175", "27", "28", "29", "87", "107", "9", "153", "164", "91", "142", "181", "64", "123", "31", "93", "108", "11", "24", "137", "62", "154", "22", "75", "128", "71", "96", "28", "152", "27", "29", "66", "160", "74", "4", "131", "163", "105", "175", "87", "81", "31", "90", "162", "88", "23", "142", "164", "107", "181", "184", "38", "41", "170", "49", "122", "85", "154", "109", "108", "91", "88", "127", "81", "53", "71", "75", "74"); + private final CardRun masterpiece = new CardRun(false, "MPS_31", "MPS_32", "MPS_33", "MPS_34", "MPS_35", "MPS_36", "MPS_37", "MPS_38", "MPS_39", "MPS_40", "MPS_41", "MPS_42", "MPS_43", "MPS_44", "MPS_45", "MPS_46", "MPS_47", "MPS_48", "MPS_49", "MPS_50", "MPS_51", "MPS_52", "MPS_53", "MPS_54"); + private final CardRun land = new CardRun(false, "KLD_250", "KLD_251", "KLD_252", "KLD_253", "KLD_254", "KLD_255", "KLD_256", "KLD_257", "KLD_258", "KLD_259", "KLD_260", "KLD_261", "KLD_262", "KLD_263", "KLD_264"); + + private final BoosterStructure AAABBC1C1C1C1C1 = new BoosterStructure( + commonA, commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AAABBBC1C1C1C1 = new BoosterStructure( + commonA, commonA, commonA, + commonB, commonB, commonB, + commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AAABBC1C1C1C1M = new BoosterStructure( + commonA, commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1, + masterpiece + ); + private final BoosterStructure AAABBBC2C2C2C2 = new BoosterStructure( + commonA, commonA, commonA, + commonB, commonB, commonB, + commonC2, commonC2, commonC2, commonC2 + ); + private final BoosterStructure AAAABBC2C2C2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, + commonC2, commonC2, commonC2, commonC2 + ); + private final BoosterStructure AAB = new BoosterStructure(uncommonA, uncommonA, uncommonB); + private final BoosterStructure ABB = new BoosterStructure(uncommonA, uncommonB, uncommonB); + private final BoosterStructure R1 = new BoosterStructure(rare); + private final BoosterStructure L1 = new BoosterStructure(land); + + // In order for equal numbers of each common to exist, the average booster must contain: + // 3.14 A commons (44 / 14) (rounded to 455/145) + // 2.57 B commons (36 / 14) (rounded to 373/145) + // 2.36 C1 commons (33 / 14) (rounded to 341/145) + // 1.93 C2 commons (27 / 14) (rounded to 280/145) + // These numbers are the same for all sets with 70 commons in A/B/C1/C2 print runs + // and with 10 common slots per booster + private final RarityConfiguration commonRuns = new RarityConfiguration( + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + + AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, + AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, + AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, + AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, + AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, + AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, + AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBC1C1C1C1M, + + AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, + AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, + AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, + AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, + AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, + AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, + AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, + AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, + AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, + AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, + + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2 + ); + private final RarityConfiguration uncommonRuns = new RarityConfiguration(AAB, ABB); + private final RarityConfiguration rareRuns = new RarityConfiguration(R1); + private final RarityConfiguration landRuns = new RarityConfiguration(L1); + + @Override + public List makeBooster() { + List booster = new ArrayList<>(); + booster.addAll(commonRuns.getNext().makeRun()); + booster.addAll(uncommonRuns.getNext().makeRun()); + booster.addAll(rareRuns.getNext().makeRun()); + booster.addAll(landRuns.getNext().makeRun()); + return booster; } } diff --git a/Mage.Sets/src/mage/sets/AlaraReborn.java b/Mage.Sets/src/mage/sets/AlaraReborn.java index 5e8c9b528b8..c4fcf0254f4 100644 --- a/Mage.Sets/src/mage/sets/AlaraReborn.java +++ b/Mage.Sets/src/mage/sets/AlaraReborn.java @@ -28,6 +28,7 @@ public final class AlaraReborn extends ExpansionSet { this.numBoosterUncommon = 3; this.numBoosterRare = 1; this.ratioBoosterMythic = 8; + this.hasOnlyMulticolorCards = true; cards.add(new SetCardInfo("Anathemancer", 33, Rarity.UNCOMMON, mage.cards.a.Anathemancer.class)); cards.add(new SetCardInfo("Architects of Will", 17, Rarity.COMMON, mage.cards.a.ArchitectsOfWill.class)); cards.add(new SetCardInfo("Ardent Plea", 1, Rarity.UNCOMMON, mage.cards.a.ArdentPlea.class)); diff --git a/Mage.Sets/src/mage/sets/AlchemyInnistrad.java b/Mage.Sets/src/mage/sets/AlchemyInnistrad.java new file mode 100644 index 00000000000..9e4500ddfb2 --- /dev/null +++ b/Mage.Sets/src/mage/sets/AlchemyInnistrad.java @@ -0,0 +1,36 @@ +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * @author TheElk801 + */ +public final class AlchemyInnistrad extends ExpansionSet { + + private static final AlchemyInnistrad instance = new AlchemyInnistrad(); + + public static AlchemyInnistrad getInstance() { + return instance; + } + + private AlchemyInnistrad() { + super("Alchemy: Innistrad", "Y22", ExpansionSet.buildDate(2021, 12, 9), SetType.MAGIC_ARENA); + this.blockName = "Alchemy"; + this.hasBoosters = false; + this.hasBasicLands = false; + + cards.add(new SetCardInfo("Citystalker Connoisseur", 27, Rarity.RARE, mage.cards.c.CitystalkerConnoisseur.class)); + cards.add(new SetCardInfo("Cursebound Witch", 24, Rarity.UNCOMMON, mage.cards.c.CurseboundWitch.class)); + cards.add(new SetCardInfo("Expedition Supplier", 6, Rarity.RARE, mage.cards.e.ExpeditionSupplier.class)); + cards.add(new SetCardInfo("Faithful Disciple", 7, Rarity.UNCOMMON, mage.cards.f.FaithfulDisciple.class)); + cards.add(new SetCardInfo("Ishkanah, Broodmother", 52, Rarity.MYTHIC, mage.cards.i.IshkanahBroodmother.class)); + cards.add(new SetCardInfo("Key to the Archive", 59, Rarity.RARE, mage.cards.k.KeyToTheArchive.class)); + cards.add(new SetCardInfo("Kindred Denial", 18, Rarity.UNCOMMON, mage.cards.k.KindredDenial.class)); + cards.add(new SetCardInfo("Obsessive Collector", 19, Rarity.RARE, mage.cards.o.ObsessiveCollector.class)); + cards.add(new SetCardInfo("Soulstealer Axe", 60, Rarity.UNCOMMON, mage.cards.s.SoulstealerAxe.class)); + cards.add(new SetCardInfo("Suntail Squadron", 11, Rarity.RARE, mage.cards.s.SuntailSquadron.class)); + cards.add(new SetCardInfo("Tireless Angler", 23, Rarity.RARE, mage.cards.t.TirelessAngler.class)); + } +} diff --git a/Mage.Sets/src/mage/sets/Amonkhet.java b/Mage.Sets/src/mage/sets/Amonkhet.java index 4cfcba2fbb7..4c14beb8ed4 100644 --- a/Mage.Sets/src/mage/sets/Amonkhet.java +++ b/Mage.Sets/src/mage/sets/Amonkhet.java @@ -1,10 +1,13 @@ - package mage.sets; import mage.cards.ExpansionSet; import mage.cards.repository.CardCriteria; import mage.cards.repository.CardInfo; import mage.cards.repository.CardRepository; +import mage.collation.BoosterCollator; +import mage.collation.BoosterStructure; +import mage.collation.CardRun; +import mage.collation.RarityConfiguration; import mage.constants.Rarity; import mage.constants.SetType; @@ -23,8 +26,6 @@ public final class Amonkhet extends ExpansionSet { return instance; } - private final List savedSpecialLand = new ArrayList<>(); - private Amonkhet() { super("Amonkhet", "AKH", ExpansionSet.buildDate(2017, 4, 28), SetType.EXPANSION); this.blockName = "Amonkhet"; @@ -35,8 +36,8 @@ public final class Amonkhet extends ExpansionSet { this.numBoosterUncommon = 3; this.numBoosterRare = 1; this.ratioBoosterMythic = 8; + this.ratioBoosterSpecialCommon = 129; this.maxCardNumberInBooster = 269; - this.ratioBoosterSpecialLand = 144; cards.add(new SetCardInfo("Ahn-Crop Champion", 194, Rarity.UNCOMMON, mage.cards.a.AhnCropChampion.class)); cards.add(new SetCardInfo("Ahn-Crop Crasher", 117, Rarity.UNCOMMON, mage.cards.a.AhnCropCrasher.class)); @@ -328,15 +329,124 @@ public final class Amonkhet extends ExpansionSet { } @Override - public List getSpecialLand() { - if (savedSpecialLand.isEmpty()) { - CardCriteria criteria = new CardCriteria(); - criteria.setCodes("MP2"); - criteria.minCardNumber(1); - criteria.maxCardNumber(30); - savedSpecialLand.addAll(CardRepository.instance.findCards(criteria)); + protected List findCardsByRarity(Rarity rarity) { + List cardInfos = super.findCardsByRarity(rarity); + if (rarity == Rarity.SPECIAL) { + cardInfos.addAll(CardRepository.instance.findCards(new CardCriteria().setCodes("MP2").maxCardNumber(30))); } + return cardInfos; + } - return new ArrayList<>(savedSpecialLand); + @Override + protected void generateBoosterMap() { + super.generateBoosterMap(); + CardRepository + .instance + .findCards(new CardCriteria().setCodes("MP2").maxCardNumber(30)) + .stream() + .forEach(cardInfo -> inBoosterMap.put("MP2_" + cardInfo.getCardNumber(), cardInfo)); + } + + @Override + public BoosterCollator createCollator() { + return new AmonkhetCollator(); + } +} + +// Booster collation info from https://www.lethe.xyz/mtg/collation/akh.html +// Using USA collation for all rarities +// Foil rare sheet used for regular rares as regular rare sheet is not known +class AmonkhetCollator implements BoosterCollator { + private final CardRun commonA = new CardRun(true, "145", "29", "62", "151", "30", "58", "142", "39", "52", "128", "13", "69", "146", "26", "57", "132", "20", "40", "143", "3", "72", "120", "27", "47", "122", "7", "45", "145", "12", "44", "150", "11", "62", "124", "29", "56", "142", "30", "52", "151", "39", "58", "146", "20", "40", "128", "13", "47", "150", "26", "69", "132", "3", "57", "124", "7", "45", "122", "27", "44", "120", "12", "72", "143", "11", "56"); + private final CardRun commonB = new CardRun(true, "166", "85", "185", "87", "178", "100", "161", "106", "156", "103", "174", "89", "168", "115", "188", "80", "171", "91", "181", "114", "185", "108", "179", "85", "161", "106", "178", "87", "156", "100", "166", "89", "174", "103", "188", "80", "171", "91", "185", "114", "181", "106", "179", "115", "168", "100", "166", "89", "178", "85", "156", "108", "161", "87", "174", "103", "188", "80", "171", "91", "181", "115", "168", "114", "179", "108"); + private final CardRun commonC1 = new CardRun(true, "230", "157", "144", "83", "70", "241", "177", "129", "102", "71", "19", "17", "119", "31", "53", "167", "116", "130", "32", "68", "111", "189", "141", "19", "43", "246", "157", "154", "6", "50", "230", "83", "144", "177", "70", "119", "102", "130", "241", "53", "189", "116", "129", "32", "71", "167", "31", "154", "246", "43", "17", "111", "141", "50", "68"); + private final CardRun commonC2 = new CardRun(true, "164", "109", "139", "173", "232", "9", "66", "95", "249", "176", "147", "36", "112", "92", "76", "6", "164", "158", "242", "18", "139", "95", "232", "173", "66", "36", "249", "109", "147", "176", "112", "9", "76", "92", "164", "158", "139", "18", "232", "95", "242", "173", "66", "9", "249", "109", "147", "176", "76", "36", "112", "92", "242", "158", "18"); + private final CardRun uncommonA = new CardRun(true, "33", "48", "191", "105", "231", "208", "126", "113", "67", "201", "123", "10", "228", "187", "101", "225", "198", "35", "193", "105", "218", "131", "23", "64", "227", "175", "99", "215", "38", "190", "90", "208", "126", "10", "60", "236", "191", "81", "201", "35", "229", "113", "198", "155", "33", "46", "231", "175", "90", "218", "38", "193", "67", "118", "23", "77", "190", "101", "227", "215", "123", "48", "191", "225", "153", "35", "60", "169", "99", "229", "208", "131", "67", "228", "187", "155", "33", "46", "193", "81", "236", "201", "126", "64", "175", "231", "118", "10", "77", "169", "101", "227", "198", "153", "48", "90", "228", "131", "23", "60", "190", "105", "225", "218", "123", "46", "99", "229", "155", "38", "77", "187", "81", "236", "215", "118", "64", "113", "169", "153"); + private final CardRun uncommonB = new CardRun(true, "25", "140", "202", "163", "238", "206", "28", "54", "152", "197", "165", "110", "244", "217", "93", "233", "194", "75", "8", "121", "200", "162", "37", "41", "96", "186", "226", "219", "34", "61", "127", "216", "163", "94", "117", "209", "79", "238", "202", "22", "73", "140", "195", "184", "25", "93", "206", "110", "194", "28", "54", "152", "197", "162", "37", "41", "200", "165", "233", "217", "8", "75", "117", "209", "186", "22", "121", "219", "96", "226", "195", "34", "61", "127", "216", "184", "94", "73", "244", "79", "238", "202", "25", "54", "140", "194", "163", "8", "121", "206", "165", "110", "200", "22", "75", "152", "197", "162", "28", "93", "209", "96", "233", "217", "37", "41", "117", "195", "186", "94", "61", "244", "79", "226", "219", "34", "73", "127", "216", "184"); + private final CardRun rare = new CardRun(true, "65", "222", "148", "243", "235", "5", "196", "82", "98", "224", "16", "49", "199", "134", "239", "14", "15", "203", "213", "78", "234", "2", "211", "59", "135", "240", "207", "55", "221", "137", "104", "1", "24", "51", "159", "138", "245", "107", "63", "125", "149", "86", "243", "4", "74", "172", "213", "182", "88", "211", "180", "148", "212", "196", "210", "84", "183", "137", "247", "98", "65", "170", "134", "204", "220", "5", "78", "160", "135", "239", "104", "42", "214", "138", "2", "235", "15", "107", "180", "133", "248", "203", "49", "159", "149", "16", "237", "192", "86", "170", "223", "240", "222", "55", "172", "21", "4", "245", "224", "212", "183", "199", "247", "97", "51", "214", "220", "24", "234", "63", "88", "205", "207", "248", "221", "74", "160", "237", "210", "136", "223"); + private final CardRun masterpiece = new CardRun(false, "MP2_1", "MP2_2", "MP2_3", "MP2_4", "MP2_5", "MP2_6", "MP2_7", "MP2_8", "MP2_9", "MP2_10", "MP2_11", "MP2_12", "MP2_13", "MP2_14", "MP2_15", "MP2_16", "MP2_17", "MP2_18", "MP2_19", "MP2_20", "MP2_21", "MP2_22", "MP2_23", "MP2_24", "MP2_25", "MP2_26", "MP2_27", "MP2_28", "MP2_29", "MP2_30"); + private final CardRun land = new CardRun(false, "250", "251", "252", "253", "254", "255", "256", "257", "258", "259", "260", "261", "262", "263", "264", "265", "266", "267", "268", "269"); + + private final BoosterStructure AABBC1C1C1C1C1C1 = new BoosterStructure( + commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AAABBC1C1C1C1C1 = new BoosterStructure( + commonA, commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AABBC1C1C1C1C1M = new BoosterStructure( + commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1, commonC1, + masterpiece + ); + private final BoosterStructure AAAABBC2C2C2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, + commonC2, commonC2, commonC2, commonC2 + ); + private final BoosterStructure AAAABBBBC2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, commonB, commonB, + commonC2, commonC2 + ); + private final BoosterStructure AAB = new BoosterStructure(uncommonA, uncommonA, uncommonB); + private final BoosterStructure ABB = new BoosterStructure(uncommonA, uncommonB, uncommonB); + private final BoosterStructure R1 = new BoosterStructure(rare); + private final BoosterStructure L1 = new BoosterStructure(land); + + // In order for equal numbers of each common to exist, the average booster must contain: + // 3.27 A commons (36 / 11) (rounded to 418/128) + // 2.18 B commons (24 / 11) (rounded to 280/128) + // 2.73 C1 commons (30 / 11) (rounded to 349/128) + // 1.82 C2 commons (20 / 11) (rounded to 232/128) + // These numbers are the same for all sets with 101 commons in A/B/C1/C2 print runs + // and with 10 common slots per booster + private final RarityConfiguration commonRuns = new RarityConfiguration( + AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, + + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AABBC1C1C1C1C1M, + + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, + AAAABBC2C2C2C2, AAAABBC2C2C2C2, + + AAAABBBBC2C2, AAAABBBBC2C2, AAAABBBBC2C2, AAAABBBBC2C2, AAAABBBBC2C2, AAAABBBBC2C2, + AAAABBBBC2C2, AAAABBBBC2C2, AAAABBBBC2C2, AAAABBBBC2C2, AAAABBBBC2C2, AAAABBBBC2C2 + ); + private final RarityConfiguration uncommonRuns = new RarityConfiguration(AAB, ABB); + private final RarityConfiguration rareRuns = new RarityConfiguration(R1); + private final RarityConfiguration landRuns = new RarityConfiguration(L1); + + @Override + public List makeBooster() { + List booster = new ArrayList<>(); + booster.addAll(commonRuns.getNext().makeRun()); + booster.addAll(uncommonRuns.getNext().makeRun()); + booster.addAll(rareRuns.getNext().makeRun()); + booster.addAll(landRuns.getNext().makeRun()); + return booster; } } diff --git a/Mage.Sets/src/mage/sets/AmonkhetRemastered.java b/Mage.Sets/src/mage/sets/AmonkhetRemastered.java index 58263f0206b..5f05893c39e 100644 --- a/Mage.Sets/src/mage/sets/AmonkhetRemastered.java +++ b/Mage.Sets/src/mage/sets/AmonkhetRemastered.java @@ -1,16 +1,9 @@ package mage.sets; import mage.cards.ExpansionSet; -import mage.cards.repository.CardCriteria; -import mage.cards.repository.CardInfo; -import mage.cards.repository.CardRepository; -import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.SetType; -import java.util.ArrayList; -import java.util.List; - /** * https://scryfall.com/sets/akr */ @@ -22,8 +15,6 @@ public class AmonkhetRemastered extends ExpansionSet { return instance; } - private final List savedSpecialLand = new ArrayList<>(); - private AmonkhetRemastered() { super("Amonkhet Remastered", "AKR", ExpansionSet.buildDate(2020, 8, 13), SetType.MAGIC_ARENA); this.hasBoosters = true; @@ -34,7 +25,6 @@ public class AmonkhetRemastered extends ExpansionSet { this.numBoosterUncommon = 3; this.numBoosterRare = 1; this.ratioBoosterMythic = 8; - this.ratioBoosterSpecialLand = 1; // replace all basic lands cards.add(new SetCardInfo("Abandoned Sarcophagus", 268, Rarity.RARE, mage.cards.a.AbandonedSarcophagus.class)); cards.add(new SetCardInfo("Abrade", 136, Rarity.UNCOMMON, mage.cards.a.Abrade.class)); @@ -376,19 +366,4 @@ public class AmonkhetRemastered extends ExpansionSet { cards.add(new SetCardInfo("Wrath of God", 46, Rarity.RARE, mage.cards.w.WrathOfGod.class)); cards.add(new SetCardInfo("Zealot of the God-Pharaoh", 181, Rarity.COMMON, mage.cards.z.ZealotOfTheGodPharaoh.class)); } - - @Override - // the common taplands replacing the basic land - public List getSpecialLand() { - if (savedSpecialLand.isEmpty()) { - CardCriteria criteria = new CardCriteria(); - criteria.setCodes(this.code); - criteria.rarities(Rarity.COMMON); - criteria.types(CardType.LAND); - savedSpecialLand.addAll(CardRepository.instance.findCards(criteria)); - } - - return new ArrayList<>(savedSpecialLand); - } - } diff --git a/Mage.Sets/src/mage/sets/Apocalypse.java b/Mage.Sets/src/mage/sets/Apocalypse.java index 787c55c20ab..33e89f389a4 100644 --- a/Mage.Sets/src/mage/sets/Apocalypse.java +++ b/Mage.Sets/src/mage/sets/Apocalypse.java @@ -145,6 +145,7 @@ public final class Apocalypse extends ExpansionSet { cards.add(new SetCardInfo("Suffocating Blast", 124, Rarity.RARE, mage.cards.s.SuffocatingBlast.class)); cards.add(new SetCardInfo("Sylvan Messenger", 87, Rarity.UNCOMMON, mage.cards.s.SylvanMessenger.class)); cards.add(new SetCardInfo("Symbiotic Deployment", 88, Rarity.RARE, mage.cards.s.SymbioticDeployment.class)); + cards.add(new SetCardInfo("Tahngarth's Glare", 70, Rarity.COMMON, mage.cards.t.TahngarthsGlare.class)); cards.add(new SetCardInfo("Temporal Spring", 125, Rarity.COMMON, mage.cards.t.TemporalSpring.class)); cards.add(new SetCardInfo("Tidal Courier", 31, Rarity.UNCOMMON, mage.cards.t.TidalCourier.class)); cards.add(new SetCardInfo("Tranquil Path", 89, Rarity.COMMON, mage.cards.t.TranquilPath.class)); @@ -160,5 +161,6 @@ public final class Apocalypse extends ExpansionSet { cards.add(new SetCardInfo("Wild Research", 72, Rarity.RARE, mage.cards.w.WildResearch.class)); cards.add(new SetCardInfo("Yavimaya Coast", 143, Rarity.RARE, mage.cards.y.YavimayaCoast.class)); cards.add(new SetCardInfo("Yavimaya's Embrace", 127, Rarity.RARE, mage.cards.y.YavimayasEmbrace.class)); + cards.add(new SetCardInfo("Zombie Boa", 54, Rarity.COMMON, mage.cards.z.ZombieBoa.class)); } } diff --git a/Mage.Sets/src/mage/sets/Archenemy.java b/Mage.Sets/src/mage/sets/Archenemy.java index 7c394c4c9f3..4df6d1c6eb3 100644 --- a/Mage.Sets/src/mage/sets/Archenemy.java +++ b/Mage.Sets/src/mage/sets/Archenemy.java @@ -67,6 +67,7 @@ public final class Archenemy extends ExpansionSet { cards.add(new SetCardInfo("Forgotten Ancient", 57, Rarity.RARE, mage.cards.f.ForgottenAncient.class)); cards.add(new SetCardInfo("Furnace Whelp", 39, Rarity.UNCOMMON, mage.cards.f.FurnaceWhelp.class)); cards.add(new SetCardInfo("Gathan Raiders", 40, Rarity.COMMON, mage.cards.g.GathanRaiders.class)); + cards.add(new SetCardInfo("Gleeful Sabotage", 58, Rarity.COMMON, mage.cards.g.GleefulSabotage.class)); cards.add(new SetCardInfo("Graypelt Refuge", 125, Rarity.UNCOMMON, mage.cards.g.GraypeltRefuge.class)); cards.add(new SetCardInfo("Gruul Signet", 108, Rarity.COMMON, mage.cards.g.GruulSignet.class)); cards.add(new SetCardInfo("Harmonize", 59, Rarity.UNCOMMON, mage.cards.h.Harmonize.class)); @@ -98,6 +99,7 @@ public final class Archenemy extends ExpansionSet { cards.add(new SetCardInfo("Makeshift Mannequin", 20, Rarity.UNCOMMON, mage.cards.m.MakeshiftMannequin.class)); cards.add(new SetCardInfo("March of the Machines", 6, Rarity.RARE, mage.cards.m.MarchOfTheMachines.class)); cards.add(new SetCardInfo("Master Transmuter", 7, Rarity.RARE, mage.cards.m.MasterTransmuter.class)); + cards.add(new SetCardInfo("Memnarch", 112, Rarity.RARE, mage.cards.m.Memnarch.class)); cards.add(new SetCardInfo("Metallurgeon", 2, Rarity.UNCOMMON, mage.cards.m.Metallurgeon.class)); cards.add(new SetCardInfo("Mistvein Borderpost", 90, Rarity.COMMON, mage.cards.m.MistveinBorderpost.class)); cards.add(new SetCardInfo("Molimo, Maro-Sorcerer", 64, Rarity.RARE, mage.cards.m.MolimoMaroSorcerer.class)); diff --git a/Mage.Sets/src/mage/sets/BattleForZendikar.java b/Mage.Sets/src/mage/sets/BattleForZendikar.java index 03aace3d548..1d4e45fb1a9 100644 --- a/Mage.Sets/src/mage/sets/BattleForZendikar.java +++ b/Mage.Sets/src/mage/sets/BattleForZendikar.java @@ -4,6 +4,10 @@ import mage.cards.ExpansionSet; import mage.cards.repository.CardCriteria; import mage.cards.repository.CardInfo; import mage.cards.repository.CardRepository; +import mage.collation.BoosterCollator; +import mage.collation.BoosterStructure; +import mage.collation.CardRun; +import mage.collation.RarityConfiguration; import mage.constants.Rarity; import mage.constants.SetType; @@ -21,8 +25,6 @@ public final class BattleForZendikar extends ExpansionSet { return instance; } - private final List savedSpecialLand = new ArrayList<>(); - private BattleForZendikar() { super("Battle for Zendikar", "BFZ", ExpansionSet.buildDate(2015, 10, 2), SetType.EXPANSION); this.blockName = "Battle for Zendikar"; @@ -33,8 +35,7 @@ public final class BattleForZendikar extends ExpansionSet { this.numBoosterUncommon = 3; this.numBoosterRare = 1; this.ratioBoosterMythic = 8; - this.numBoosterSpecial = 0; - this.ratioBoosterSpecialLand = 144; + this.ratioBoosterSpecialCommon = 144; cards.add(new SetCardInfo("Adverse Conditions", 54, Rarity.UNCOMMON, mage.cards.a.AdverseConditions.class)); cards.add(new SetCardInfo("Akoum Firebird", 138, Rarity.MYTHIC, mage.cards.a.AkoumFirebird.class)); @@ -338,15 +339,130 @@ public final class BattleForZendikar extends ExpansionSet { } @Override - public List getSpecialLand() { - if (savedSpecialLand.isEmpty()) { - CardCriteria criteria = new CardCriteria(); - criteria.setCodes("EXP"); - criteria.minCardNumber(1); - criteria.maxCardNumber(45); - savedSpecialLand.addAll(CardRepository.instance.findCards(criteria)); + protected List findCardsByRarity(Rarity rarity) { + List cardInfos = super.findCardsByRarity(rarity); + if (rarity == Rarity.LAND) { + // only the full-art basic lands are found in boosters + cardInfos.removeIf(cardInfo -> cardInfo.getCardNumber().contains("a")); + } else if (rarity == Rarity.SPECIAL) { + cardInfos.addAll(CardRepository.instance.findCards(new CardCriteria().setCodes("EXP").maxCardNumber(25))); } + return cardInfos; + } - return new ArrayList<>(savedSpecialLand); + @Override + protected void generateBoosterMap() { + super.generateBoosterMap(); + CardRepository + .instance + .findCards(new CardCriteria().setCodes("EXP").maxCardNumber(25)) + .stream() + .forEach(cardInfo -> inBoosterMap.put("EXP_" + cardInfo.getCardNumber(), cardInfo)); + } + + @Override + public BoosterCollator createCollator() { + return new BattleForZendikarCollator(); + } +} + +// Booster collation info from https://www.lethe.xyz/mtg/collation/bfz.html +// Using USA collation for all rarities +// Foil rare sheet used for regular rares as regular rare sheet is not known +class BattleForZendikarCollator implements BoosterCollator { + private final CardRun commonA = new CardRun(true, "165", "83", "100", "194", "60", "91", "189", "74", "118", "182", "76", "95", "196", "72", "115", "191", "61", "110", "163", "69", "98", "172", "60", "114", "185", "65", "100", "165", "62", "125", "182", "70", "118", "189", "63", "103", "184", "74", "117", "194", "72", "91", "173", "69", "95", "196", "83", "110", "191", "76", "115", "172", "62", "98", "184", "65", "114", "163", "63", "125", "185", "61", "117", "173", "70", "103"); + private final CardRun commonB = new CardRun(true, "129", "32", "142", "39", "147", "27", "131", "20", "150", "19", "141", "51", "161", "52", "155", "25", "146", "35", "142", "38", "157", "33", "152", "32", "161", "51", "129", "39", "131", "27", "141", "19", "147", "20", "146", "52", "150", "33", "155", "35", "157", "32", "152", "25", "142", "38", "150", "27", "147", "39", "129", "20", "161", "19", "131", "33", "141", "35", "152", "51", "155", "38", "146", "52", "157", "25"); + private final CardRun commonC1 = new CardRun(true, "58", "12", "105", "171", "240", "86", "238", "93", "166", "7", "88", "237", "111", "190", "236", "58", "224", "105", "168", "243", "71", "13", "93", "171", "66", "90", "12", "166", "246", "84", "96", "240", "179", "86", "7", "108", "238", "237", "190", "66", "90", "13", "193", "88", "236", "96", "84", "179", "168", "111", "71", "243", "193", "108", "246"); + private final CardRun commonC2 = new CardRun(true, "134", "47", "156", "49", "10", "134", "40", "149", "148", "30", "177", "160", "48", "159", "47", "92", "156", "28", "55", "148", "49", "10", "136", "21", "160", "47", "177", "159", "40", "92", "149", "28", "55", "136", "30", "134", "48", "10", "148", "21", "49", "156", "40", "177", "149", "30", "136", "28", "92", "160", "48", "224", "159", "21", "55"); + private final CardRun uncommonA = new CardRun(true, "158", "97", "50", "188", "212", "137", "122", "24", "164", "218", "140", "113", "41", "195", "201", "132", "94", "36", "178", "210", "143", "104", "46", "183", "211", "135", "124", "18", "186", "220", "145", "126", "53", "187", "212", "154", "97", "45", "169", "204", "158", "101", "41", "178", "218", "135", "94", "53", "164", "220", "132", "113", "24", "188", "201", "137", "104", "36", "195", "205", "143", "124", "18", "186", "210", "145", "122", "46", "183", "211", "140", "126", "50", "187", "204", "158", "124", "45", "195", "205", "143", "101", "50", "188", "212", "137", "97", "24", "169", "211", "154", "113", "18", "164", "210", "140", "94", "41", "186", "201", "135", "104", "36", "183", "218", "132", "122", "53", "178", "220", "154", "101", "46", "187", "204", "145", "126", "45", "169", "205"); + private final CardRun uncommonB = new CardRun(true, "223", "232", "5", "79", "219", "121", "67", "248", "3", "153", "73", "225", "207", "23", "54", "176", "106", "89", "230", "1", "128", "64", "227", "229", "16", "56", "192", "116", "82", "233", "14", "34", "75", "223", "232", "44", "79", "175", "67", "121", "231", "3", "130", "68", "226", "219", "23", "59", "176", "248", "89", "153", "5", "73", "225", "231", "34", "64", "192", "106", "82", "230", "16", "128", "54", "226", "229", "44", "75", "207", "121", "56", "233", "14", "130", "68", "227", "219", "1", "67", "175", "116", "59", "231", "3", "23", "79", "225", "232", "34", "89", "176", "106", "73", "230", "5", "153", "56", "223", "207", "14", "54", "192", "227", "82", "248", "1", "128", "59", "226", "229", "44", "75", "175", "116", "64", "233", "16", "130", "68"); + private final CardRun rare = new CardRun(true, "107", "22", "4", "2", "170", "235", "198", "217", "120", "139", "26", "214", "247", "151", "167", "119", "242", "112", "37", "123", "85", "180", "200", "213", "42", "57", "234", "239", "6", "245", "99", "11", "216", "199", "81", "87", "8", "228", "29", "244", "241", "162", "31", "203", "208", "215", "144", "133", "22", "78", "107", "222", "127", "209", "174", "181", "249", "77", "120", "4", "2", "202", "197", "9", "221", "214", "167", "247", "235", "123", "17", "198", "245", "112", "139", "102", "200", "85", "138", "242", "99", "37", "8", "87", "239", "42", "206", "199", "6", "180", "26", "162", "241", "80", "31", "57", "170", "151", "208", "78", "216", "43", "127", "181", "81", "234", "202", "221", "77", "15", "209", "203", "102", "133", "244", "222", "228", "109", "9", "215", "249"); + private final CardRun expedition = new CardRun(false, "EXP_1", "EXP_2", "EXP_3", "EXP_4", "EXP_5", "EXP_6", "EXP_7", "EXP_8", "EXP_9", "EXP_10", "EXP_11", "EXP_12", "EXP_13", "EXP_14", "EXP_15", "EXP_16", "EXP_17", "EXP_18", "EXP_19", "EXP_20", "EXP_21", "EXP_22", "EXP_23", "EXP_24", "EXP_25"); + private final CardRun land = new CardRun(false, "250", "251", "252", "253", "254", "255", "256", "257", "258", "259", "260", "261", "262", "263", "264", "265", "266", "267", "268", "269", "270", "271", "272", "273", "274"); + + private final BoosterStructure AABBC1C1C1C1C1C1 = new BoosterStructure( + commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AAABBC1C1C1C1C1 = new BoosterStructure( + commonA, commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AABBC1C1C1C1C1X = new BoosterStructure( + commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1, commonC1, + expedition + ); + private final BoosterStructure AAAABBC2C2C2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, + commonC2, commonC2, commonC2, commonC2 + ); + private final BoosterStructure AAAABBBBC2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, commonB, commonB, + commonC2, commonC2 + ); + private final BoosterStructure AAB = new BoosterStructure(uncommonA, uncommonA, uncommonB); + private final BoosterStructure ABB = new BoosterStructure(uncommonA, uncommonB, uncommonB); + private final BoosterStructure R1 = new BoosterStructure(rare); + private final BoosterStructure L1 = new BoosterStructure(land); + + // In order for equal numbers of each common to exist, the average booster must contain: + // 3.27 A commons (36 / 11) (rounded to 471/144) + // 2.18 B commons (24 / 11) (rounded to 314/144) + // 2.73 C1 commons (30 / 11) (rounded to 392/144) + // 1.82 C2 commons (20 / 11) (rounded to 262/144) + // These numbers are the same for all sets with 101 commons in A/B/C1/C2 print runs + // and with 10 common slots per booster + private final RarityConfiguration commonRuns = new RarityConfiguration( + AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AABBC1C1C1C1C1X, + + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, + + AAAABBBBC2C2, AAAABBBBC2C2, AAAABBBBC2C2, AAAABBBBC2C2, AAAABBBBC2C2, + AAAABBBBC2C2, AAAABBBBC2C2, AAAABBBBC2C2, AAAABBBBC2C2, AAAABBBBC2C2, + AAAABBBBC2C2, AAAABBBBC2C2, AAAABBBBC2C2 + ); + private final RarityConfiguration uncommonRuns = new RarityConfiguration(AAB, ABB); + private final RarityConfiguration rareRuns = new RarityConfiguration(R1); + private final RarityConfiguration landRuns = new RarityConfiguration(L1); + + @Override + public List makeBooster() { + List booster = new ArrayList<>(); + booster.addAll(commonRuns.getNext().makeRun()); + booster.addAll(uncommonRuns.getNext().makeRun()); + booster.addAll(rareRuns.getNext().makeRun()); + booster.addAll(landRuns.getNext().makeRun()); + return booster; } } diff --git a/Mage.Sets/src/mage/sets/Battlebond.java b/Mage.Sets/src/mage/sets/Battlebond.java index 881718498c4..3cecbae6fd7 100644 --- a/Mage.Sets/src/mage/sets/Battlebond.java +++ b/Mage.Sets/src/mage/sets/Battlebond.java @@ -1,9 +1,17 @@ package mage.sets; +import mage.abilities.Ability; +import mage.abilities.keyword.PartnerWithAbility; +import mage.cards.Card; import mage.cards.ExpansionSet; +import mage.cards.repository.CardInfo; +import mage.cards.repository.CardRepository; import mage.constants.Rarity; import mage.constants.SetType; +import java.util.ArrayList; +import java.util.List; + /** * @author TheElk801 */ @@ -20,7 +28,6 @@ public final class Battlebond extends ExpansionSet { this.blockName = "Battlebond"; this.hasBasicLands = true; this.hasBoosters = true; - this.hasPartnerMechanic = true; this.numBoosterLands = 1; this.numBoosterCommon = 10; this.numBoosterUncommon = 3; @@ -287,4 +294,85 @@ public final class Battlebond extends ExpansionSet { cards.add(new SetCardInfo("Zndrsplt, Eye of Wisdom", 5, Rarity.RARE, mage.cards.z.ZndrspltEyeOfWisdom.class)); } + @Override + public List tryBooster() { + List booster = new ArrayList<>(); + boolean partnerAllowed = true; + List uncommons = getCardsByRarity(Rarity.UNCOMMON); + for (int i = 0; i < numBoosterUncommon; i++) { + while (true) { + addToBooster(booster, uncommons); + int check = addMissingPartner(booster, partnerAllowed, numBoosterUncommon - 1, i); + if (check == 1) { + break; + } + if (check == 2) { + partnerAllowed = false; + //Be sure to account for the added card + if (i != numBoosterUncommon - 1) { + i += 1; + } + break; + } + } + } + + List commons = getCardsByRarity(Rarity.COMMON); + for (int i = 0; i < numBoosterCommon; i++) { + addToBooster(booster, commons); + } + + List rares = getCardsByRarity(Rarity.RARE); + List mythics = getCardsByRarity(Rarity.MYTHIC); + for (int i = 0; i < numBoosterRare; i++) { + List cards = (checkMythic() ? mythics : rares); + while (true) { + addToBooster(booster, cards); + int check = addMissingPartner(booster, partnerAllowed, -1, 1); + if (check == 1) { + break; + } + if (check == 2) { + partnerAllowed = false; + break; + } + } + } + + List basicLands = getCardsByRarity(Rarity.LAND); + for (int i = 0; i < numBoosterLands; i++) { + addToBooster(booster, basicLands); + } + + return booster; + } + + private int addMissingPartner(List booster, boolean partnerAllowed, int max, int i) { + + Card sourceCard = booster.get(booster.size() - 1); + for (Ability ability : sourceCard.getAbilities()) { + + //Check if fetched card has the PartnerWithAbility + if (ability instanceof PartnerWithAbility) { + String partnerName = ((PartnerWithAbility) ability).getPartnerName(); + //Check if the pack already contains a partner pair + if (partnerAllowed) { + //Added card always replaces an uncommon card + Card card = CardRepository.instance.findCardWPreferredSet(partnerName, sourceCard.getExpansionSetCode(), false).getCard(); + if (i < max) { + booster.add(card); + } else { + booster.set(0, card); + } + //2 return value indicates found partner + return 2; + } else { + //If partner already exists, remove card and loop again + booster.remove(booster.size() - 1); + return 0; + } + } + } + return 1; + } } diff --git a/Mage.Sets/src/mage/sets/BornOfTheGods.java b/Mage.Sets/src/mage/sets/BornOfTheGods.java index 5bcb1357669..f57343f4401 100644 --- a/Mage.Sets/src/mage/sets/BornOfTheGods.java +++ b/Mage.Sets/src/mage/sets/BornOfTheGods.java @@ -1,10 +1,16 @@ - package mage.sets; import mage.cards.ExpansionSet; +import mage.collation.BoosterCollator; +import mage.collation.BoosterStructure; +import mage.collation.CardRun; +import mage.collation.RarityConfiguration; import mage.constants.Rarity; import mage.constants.SetType; +import java.util.ArrayList; +import java.util.List; + /** * * @author LevelX2 @@ -195,4 +201,56 @@ public final class BornOfTheGods extends ExpansionSet { cards.add(new SetCardInfo("Xenagos, God of Revels", 156, Rarity.MYTHIC, mage.cards.x.XenagosGodOfRevels.class)); } + @Override + public BoosterCollator createCollator() { + return new BornOfTheGodsCollator(); + } +} + +// Booster collation info from https://www.lethe.xyz/mtg/collation/bng.html +// Using USA collation for common/uncommon, rare collation inferred from other sets +class BornOfTheGodsCollator implements BoosterCollator { + private final CardRun commonA = new CardRun(true, "131", "38", "91", "82", "15", "136", "49", "78", "89", "20", "67", "46", "142", "91", "18", "76", "131", "41", "107", "16", "78", "118", "104", "51", "9", "139", "72", "10", "110", "33", "70", "142", "16", "67", "38", "95", "18", "139", "86", "51", "85", "25", "110", "118", "41", "70", "9", "89", "138", "76", "33", "20", "95", "82", "136", "104", "49", "15", "86", "138", "10", "107", "46", "85", "72", "25"); + private final CardRun commonB = new CardRun(true, "99", "120", "77", "100", "19", "8", "127", "60", "3", "106", "141", "45", "90", "77", "53", "100", "117", "127", "36", "3", "64", "99", "134", "35", "34", "106", "74", "141", "8", "22", "36", "102", "125", "52", "21", "134", "60", "93", "90", "34", "120", "19", "74", "102", "53", "117", "64", "35", "21", "93", "125", "22", "52", "45"); + private final CardRun uncommonA = new CardRun(true, "12", "158", "54", "2", "71", "132", "4", "114", "30", "162", "62", "116", "158", "50", "112", "23", "54", "140", "71", "12", "2", "29", "47", "143", "160", "109", "79", "162", "42", "112", "146", "133", "23", "47", "59", "113", "150", "116", "154", "30", "83", "88", "146", "160", "32", "87", "109", "143", "59", "50", "150", "114", "154", "132", "79", "32", "113", "4", "140", "83", "87", "29", "42", "88", "62", "133"); + private final CardRun uncommonB = new CardRun(true, "14", "37", "65", "130", "111", "147", "6", "161", "58", "122", "43", "40", "92", "28", "128", "65", "153", "56", "105", "135", "1", "61", "43", "101", "155", "128", "13", "81", "37", "126", "92", "14", "58", "56", "28", "161", "135", "84", "147", "130", "1", "105", "155", "6", "81", "126", "153", "111", "84", "13", "122", "40", "101", "61"); + private final CardRun rare = new CardRun(false, "7", "11", "17", "24", "26", "27", "31", "39", "44", "48", "55", "57", "66", "68", "69", "73", "75", "80", "94", "96", "98", "103", "108", "115", "119", "121", "123", "124", "129", "137", "157", "159", "163", "164", "165"); + private final CardRun mythic = new CardRun(false, "5", "63", "97", "144", "145", "148", "149", "151", "152", "156"); + private final CardRun land = new CardRun(false, "THS_230", "THS_231", "THS_232", "THS_233", "THS_234", "THS_235", "THS_236", "THS_237", "THS_238", "THS_239", "THS_240", "THS_241", "THS_242", "THS_243", "THS_244", "THS_245", "THS_246", "THS_247", "THS_248", "THS_249"); + + private final BoosterStructure AAAAABBBBB = new BoosterStructure( + commonA, commonA, commonA, commonA, commonA, + commonB, commonB, commonB, commonB, commonB + ); + private final BoosterStructure AAAAAABBBB = new BoosterStructure( + commonA, commonA, commonA, commonA, commonA, commonA, + commonB, commonB, commonB, commonB + ); + private final BoosterStructure AAB = new BoosterStructure(uncommonA, uncommonA, uncommonB); + private final BoosterStructure ABB = new BoosterStructure(uncommonA, uncommonB, uncommonB); + private final BoosterStructure R1 = new BoosterStructure(rare); + private final BoosterStructure M1 = new BoosterStructure(mythic); + private final BoosterStructure L1 = new BoosterStructure(land); + + private final RarityConfiguration commonRuns = new RarityConfiguration(AAAAABBBBB, AAAAAABBBB); + // In order for equal numbers of each uncommon to exist, the average booster must contain: + // 1.65 A uncommons (33 / 20) + // 1.35 B uncommons (27 / 20) + // These numbers are the same for all sets with 60 uncommons in asymmetrical A/B print runs + private final RarityConfiguration uncommonRuns = new RarityConfiguration( + AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, + ABB, ABB, ABB, ABB, ABB, ABB, ABB + ); + private final RarityConfiguration rareRuns = new RarityConfiguration(R1, R1, R1, R1, R1, R1, R1, M1); + private final RarityConfiguration landRuns = new RarityConfiguration(L1); + + @Override + public List makeBooster() { + List booster = new ArrayList<>(); + booster.addAll(commonRuns.getNext().makeRun()); + booster.addAll(uncommonRuns.getNext().makeRun()); + booster.addAll(rareRuns.getNext().makeRun()); + booster.addAll(landRuns.getNext().makeRun()); + return booster; + } } diff --git a/Mage.Sets/src/mage/sets/Coldsnap.java b/Mage.Sets/src/mage/sets/Coldsnap.java index 297fb01dbfd..dee3d0c5992 100644 --- a/Mage.Sets/src/mage/sets/Coldsnap.java +++ b/Mage.Sets/src/mage/sets/Coldsnap.java @@ -1,4 +1,3 @@ - package mage.sets; import mage.cards.ExpansionSet; @@ -8,7 +7,6 @@ import mage.cards.repository.CardRepository; import mage.constants.Rarity; import mage.constants.SetType; -import java.util.ArrayList; import java.util.List; /** @@ -192,23 +190,12 @@ public final class Coldsnap extends ExpansionSet { } @Override - public List getCardsByRarity(Rarity rarity) { - if (rarity != Rarity.COMMON) { - return super.getCardsByRarity(rarity); + protected List findCardsByRarity(Rarity rarity) { + List cardInfos = super.findCardsByRarity(rarity); + if (rarity == Rarity.COMMON) { + // Snow basic lands are included in boosters as regular commons + cardInfos.addAll(CardRepository.instance.findCards(new CardCriteria().setCodes(this.code).rarities(Rarity.LAND))); } - List savedCardsInfos = savedCards.get(rarity); - if (savedCardsInfos != null) { - return new ArrayList(savedCardsInfos); - } - CardCriteria criteria = new CardCriteria(); - criteria.setCodes(this.code).rarities(rarity); - savedCardsInfos = CardRepository.instance.findCards(criteria); - // Snow basic lands are included in boosters as regular commons, not in a land slot - criteria = new CardCriteria(); - criteria.setCodes(this.code).rarities(Rarity.LAND); - savedCardsInfos.addAll(CardRepository.instance.findCards(criteria)); - savedCards.put(rarity, savedCardsInfos); - // Return a copy of the saved cards information, as not to modify the original. - return new ArrayList(savedCardsInfos); + return cardInfos; } } diff --git a/Mage.Sets/src/mage/sets/ColdsnapThemeDecks.java b/Mage.Sets/src/mage/sets/ColdsnapThemeDecks.java index 2878cb519a4..739ce31dc39 100644 --- a/Mage.Sets/src/mage/sets/ColdsnapThemeDecks.java +++ b/Mage.Sets/src/mage/sets/ColdsnapThemeDecks.java @@ -50,7 +50,7 @@ public class ColdsnapThemeDecks extends ExpansionSet { cards.add(new SetCardInfo("Island", 373, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Island", 374, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Kjeldoran Dead", 137, Rarity.COMMON, mage.cards.k.KjeldoranDead.class)); - //cards.add(new SetCardInfo("Kjeldoran Elite Guard", 34, Rarity.UNCOMMON, mage.cards.k.KjeldoranEliteGuard.class)); + cards.add(new SetCardInfo("Kjeldoran Elite Guard", 34, Rarity.UNCOMMON, mage.cards.k.KjeldoranEliteGuard.class)); cards.add(new SetCardInfo("Kjeldoran Home Guard", 8, Rarity.UNCOMMON, mage.cards.k.KjeldoranHomeGuard.class)); cards.add(new SetCardInfo("Kjeldoran Pride", "9b", Rarity.COMMON, mage.cards.k.KjeldoranPride.class)); cards.add(new SetCardInfo("Lat-Nam's Legacy", "30b", Rarity.COMMON, mage.cards.l.LatNamsLegacy.class)); diff --git a/Mage.Sets/src/mage/sets/CoreSet2019.java b/Mage.Sets/src/mage/sets/CoreSet2019.java index ec4614f3672..09d37b5b5f0 100644 --- a/Mage.Sets/src/mage/sets/CoreSet2019.java +++ b/Mage.Sets/src/mage/sets/CoreSet2019.java @@ -1,10 +1,10 @@ package mage.sets; import mage.cards.ExpansionSet; -import mage.cards.repository.CardCriteria; -import mage.cards.repository.CardInfo; -import mage.cards.repository.CardRepository; -import mage.constants.CardType; +import mage.collation.BoosterCollator; +import mage.collation.BoosterStructure; +import mage.collation.CardRun; +import mage.collation.RarityConfiguration; import mage.constants.Rarity; import mage.constants.SetType; @@ -22,13 +22,10 @@ public final class CoreSet2019 extends ExpansionSet { return instance; } - private final List savedSpecialLand = new ArrayList<>(); - private CoreSet2019() { super("Core Set 2019", "M19", ExpansionSet.buildDate(2018, 7, 13), SetType.CORE); this.hasBoosters = true; this.hasBasicLands = true; - this.numBoosterSpecial = 0; this.numBoosterLands = 1; this.numBoosterCommon = 10; this.numBoosterUncommon = 3; @@ -360,38 +357,101 @@ public final class CoreSet2019 extends ExpansionSet { } @Override - public List getCardsByRarity(Rarity rarity) { - // Common cards retrievement of Core Set 2019 boosters - prevent the retrievement of the common lands (e.g. Meandering River) - if (rarity == Rarity.COMMON) { - List savedCardsInfos = savedCards.get(rarity); - if (savedCardsInfos == null) { - CardCriteria criteria = new CardCriteria(); - criteria.rarities(Rarity.COMMON); - criteria.setCodes(this.code).notTypes(CardType.LAND); - savedCardsInfos = CardRepository.instance.findCards(criteria); - if (maxCardNumberInBooster != Integer.MAX_VALUE) { - savedCardsInfos.removeIf(next -> next.getCardNumberAsInt() > maxCardNumberInBooster); - } - savedCards.put(rarity, savedCardsInfos); - } - // Return a copy of the saved cards information, as not to let modify the original. - return new ArrayList<>(savedCardsInfos); - } else { - return super.getCardsByRarity(rarity); - } - } - - @Override - // the common taplands replacing the basic land - public List getSpecialLand() { - if (savedSpecialLand.isEmpty()) { - CardCriteria criteria = new CardCriteria(); - criteria.setCodes(this.code); - criteria.rarities(Rarity.COMMON); - criteria.types(CardType.LAND); - savedSpecialLand.addAll(CardRepository.instance.findCards(criteria)); - } - - return new ArrayList<>(savedSpecialLand); + public BoosterCollator createCollator() { + return new CoreSet2019Collator(); + } +} + +// Booster collation info from https://www.lethe.xyz/mtg/collation/m19.html +// Using USA collation for common/uncommon, rare collation inferred from other sets +class CoreSet2019Collator implements BoosterCollator { + private final CardRun commonA = new CardRun(true, "161", "28", "64", "142", "24", "50", "165", "35", "56", "150", "18", "57", "132", "36", "80", "127", "19", "82", "143", "10", "71", "164", "31", "51", "142", "11", "53", "141", "24", "56", "165", "41", "64", "150", "15", "44", "147", "18", "80", "153", "28", "48", "161", "35", "82", "132", "36", "50", "143", "41", "57", "141", "31", "51", "127", "10", "71", "164", "11", "48", "147", "19", "44", "153", "15", "53"); + private final CardRun commonB = new CardRun(true, "170", "100", "188", "87", "193", "123", "202", "108", "180", "103", "183", "93", "174", "94", "191", "95", "205", "101", "188", "123", "198", "108", "180", "89", "170", "87", "187", "100", "202", "94", "193", "109", "174", "103", "191", "93", "198", "101", "183", "95", "170", "89", "205", "100", "188", "87", "187", "123", "180", "108", "202", "103", "193", "109", "174", "94", "183", "95", "205", "101", "191", "93", "198", "89", "187", "109"); + private final CardRun commonC1 = new CardRun(true, "234", "46", "166", "195", "120", "146", "204", "12", "118", "83", "40", "199", "139", "126", "75", "237", "177", "158", "105", "12", "245", "70", "120", "131", "83", "195", "239", "85", "58", "7", "46", "237", "146", "16", "118", "204", "32", "158", "177", "245", "75", "105", "239", "40", "58", "139", "126", "7", "166", "70", "199", "85", "131", "16", "32"); + private final CardRun commonC2 = new CardRun(true, "234", "172", "54", "190", "122", "240", "156", "81", "171", "124", "45", "233", "25", "119", "210", "159", "124", "38", "54", "122", "133", "172", "240", "8", "156", "190", "25", "124", "45", "38", "171", "133", "119", "233", "8", "159", "210", "25", "240", "54", "190", "122", "81", "172", "233", "156", "38", "81", "171", "133", "45", "8", "159", "210", "119"); + private final CardRun uncommonA = new CardRun(true, "47", "6", "169", "246", "168", "117", "68", "243", "181", "115", "231", "2", "163", "73", "227", "207", "86", "254", "137", "37", "242", "184", "255", "121", "148", "176", "26", "111", "138", "235", "196", "43", "117", "140", "6", "197", "1", "246", "47", "162", "169", "30", "168", "181", "243", "254", "2", "182", "151", "86", "68", "22", "184", "137", "73", "197", "163", "231", "138", "26", "176", "115", "148", "242", "37", "111", "168", "6", "196", "1", "227", "255", "121", "43", "207", "140", "30", "117", "254", "182", "137", "235", "26", "162", "47", "169", "151", "86", "176", "68", "243", "138", "181", "2", "73", "148", "196", "246", "37", "115", "163", "231", "207", "151", "22", "121", "184", "227", "255", "1", "111", "162", "197", "43", "242", "30", "182", "140", "235", "22"); + private final CardRun uncommonB = new CardRun(true, "222", "49", "145", "216", "125", "178", "78", "215", "114", "241", "221", "157", "77", "224", "206", "98", "55", "213", "14", "92", "211", "110", "236", "223", "72", "116", "74", "167", "217", "96", "61", "220", "175", "114", "59", "222", "145", "125", "173", "215", "152", "78", "5", "98", "216", "178", "77", "92", "221", "157", "241", "224", "96", "49", "13", "223", "20", "167", "213", "110", "55", "29", "14", "217", "236", "152", "78", "220", "114", "5", "74", "211", "206", "59", "215", "173", "125", "216", "49", "20", "222", "13", "116", "72", "175", "241", "92", "221", "29", "61", "224", "145", "98", "178", "213", "77", "217", "14", "55", "157", "220", "5", "74", "96", "206", "72", "20", "236", "110", "173", "223", "59", "167", "175", "116", "211", "13", "61", "29", "152"); + private final CardRun rare = new CardRun(false, "4", "9", "17", "21", "23", "27", "33", "39", "42", "52", "60", "62", "63", "66", "67", "69", "76", "84", "90", "91", "97", "99", "102", "104", "107", "112", "113", "128", "130", "134", "135", "136", "144", "149", "155", "160", "179", "185", "186", "189", "192", "194", "200", "203", "209", "226", "228", "230", "232", "238", "244", "247", "249", "4", "9", "17", "21", "23", "27", "33", "39", "42", "52", "60", "62", "63", "66", "67", "69", "76", "84", "90", "91", "97", "99", "102", "104", "107", "112", "113", "128", "130", "134", "135", "136", "144", "149", "155", "160", "179", "185", "186", "189", "192", "194", "200", "203", "209", "226", "228", "230", "232", "238", "244", "247", "249", "3", "34", "65", "79", "88", "106", "129", "154", "201", "208", "212", "214", "218", "219", "225", "229"); + private final CardRun landBasic = new CardRun(false, "261", "262", "263", "264", "265", "266", "267", "268", "269", "270", "271", "272", "273", "274", "275", "276", "277", "278", "279", "280"); + private final CardRun landNonBasic = new CardRun(false, "248", "250", "251", "252", "253", "256", "257", "258", "259", "260"); + + private final BoosterStructure AABBC1C1C1C1C1C1 = new BoosterStructure( + commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AAABBC1C1C1C1C1 = new BoosterStructure( + commonA, commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AAAABBC2C2C2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, + commonC2, commonC2, commonC2, commonC2 + ); + private final BoosterStructure AAAABBBC2C2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, commonB, + commonC2, commonC2, commonC2 + ); + private final BoosterStructure AAAABBBBC2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, commonB, commonB, + commonC2, commonC2 + ); + private final BoosterStructure AAB = new BoosterStructure(uncommonA, uncommonA, uncommonB); + private final BoosterStructure ABB = new BoosterStructure(uncommonA, uncommonB, uncommonB); + private final BoosterStructure R1 = new BoosterStructure(rare); + private final BoosterStructure LB = new BoosterStructure(landBasic); + private final BoosterStructure LN = new BoosterStructure(landNonBasic); + + // In order for equal numbers of each common to exist, the average booster must contain: + // 3.27 A commons (36 / 11) + // 2.18 B commons (24 / 11) + // 2.73 C1 commons (30 / 11, or 60 / 11 in each C1 booster) + // 1.82 C2 commons (20 / 11, or 40 / 11 in each C2 booster) + // These numbers are the same for all sets with 101 commons in A/B/C1/C2 print runs + // and with 10 common slots per booster + private final RarityConfiguration commonRuns = new RarityConfiguration( + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBBC2C2C2, + AAAABBBC2C2C2, + AAAABBBBC2C2 + ); + private final RarityConfiguration uncommonRuns = new RarityConfiguration(AAB, ABB); + private final RarityConfiguration rareRuns = new RarityConfiguration(R1); + private final RarityConfiguration landRuns = new RarityConfiguration( + LB, LB, LB, LB, LB, LB, LB, + LN, LN, LN, LN, LN + ); + + @Override + public List makeBooster() { + List booster = new ArrayList<>(); + booster.addAll(commonRuns.getNext().makeRun()); + booster.addAll(uncommonRuns.getNext().makeRun()); + booster.addAll(rareRuns.getNext().makeRun()); + booster.addAll(landRuns.getNext().makeRun()); + return booster; } } diff --git a/Mage.Sets/src/mage/sets/CoreSet2020.java b/Mage.Sets/src/mage/sets/CoreSet2020.java index 6099b875cbc..ce211f1d633 100644 --- a/Mage.Sets/src/mage/sets/CoreSet2020.java +++ b/Mage.Sets/src/mage/sets/CoreSet2020.java @@ -1,10 +1,10 @@ package mage.sets; import mage.cards.ExpansionSet; -import mage.cards.repository.CardCriteria; -import mage.cards.repository.CardInfo; -import mage.cards.repository.CardRepository; -import mage.constants.CardType; +import mage.collation.BoosterCollator; +import mage.collation.BoosterStructure; +import mage.collation.CardRun; +import mage.collation.RarityConfiguration; import mage.constants.Rarity; import mage.constants.SetType; @@ -22,13 +22,10 @@ public final class CoreSet2020 extends ExpansionSet { return instance; } - private final List savedSpecialLand = new ArrayList<>(); - private CoreSet2020() { super("Core Set 2020", "M20", ExpansionSet.buildDate(2019, 7, 12), SetType.CORE); this.hasBoosters = true; this.hasBasicLands = true; - this.numBoosterSpecial = 0; this.numBoosterLands = 1; this.numBoosterCommon = 10; this.numBoosterUncommon = 3; @@ -36,10 +33,10 @@ public final class CoreSet2020 extends ExpansionSet { this.ratioBoosterMythic = 8; this.maxCardNumberInBooster = 280; - // Core 2020 boosters have a 5/12 chance of basic land being replaced + // Core 2020 boosters have a 11/24 chance of basic land being replaced // with the common taplands, which DO NOT appear in the common slot. - this.ratioBoosterSpecialLand = 12; - this.ratioBoosterSpecialLandNumerator = 5; + this.ratioBoosterSpecialLand = 24; + this.ratioBoosterSpecialLandNumerator = 11; cards.add(new SetCardInfo("Act of Treason", 124, Rarity.COMMON, mage.cards.a.ActOfTreason.class)); cards.add(new SetCardInfo("Aerial Assault", 1, Rarity.COMMON, mage.cards.a.AerialAssault.class)); @@ -389,38 +386,102 @@ public final class CoreSet2020 extends ExpansionSet { } @Override - public List getCardsByRarity(Rarity rarity) { - // Common cards retrievement of Core Set 2020 boosters - prevent the retrievement of the common lands - if (rarity == Rarity.COMMON) { - List savedCardsInfos = savedCards.get(rarity); - if (savedCardsInfos == null) { - CardCriteria criteria = new CardCriteria(); - criteria.rarities(Rarity.COMMON); - criteria.setCodes(this.code).notTypes(CardType.LAND); - savedCardsInfos = CardRepository.instance.findCards(criteria); - if (maxCardNumberInBooster != Integer.MAX_VALUE) { - savedCardsInfos.removeIf(next -> next.getCardNumberAsInt() > maxCardNumberInBooster); - } - savedCards.put(rarity, savedCardsInfos); - } - // Return a copy of the saved cards information, as not to let modify the original. - return new ArrayList<>(savedCardsInfos); - } else { - return super.getCardsByRarity(rarity); - } - } - - @Override - // the common taplands replacing the basic land - public List getSpecialLand() { - if (savedSpecialLand.isEmpty()) { - CardCriteria criteria = new CardCriteria(); - criteria.setCodes(this.code); - criteria.rarities(Rarity.COMMON); - criteria.types(CardType.LAND); - savedSpecialLand.addAll(CardRepository.instance.findCards(criteria)); - } - - return new ArrayList<>(savedSpecialLand); + public BoosterCollator createCollator() { + return new CoreSet2020Collator(); + } +} + +// Booster collation info from https://www.lethe.xyz/mtg/collation/m20.html +// Using USA collation for all rarities +// Foil rare sheet used for regular rares as regular rare sheet is not known +class CoreSet2020Collator implements BoosterCollator { + private final CardRun commonA = new CardRun(true, "154", "34", "69", "155", "38", "61", "124", "25", "45", "145", "31", "58", "157", "14", "67", "138", "5", "66", "146", "1", "73", "155", "30", "69", "142", "12", "60", "159", "24", "70", "147", "31", "55", "144", "34", "48", "145", "40", "61", "154", "25", "67", "124", "38", "58", "146", "5", "60", "142", "1", "55", "144", "12", "66", "157", "14", "45", "138", "24", "70", "159", "40", "73", "147", "30", "48"); + private final CardRun commonB = new CardRun(true, "183", "85", "174", "88", "196", "101", "201", "99", "171", "112", "173", "100", "170", "97", "203", "92", "166", "84", "188", "101", "181", "116", "196", "88", "174", "86", "183", "85", "173", "112", "166", "99", "203", "100", "170", "84", "201", "101", "171", "85", "188", "92", "174", "97", "183", "86", "181", "116", "196", "88", "201", "99", "170", "100", "171", "84", "203", "112", "173", "97", "166", "92", "188", "116", "181", "86"); + private final CardRun commonC1 = new CardRun(true, "20", "108", "54", "161", "192", "241", "82", "178", "7", "160", "176", "231", "134", "75", "41", "133", "221", "195", "102", "161", "80", "108", "192", "82", "21", "175", "152", "37", "241", "83", "176", "54", "134", "20", "240", "178", "62", "133", "119", "231", "75", "41", "102", "175", "160", "240", "195", "7", "80", "119", "21", "83", "62", "152", "37"); + private final CardRun commonC2 = new CardRun(true, "184", "118", "129", "235", "32", "47", "168", "114", "93", "149", "49", "190", "228", "130", "18", "78", "114", "184", "47", "109", "11", "129", "168", "235", "32", "118", "49", "149", "93", "11", "190", "228", "130", "78", "235", "184", "47", "109", "221", "129", "18", "49", "149", "168", "93", "118", "32", "11", "130", "228", "190", "114", "78", "18", "109"); + private final CardRun uncommonA = new CardRun(true, "4", "198", "139", "19", "120", "215", "51", "232", "189", "128", "15", "89", "207", "234", "74", "197", "153", "16", "87", "209", "79", "177", "244", "198", "140", "28", "104", "213", "4", "44", "204", "164", "23", "95", "208", "50", "223", "180", "163", "29", "89", "215", "71", "103", "189", "128", "15", "120", "207", "197", "232", "74", "139", "19", "87", "209", "51", "23", "180", "153", "16", "104", "213", "79", "244", "177", "140", "28", "95", "208", "204", "234", "44", "164", "29", "103", "215", "50", "223", "189", "163", "19", "89", "207", "71", "232", "198", "139", "4", "120", "209", "51", "15", "197", "128", "16", "87", "153", "79", "234", "180", "140", "28", "103", "213", "50", "244", "177", "164", "23", "104", "208", "71", "223", "204", "163", "74", "95", "29", "44"); + private final CardRun uncommonB = new CardRun(true, "123", "205", "63", "214", "158", "76", "237", "110", "165", "17", "217", "151", "72", "236", "187", "117", "3", "206", "137", "53", "230", "90", "191", "6", "91", "143", "238", "81", "224", "110", "182", "35", "218", "132", "76", "225", "121", "186", "13", "219", "151", "42", "237", "123", "205", "3", "63", "158", "214", "236", "90", "165", "17", "217", "135", "81", "230", "6", "187", "117", "206", "143", "72", "238", "91", "191", "13", "218", "137", "53", "224", "121", "182", "35", "214", "132", "76", "225", "110", "186", "3", "205", "158", "42", "236", "123", "165", "6", "63", "135", "219", "237", "90", "187", "17", "217", "151", "81", "230", "182", "117", "137", "206", "143", "72", "238", "121", "191", "13", "218", "132", "53", "224", "91", "186", "35", "219", "135", "42", "225"); + private final CardRun rare = new CardRun(true, "8", "96", "126", "210", "169", "43", "233", "10", "148", "57", "247", "9", "98", "131", "2", "172", "46", "107", "200", "150", "59", "249", "22", "105", "136", "52", "229", "56", "111", "202", "156", "64", "253", "26", "106", "222", "211", "185", "8", "113", "68", "162", "65", "254", "27", "57", "200", "94", "247", "9", "122", "193", "239", "77", "255", "33", "59", "202", "125", "249", "22", "126", "194", "96", "222", "256", "36", "64", "107", "212", "253", "26", "131", "115", "98", "226", "257", "39", "65", "111", "167", "169", "27", "136", "179", "105", "148", "254", "43", "77", "113", "216", "172", "33", "141", "227", "106", "150", "255", "46", "141", "122", "127", "179", "36", "233", "199", "193", "156", "256", "56", "226", "239", "220", "185", "39", "229", "227", "194", "162", "257"); + private final CardRun landBasic = new CardRun(false, "261", "262", "263", "264", "265", "266", "267", "268", "269", "270", "271", "272", "273", "274", "275", "276", "277", "278", "279", "280"); + private final CardRun landNonBasic = new CardRun(false, "242", "243", "245", "246", "248", "250", "251", "252", "258", "259", "260"); + + private final BoosterStructure AABBC1C1C1C1C1C1 = new BoosterStructure( + commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AAABBC1C1C1C1C1 = new BoosterStructure( + commonA, commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AAAABBC2C2C2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, + commonC2, commonC2, commonC2, commonC2 + ); + private final BoosterStructure AAAABBBC2C2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, commonB, + commonC2, commonC2, commonC2 + ); + private final BoosterStructure AAAABBBBC2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, commonB, commonB, + commonC2, commonC2 + ); + private final BoosterStructure AAB = new BoosterStructure(uncommonA, uncommonA, uncommonB); + private final BoosterStructure ABB = new BoosterStructure(uncommonA, uncommonB, uncommonB); + private final BoosterStructure R1 = new BoosterStructure(rare); + private final BoosterStructure LB = new BoosterStructure(landBasic); + private final BoosterStructure LN = new BoosterStructure(landNonBasic); + + // In order for equal numbers of each common to exist, the average booster must contain: + // 3.27 A commons (36 / 11) + // 2.18 B commons (24 / 11) + // 2.73 C1 commons (30 / 11, or 60 / 11 in each C1 booster) + // 1.82 C2 commons (20 / 11, or 40 / 11 in each C2 booster) + // These numbers are the same for all sets with 101 commons in A/B/C1/C2 print runs + // and with 10 common slots per booster + private final RarityConfiguration commonRuns = new RarityConfiguration( + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBBC2C2C2, + AAAABBBC2C2C2, + AAAABBBBC2C2 + ); + private final RarityConfiguration uncommonRuns = new RarityConfiguration(AAB, ABB); + private final RarityConfiguration rareRuns = new RarityConfiguration(R1); + private final RarityConfiguration landRuns = new RarityConfiguration( + LB, LB, LB, LB, LB, LB, LB, LB, LB, LB, LB, LB, LB, + LN, LN, LN, LN, LN, LN, LN, LN, LN, LN, LN + ); + + @Override + public List makeBooster() { + List booster = new ArrayList<>(); + booster.addAll(commonRuns.getNext().makeRun()); + booster.addAll(uncommonRuns.getNext().makeRun()); + booster.addAll(rareRuns.getNext().makeRun()); + booster.addAll(landRuns.getNext().makeRun()); + return booster; } } diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java index 0db411aaa9c..bb377fdf999 100644 --- a/Mage.Sets/src/mage/sets/CoreSet2021.java +++ b/Mage.Sets/src/mage/sets/CoreSet2021.java @@ -1,14 +1,10 @@ package mage.sets; import mage.cards.ExpansionSet; -import mage.cards.repository.CardCriteria; import mage.cards.repository.CardInfo; -import mage.cards.repository.CardRepository; -import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.SetType; -import java.util.ArrayList; import java.util.List; /** @@ -22,13 +18,10 @@ public final class CoreSet2021 extends ExpansionSet { return instance; } - private final List savedSpecialLand = new ArrayList<>(); - private CoreSet2021() { super("Core Set 2021", "M21", ExpansionSet.buildDate(2020, 7, 3), SetType.CORE); this.hasBoosters = true; this.hasBasicLands = true; - this.numBoosterSpecial = 0; this.numBoosterLands = 1; this.numBoosterCommon = 10; this.numBoosterUncommon = 3; @@ -437,43 +430,13 @@ public final class CoreSet2021 extends ExpansionSet { cards.add(new SetCardInfo("Witch's Cauldron", 129, Rarity.UNCOMMON, mage.cards.w.WitchsCauldron.class)); } - @Override - public List getCardsByRarity(Rarity rarity) { - if (rarity != Rarity.COMMON) { - return super.getCardsByRarity(rarity); + protected List findSpecialCardsByRarity(Rarity rarity) { + List cardInfos = super.findSpecialCardsByRarity(rarity); + if (rarity == Rarity.LAND) { + // Radiant Fountain is a normal common + cardInfos.removeIf(cardInfo -> "Radiant Fountain".equals(cardInfo.getName())); } - List savedCardsInfos = savedCards.get(rarity); - if (savedCardsInfos != null) { - return new ArrayList(savedCardsInfos); - } - CardCriteria criteria = new CardCriteria(); - criteria.setCodes(this.code).notTypes(CardType.LAND); - criteria.rarities(rarity).doubleFaced(false); - savedCardsInfos = CardRepository.instance.findCards(criteria); - if (maxCardNumberInBooster != Integer.MAX_VALUE) { - savedCardsInfos.removeIf(next -> next.getCardNumberAsInt() > maxCardNumberInBooster); - } - criteria = new CardCriteria(); - criteria.setCodes(this.code).nameExact("Radiant Fountain"); - savedCardsInfos.addAll(CardRepository.instance.findCards(criteria)); - savedCards.put(rarity, savedCardsInfos); - // Return a copy of the saved cards information, as not to modify the original. - return new ArrayList(savedCardsInfos); - } - - @Override - // the common taplands replacing the basic land - public List getSpecialLand() { - if (savedSpecialLand.isEmpty()) { - CardCriteria criteria = new CardCriteria(); - criteria.setCodes(this.code); - criteria.rarities(Rarity.COMMON); - criteria.types(CardType.LAND); - savedSpecialLand.addAll(CardRepository.instance.findCards(criteria)); - savedSpecialLand.removeIf(cardInfo -> "Radiant Fountain".equals(cardInfo.getName())); - } - - return new ArrayList<>(savedSpecialLand); + return cardInfos; } } diff --git a/Mage.Sets/src/mage/sets/CrimsonVowCommander.java b/Mage.Sets/src/mage/sets/CrimsonVowCommander.java new file mode 100644 index 00000000000..c07dbcfa52b --- /dev/null +++ b/Mage.Sets/src/mage/sets/CrimsonVowCommander.java @@ -0,0 +1,177 @@ +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * @author TheElk801 + */ +public final class CrimsonVowCommander extends ExpansionSet { + + private static final CrimsonVowCommander instance = new CrimsonVowCommander(); + + public static CrimsonVowCommander getInstance() { + return instance; + } + + private CrimsonVowCommander() { + super("Crimson Vow Commander", "VOC", ExpansionSet.buildDate(2021, 11, 19), SetType.SUPPLEMENTAL); + this.hasBasicLands = false; + + cards.add(new SetCardInfo("Ancient Craving", 117, Rarity.UNCOMMON, mage.cards.a.AncientCraving.class)); + cards.add(new SetCardInfo("Angel of Flight Alabaster", 77, Rarity.RARE, mage.cards.a.AngelOfFlightAlabaster.class)); + cards.add(new SetCardInfo("Anje's Ravager", 141, Rarity.RARE, mage.cards.a.AnjesRavager.class)); + cards.add(new SetCardInfo("Anowon, the Ruin Sage", 118, Rarity.RARE, mage.cards.a.AnowonTheRuinSage.class)); + cards.add(new SetCardInfo("Arcane Denial", 102, Rarity.COMMON, mage.cards.a.ArcaneDenial.class)); + cards.add(new SetCardInfo("Arcane Signet", 159, Rarity.COMMON, mage.cards.a.ArcaneSignet.class)); + cards.add(new SetCardInfo("Arterial Alchemy", 23, Rarity.RARE, mage.cards.a.ArterialAlchemy.class)); + cards.add(new SetCardInfo("Avacyn's Judgment", 142, Rarity.RARE, mage.cards.a.AvacynsJudgment.class)); + cards.add(new SetCardInfo("Azorius Chancery", 171, Rarity.UNCOMMON, mage.cards.a.AzoriusChancery.class)); + cards.add(new SetCardInfo("Azorius Locket", 160, Rarity.COMMON, mage.cards.a.AzoriusLocket.class)); + cards.add(new SetCardInfo("Azorius Signet", 161, Rarity.UNCOMMON, mage.cards.a.AzoriusSignet.class)); + cards.add(new SetCardInfo("Benevolent Offering", 78, Rarity.RARE, mage.cards.b.BenevolentOffering.class)); + cards.add(new SetCardInfo("Blasphemous Act", 143, Rarity.RARE, mage.cards.b.BlasphemousAct.class)); + cards.add(new SetCardInfo("Blood Artist", 119, Rarity.UNCOMMON, mage.cards.b.BloodArtist.class)); + cards.add(new SetCardInfo("Bloodline Necromancer", 120, Rarity.UNCOMMON, mage.cards.b.BloodlineNecromancer.class)); + cards.add(new SetCardInfo("Bloodlord of Vaasgoth", 121, Rarity.MYTHIC, mage.cards.b.BloodlordOfVaasgoth.class)); + cards.add(new SetCardInfo("Bloodsworn Steward", 144, Rarity.RARE, mage.cards.b.BloodswornSteward.class)); + cards.add(new SetCardInfo("Bloodtracker", 122, Rarity.RARE, mage.cards.b.Bloodtracker.class)); + cards.add(new SetCardInfo("Boreas Charger", 79, Rarity.RARE, mage.cards.b.BoreasCharger.class)); + cards.add(new SetCardInfo("Breath of the Sleepless", 11, Rarity.RARE, mage.cards.b.BreathOfTheSleepless.class)); + cards.add(new SetCardInfo("Breathkeeper Seraph", 31, Rarity.RARE, mage.cards.b.BreathkeeperSeraph.class)); + cards.add(new SetCardInfo("Butcher of Malakir", 123, Rarity.RARE, mage.cards.b.ButcherOfMalakir.class)); + cards.add(new SetCardInfo("Bygone Bishop", 80, Rarity.RARE, mage.cards.b.BygoneBishop.class)); + cards.add(new SetCardInfo("Champion of Dusk", 124, Rarity.RARE, mage.cards.c.ChampionOfDusk.class)); + cards.add(new SetCardInfo("Charcoal Diamond", 162, Rarity.COMMON, mage.cards.c.CharcoalDiamond.class)); + cards.add(new SetCardInfo("Command Tower", 172, Rarity.COMMON, mage.cards.c.CommandTower.class)); + cards.add(new SetCardInfo("Commander's Sphere", 163, Rarity.COMMON, mage.cards.c.CommandersSphere.class)); + cards.add(new SetCardInfo("Cordial Vampire", 125, Rarity.RARE, mage.cards.c.CordialVampire.class)); + cards.add(new SetCardInfo("Crimson Honor Guard", 145, Rarity.RARE, mage.cards.c.CrimsonHonorGuard.class)); + cards.add(new SetCardInfo("Crossway Troublemakers", 17, Rarity.RARE, mage.cards.c.CrosswayTroublemakers.class)); + cards.add(new SetCardInfo("Crush Contraband", 81, Rarity.UNCOMMON, mage.cards.c.CrushContraband.class)); + cards.add(new SetCardInfo("Custodi Soulbinders", 82, Rarity.RARE, mage.cards.c.CustodiSoulbinders.class)); + cards.add(new SetCardInfo("Custodi Squire", 83, Rarity.COMMON, mage.cards.c.CustodiSquire.class)); + cards.add(new SetCardInfo("Damnable Pact", 126, Rarity.RARE, mage.cards.d.DamnablePact.class)); + cards.add(new SetCardInfo("Dark Impostor", 127, Rarity.RARE, mage.cards.d.DarkImpostor.class)); + cards.add(new SetCardInfo("Darksteel Mutation", 84, Rarity.UNCOMMON, mage.cards.d.DarksteelMutation.class)); + cards.add(new SetCardInfo("Disorder in the Court", 29, Rarity.RARE, mage.cards.d.DisorderInTheCourt.class)); + cards.add(new SetCardInfo("Distant Melody", 103, Rarity.COMMON, mage.cards.d.DistantMelody.class)); + cards.add(new SetCardInfo("Donal, Herald of Wings", 3, Rarity.MYTHIC, mage.cards.d.DonalHeraldOfWings.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Donal, Herald of Wings", 41, Rarity.MYTHIC, mage.cards.d.DonalHeraldOfWings.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Doom Weaver", 34, Rarity.RARE, mage.cards.d.DoomWeaver.class)); + cards.add(new SetCardInfo("Dovin, Grand Arbiter", 153, Rarity.MYTHIC, mage.cards.d.DovinGrandArbiter.class)); + cards.add(new SetCardInfo("Drogskol Captain", 154, Rarity.UNCOMMON, mage.cards.d.DrogskolCaptain.class)); + cards.add(new SetCardInfo("Drogskol Reinforcements", 5, Rarity.RARE, mage.cards.d.DrogskolReinforcements.class)); + cards.add(new SetCardInfo("Ethereal Investigator", 12, Rarity.RARE, mage.cards.e.EtherealInvestigator.class)); + cards.add(new SetCardInfo("Exotic Orchard", 173, Rarity.RARE, mage.cards.e.ExoticOrchard.class)); + cards.add(new SetCardInfo("Falkenrath Gorger", 146, Rarity.RARE, mage.cards.f.FalkenrathGorger.class)); + cards.add(new SetCardInfo("Falkenrath Noble", 128, Rarity.UNCOMMON, mage.cards.f.FalkenrathNoble.class)); + cards.add(new SetCardInfo("Feed the Swarm", 129, Rarity.COMMON, mage.cards.f.FeedTheSwarm.class)); + cards.add(new SetCardInfo("Fell the Mighty", 85, Rarity.RARE, mage.cards.f.FellTheMighty.class)); + cards.add(new SetCardInfo("Field of Souls", 86, Rarity.UNCOMMON, mage.cards.f.FieldOfSouls.class)); + cards.add(new SetCardInfo("Fire Diamond", 164, Rarity.COMMON, mage.cards.f.FireDiamond.class)); + cards.add(new SetCardInfo("Flood of Tears", 104, Rarity.RARE, mage.cards.f.FloodOfTears.class)); + cards.add(new SetCardInfo("Foreboding Ruins", 174, Rarity.RARE, mage.cards.f.ForebodingRuins.class)); + cards.add(new SetCardInfo("Geist of Saint Traft", 155, Rarity.MYTHIC, mage.cards.g.GeistOfSaintTraft.class)); + cards.add(new SetCardInfo("Ghostly Pilferer", 105, Rarity.RARE, mage.cards.g.GhostlyPilferer.class)); + cards.add(new SetCardInfo("Ghostly Prison", 87, Rarity.UNCOMMON, mage.cards.g.GhostlyPrison.class)); + cards.add(new SetCardInfo("Glass-Cast Heart", 18, Rarity.RARE, mage.cards.g.GlassCastHeart.class)); + cards.add(new SetCardInfo("Hallowed Spiritkeeper", 88, Rarity.RARE, mage.cards.h.HallowedSpiritkeeper.class)); + cards.add(new SetCardInfo("Hanged Executioner", 89, Rarity.RARE, mage.cards.h.HangedExecutioner.class)); + cards.add(new SetCardInfo("Haunted Library", 6, Rarity.RARE, mage.cards.h.HauntedLibrary.class)); + cards.add(new SetCardInfo("Haunting Imitation", 13, Rarity.RARE, mage.cards.h.HauntingImitation.class)); + cards.add(new SetCardInfo("Hollowhenge Overlord", 36, Rarity.RARE, mage.cards.h.HollowhengeOverlord.class)); + cards.add(new SetCardInfo("Imperious Mindbreaker", 33, Rarity.RARE, mage.cards.i.ImperiousMindbreaker.class)); + cards.add(new SetCardInfo("Imposing Grandeur", 24, Rarity.RARE, mage.cards.i.ImposingGrandeur.class)); + cards.add(new SetCardInfo("Imprisoned in the Moon", 106, Rarity.RARE, mage.cards.i.ImprisonedInTheMoon.class)); + cards.add(new SetCardInfo("Indulgent Aristocrat", 130, Rarity.UNCOMMON, mage.cards.i.IndulgentAristocrat.class)); + cards.add(new SetCardInfo("Kamber, the Plunderer", 19, Rarity.RARE, mage.cards.k.KamberThePlunderer.class)); + cards.add(new SetCardInfo("Kami of the Crescent Moon", 107, Rarity.RARE, mage.cards.k.KamiOfTheCrescentMoon.class)); + cards.add(new SetCardInfo("Karmic Guide", 90, Rarity.RARE, mage.cards.k.KarmicGuide.class)); + cards.add(new SetCardInfo("Kirtar's Wrath", 91, Rarity.RARE, mage.cards.k.KirtarsWrath.class)); + cards.add(new SetCardInfo("Knight of the White Orchid", 92, Rarity.RARE, mage.cards.k.KnightOfTheWhiteOrchid.class)); + cards.add(new SetCardInfo("Laurine, the Diversion", 25, Rarity.RARE, mage.cards.l.LaurineTheDiversion.class)); + cards.add(new SetCardInfo("Malakir Bloodwitch", 131, Rarity.RARE, mage.cards.m.MalakirBloodwitch.class)); + cards.add(new SetCardInfo("Marble Diamond", 165, Rarity.COMMON, mage.cards.m.MarbleDiamond.class)); + cards.add(new SetCardInfo("Markov Enforcer", 26, Rarity.RARE, mage.cards.m.MarkovEnforcer.class)); + cards.add(new SetCardInfo("Mentor of the Meek", 93, Rarity.RARE, mage.cards.m.MentorOfTheMeek.class)); + cards.add(new SetCardInfo("Midnight Arsonist", 27, Rarity.RARE, mage.cards.m.MidnightArsonist.class)); + cards.add(new SetCardInfo("Midnight Clock", 108, Rarity.RARE, mage.cards.m.MidnightClock.class)); + cards.add(new SetCardInfo("Millicent, Restless Revenant", 1, Rarity.MYTHIC, mage.cards.m.MillicentRestlessRevenant.class)); + cards.add(new SetCardInfo("Mirage Phalanx", 35, Rarity.RARE, mage.cards.m.MiragePhalanx.class)); + cards.add(new SetCardInfo("Mirror Entity", 94, Rarity.RARE, mage.cards.m.MirrorEntity.class)); + cards.add(new SetCardInfo("Mob Rule", 147, Rarity.RARE, mage.cards.m.MobRule.class)); + cards.add(new SetCardInfo("Molten Echoes", 148, Rarity.RARE, mage.cards.m.MoltenEchoes.class)); + cards.add(new SetCardInfo("Moorland Haunt", 175, Rarity.RARE, mage.cards.m.MoorlandHaunt.class)); + cards.add(new SetCardInfo("Myriad Landscape", 176, Rarity.UNCOMMON, mage.cards.m.MyriadLandscape.class)); + cards.add(new SetCardInfo("Nebelgast Herald", 109, Rarity.UNCOMMON, mage.cards.n.NebelgastHerald.class)); + cards.add(new SetCardInfo("Necropolis Regent", 132, Rarity.MYTHIC, mage.cards.n.NecropolisRegent.class)); + cards.add(new SetCardInfo("Night's Whisper", 133, Rarity.COMMON, mage.cards.n.NightsWhisper.class)); + cards.add(new SetCardInfo("Nirkana Revenant", 134, Rarity.MYTHIC, mage.cards.n.NirkanaRevenant.class)); + cards.add(new SetCardInfo("Occult Epiphany", 14, Rarity.RARE, mage.cards.o.OccultEpiphany.class)); + cards.add(new SetCardInfo("Olivia's Wrath", 20, Rarity.RARE, mage.cards.o.OliviasWrath.class)); + cards.add(new SetCardInfo("Oyobi, Who Split the Heavens", 95, Rarity.RARE, mage.cards.o.OyobiWhoSplitTheHeavens.class)); + cards.add(new SetCardInfo("Path of Ancestry", 177, Rarity.COMMON, mage.cards.p.PathOfAncestry.class)); + cards.add(new SetCardInfo("Patron of the Vein", 135, Rarity.RARE, mage.cards.p.PatronOfTheVein.class)); + cards.add(new SetCardInfo("Port Town", 178, Rarity.RARE, mage.cards.p.PortTown.class)); + cards.add(new SetCardInfo("Prairie Stream", 179, Rarity.RARE, mage.cards.p.PrairieStream.class)); + cards.add(new SetCardInfo("Predators' Hour", 21, Rarity.RARE, mage.cards.p.PredatorsHour.class)); + cards.add(new SetCardInfo("Priest of the Blessed Graf", 7, Rarity.RARE, mage.cards.p.PriestOfTheBlessedGraf.class)); + cards.add(new SetCardInfo("Promise of Bunrei", 96, Rarity.RARE, mage.cards.p.PromiseOfBunrei.class)); + cards.add(new SetCardInfo("Rakdos Carnarium", 180, Rarity.UNCOMMON, mage.cards.r.RakdosCarnarium.class)); + cards.add(new SetCardInfo("Rakdos Charm", 156, Rarity.UNCOMMON, mage.cards.r.RakdosCharm.class)); + cards.add(new SetCardInfo("Rakdos Signet", 166, Rarity.UNCOMMON, mage.cards.r.RakdosSignet.class)); + cards.add(new SetCardInfo("Rakish Heir", 149, Rarity.UNCOMMON, mage.cards.r.RakishHeir.class)); + cards.add(new SetCardInfo("Rattlechains", 110, Rarity.RARE, mage.cards.r.Rattlechains.class)); + cards.add(new SetCardInfo("Reconnaissance Mission", 111, Rarity.UNCOMMON, mage.cards.r.ReconnaissanceMission.class)); + cards.add(new SetCardInfo("Remorseful Cleric", 97, Rarity.RARE, mage.cards.r.RemorsefulCleric.class)); + cards.add(new SetCardInfo("Rhoda, Geist Avenger", 8, Rarity.RARE, mage.cards.r.RhodaGeistAvenger.class)); + cards.add(new SetCardInfo("Sanctum Seeker", 136, Rarity.RARE, mage.cards.s.SanctumSeeker.class)); + cards.add(new SetCardInfo("Scion of Opulence", 28, Rarity.RARE, mage.cards.s.ScionOfOpulence.class)); + cards.add(new SetCardInfo("Shacklegeist", 112, Rarity.RARE, mage.cards.s.Shacklegeist.class)); + cards.add(new SetCardInfo("Shadowblood Ridge", 181, Rarity.RARE, mage.cards.s.ShadowbloodRidge.class)); + cards.add(new SetCardInfo("Shadowgrange Archfiend", 22, Rarity.RARE, mage.cards.s.ShadowgrangeArchfiend.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shadowgrange Archfiend", 60, Rarity.RARE, mage.cards.s.ShadowgrangeArchfiend.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sinister Waltz", 30, Rarity.RARE, mage.cards.s.SinisterWaltz.class)); + cards.add(new SetCardInfo("Sire of the Storm", 113, Rarity.UNCOMMON, mage.cards.s.SireOfTheStorm.class)); + cards.add(new SetCardInfo("Sky Diamond", 167, Rarity.COMMON, mage.cards.s.SkyDiamond.class)); + cards.add(new SetCardInfo("Skycloud Expanse", 182, Rarity.RARE, mage.cards.s.SkycloudExpanse.class)); + cards.add(new SetCardInfo("Smoldering Marsh", 183, Rarity.RARE, mage.cards.s.SmolderingMarsh.class)); + cards.add(new SetCardInfo("Sol Ring", 168, Rarity.UNCOMMON, mage.cards.s.SolRing.class)); + cards.add(new SetCardInfo("Spectral Sailor", 114, Rarity.UNCOMMON, mage.cards.s.SpectralSailor.class)); + cards.add(new SetCardInfo("Spectral Shepherd", 98, Rarity.UNCOMMON, mage.cards.s.SpectralShepherd.class)); + cards.add(new SetCardInfo("Stensia Masquerade", 150, Rarity.UNCOMMON, mage.cards.s.StensiaMasquerade.class)); + cards.add(new SetCardInfo("Storm of Souls", 9, Rarity.RARE, mage.cards.s.StormOfSouls.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Storm of Souls", 47, Rarity.RARE, mage.cards.s.StormOfSouls.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Strefan, Maurer Progenitor", 2, Rarity.MYTHIC, mage.cards.s.StrefanMaurerProgenitor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Strefan, Maurer Progenitor", 40, Rarity.MYTHIC, mage.cards.s.StrefanMaurerProgenitor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Stromkirk Captain", 157, Rarity.UNCOMMON, mage.cards.s.StromkirkCaptain.class)); + cards.add(new SetCardInfo("Stromkirk Condemned", 137, Rarity.RARE, mage.cards.s.StromkirkCondemned.class)); + cards.add(new SetCardInfo("Stromkirk Occultist", 151, Rarity.RARE, mage.cards.s.StromkirkOccultist.class)); + cards.add(new SetCardInfo("Sudden Salvation", 10, Rarity.RARE, mage.cards.s.SuddenSalvation.class)); + cards.add(new SetCardInfo("Supreme Phantom", 115, Rarity.RARE, mage.cards.s.SupremePhantom.class)); + cards.add(new SetCardInfo("Swiftfoot Boots", 169, Rarity.UNCOMMON, mage.cards.s.SwiftfootBoots.class)); + cards.add(new SetCardInfo("Swords to Plowshares", 99, Rarity.UNCOMMON, mage.cards.s.SwordsToPlowshares.class)); + cards.add(new SetCardInfo("Tainted Peak", 184, Rarity.UNCOMMON, mage.cards.t.TaintedPeak.class)); + cards.add(new SetCardInfo("Temple of Enlightenment", 185, Rarity.RARE, mage.cards.t.TempleOfEnlightenment.class)); + cards.add(new SetCardInfo("Temple of Malice", 186, Rarity.RARE, mage.cards.t.TempleOfMalice.class)); + cards.add(new SetCardInfo("Temple of the False God", 187, Rarity.UNCOMMON, mage.cards.t.TempleOfTheFalseGod.class)); + cards.add(new SetCardInfo("Thundering Mightmare", 37, Rarity.RARE, mage.cards.t.ThunderingMightmare.class)); + cards.add(new SetCardInfo("Timin, Youthful Geist", 16, Rarity.RARE, mage.cards.t.TiminYouthfulGeist.class)); + cards.add(new SetCardInfo("Timothar, Baron of Bats", 4, Rarity.MYTHIC, mage.cards.t.TimotharBaronOfBats.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Timothar, Baron of Bats", 42, Rarity.MYTHIC, mage.cards.t.TimotharBaronOfBats.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Twilight Drover", 100, Rarity.RARE, mage.cards.t.TwilightDrover.class)); + cards.add(new SetCardInfo("Umbris, Fear Manifest", 38, Rarity.MYTHIC, mage.cards.u.UmbrisFearManifest.class)); + cards.add(new SetCardInfo("Unclaimed Territory", 188, Rarity.UNCOMMON, mage.cards.u.UnclaimedTerritory.class)); + cards.add(new SetCardInfo("Underworld Connections", 138, Rarity.RARE, mage.cards.u.UnderworldConnections.class)); + cards.add(new SetCardInfo("Unstable Obelisk", 170, Rarity.UNCOMMON, mage.cards.u.UnstableObelisk.class)); + cards.add(new SetCardInfo("Urge to Feed", 139, Rarity.UNCOMMON, mage.cards.u.UrgeToFeed.class)); + cards.add(new SetCardInfo("Vampire Nighthawk", 140, Rarity.UNCOMMON, mage.cards.v.VampireNighthawk.class)); + cards.add(new SetCardInfo("Vampiric Dragon", 158, Rarity.RARE, mage.cards.v.VampiricDragon.class)); + cards.add(new SetCardInfo("Vandalblast", 152, Rarity.UNCOMMON, mage.cards.v.Vandalblast.class)); + cards.add(new SetCardInfo("Verity Circle", 116, Rarity.RARE, mage.cards.v.VerityCircle.class)); + cards.add(new SetCardInfo("Wedding Ring", 32, Rarity.MYTHIC, mage.cards.w.WeddingRing.class)); + cards.add(new SetCardInfo("Windborn Muse", 101, Rarity.RARE, mage.cards.w.WindbornMuse.class)); + } +} diff --git a/Mage.Sets/src/mage/sets/DarkAscension.java b/Mage.Sets/src/mage/sets/DarkAscension.java index 930e9c466c5..8abef1f136e 100644 --- a/Mage.Sets/src/mage/sets/DarkAscension.java +++ b/Mage.Sets/src/mage/sets/DarkAscension.java @@ -1,13 +1,17 @@ package mage.sets; +import mage.cards.Card; import mage.cards.ExpansionSet; +import mage.cards.repository.CardInfo; +import mage.cards.repository.CardRepository; import mage.collation.BoosterCollator; import mage.collation.BoosterStructure; import mage.collation.CardRun; import mage.collation.RarityConfiguration; import mage.constants.Rarity; import mage.constants.SetType; +import mage.util.RandomUtil; import java.util.ArrayList; import java.util.List; @@ -209,6 +213,25 @@ public final class DarkAscension extends ExpansionSet { cards.add(new SetCardInfo("Zombie Apocalypse", 80, Rarity.RARE, mage.cards.z.ZombieApocalypse.class)); } + @Override + protected void addDoubleFace(List booster) { + Rarity rarity; + for (int i = 0; i < numBoosterDoubleFaced; i++) { + int rarityKey = RandomUtil.nextInt(121); + if (rarityKey < 72) { + rarity = Rarity.COMMON; + } else if (rarityKey < 108) { + rarity = Rarity.UNCOMMON; + } else if (rarityKey < 117) { + rarity = Rarity.RARE; + } else { + rarity = Rarity.MYTHIC; + } + List doubleFacedCards = getSpecialCardsByRarity(rarity); + addToBooster(booster, doubleFacedCards); + } + } + @Override public BoosterCollator createCollator() { return new DarkAscensionCollator(); @@ -222,7 +245,8 @@ class DarkAscensionCollator implements BoosterCollator { private final CardRun commonB = new CardRun(true, "103", "126", "29", "8", "73", "88", "60", "111", "19", "15", "29", "107", "35", "126", "150", "90", "73", "129", "6", "15", "68", "46", "155", "105", "72", "14", "129", "77", "38", "6", "121", "88", "134", "31", "111", "72", "103", "35", "19", "121", "90", "14", "31", "68", "105", "8", "60", "150", "134", "155", "38", "107", "46", "77"); private final CardRun uncommonA = new CardRun(true, "5", "128", "101", "153", "45", "145", "12", "67", "78", "117", "97", "44", "153", "5", "136", "57", "128", "135", "101", "154", "53", "7", "12", "57", "116", "135", "97", "44", "58", "92", "12", "136", "145", "117", "5", "78", "116", "92", "53", "58", "145", "10", "97", "7", "116", "45", "67", "154", "58", "10", "92", "44", "117", "153", "7", "78", "128", "45", "101", "67", "136", "53", "10", "135", "154", "57"); private final CardRun uncommonB = new CardRun(true, "83", "48", "16", "144", "127", "104", "42", "130", "48", "16", "84", "79", "141", "32", "130", "26", "127", "74", "83", "9", "42", "61", "144", "143", "108", "84", "26", "141", "127", "9", "48", "104", "74", "143", "79", "144", "108", "26", "32", "83", "104", "61", "42", "143", "16", "141", "130", "84", "32", "74", "108", "9", "79", "61"); - private final CardRun rare = new CardRun(false, "11", "18", "20", "23", "24", "25", "30", "33", "34", "36", "37", "39", "41", "56", "62", "63", "64", "69", "80", "82", "85", "89", "93", "95", "96", "112", "114", "115", "120", "123", "124", "149", "152", "156", "158", "11", "18", "20", "23", "24", "25", "30", "33", "34", "36", "37", "39", "41", "56", "62", "63", "64", "69", "80", "82", "85", "89", "93", "95", "96", "112", "114", "115", "120", "123", "124", "149", "152", "156", "158", "1", "28", "70", "99", "131", "137", "138", "139", "142", "151"); + private final CardRun rare = new CardRun(false, "11", "18", "20", "23", "24", "25", "30", "33", "34", "36", "37", "39", "41", "56", "62", "63", "64", "69", "80", "82", "85", "89", "93", "95", "96", "112", "114", "115", "120", "123", "124", "149", "152", "156", "158"); + private final CardRun mythic = new CardRun(false, "1", "28", "70", "99", "131", "137", "138", "139", "142", "151"); private final CardRun doubleFaced = new CardRun(false, "13", "55", "94", "125", "13", "55", "94", "125", "13", "55", "94", "125", "13", "55", "94", "125", "13", "55", "94", "125", "13", "55", "94", "125", "13", "55", "94", "125", "13", "55", "94", "125", "13", "55", "94", "125", "13", "55", "94", "125", "13", "55", "94", "125", "13", "55", "94", "125", "13", "55", "94", "125", "13", "55", "94", "125", "13", "55", "94", "125", "13", "55", "94", "125", "13", "55", "94", "125", "13", "55", "94", "125", "50", "81", "122", "146", "50", "81", "122", "146", "50", "81", "122", "146", "50", "81", "122", "146", "50", "81", "122", "146", "50", "81", "122", "146", "50", "81", "122", "146", "50", "81", "122", "146", "50", "81", "122", "146", "71", "98", "133", "71", "98", "133", "71", "98", "133", "140", "147", "140", "147"); private final CardRun land = new CardRun(false, "ISD_250", "ISD_251", "ISD_252", "ISD_253", "ISD_254", "ISD_255", "ISD_256", "ISD_257", "ISD_258", "ISD_259", "ISD_260", "ISD_261", "ISD_262", "ISD_263", "ISD_264"); @@ -233,6 +257,7 @@ class DarkAscensionCollator implements BoosterCollator { private final BoosterStructure AAB = new BoosterStructure(uncommonA, uncommonA, uncommonB); private final BoosterStructure ABB = new BoosterStructure(uncommonA, uncommonB, uncommonB); private final BoosterStructure R1 = new BoosterStructure(rare); + private final BoosterStructure M1 = new BoosterStructure(mythic); private final BoosterStructure D1 = new BoosterStructure(doubleFaced); private final BoosterStructure L1 = new BoosterStructure(land); @@ -245,7 +270,7 @@ class DarkAscensionCollator implements BoosterCollator { AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, ABB, ABB, ABB, ABB, ABB, ABB, ABB ); - private final RarityConfiguration rareRuns = new RarityConfiguration(R1); + private final RarityConfiguration rareRuns = new RarityConfiguration(R1, R1, R1, R1, R1, R1, R1, M1); private final RarityConfiguration dfcRuns = new RarityConfiguration(D1); private final RarityConfiguration landRuns = new RarityConfiguration(L1); diff --git a/Mage.Sets/src/mage/sets/Dominaria.java b/Mage.Sets/src/mage/sets/Dominaria.java index 8d4a4d441c2..486edd12311 100644 --- a/Mage.Sets/src/mage/sets/Dominaria.java +++ b/Mage.Sets/src/mage/sets/Dominaria.java @@ -1,14 +1,20 @@ package mage.sets; import mage.cards.ExpansionSet; +import mage.cards.repository.CardCriteria; +import mage.cards.repository.CardInfo; +import mage.cards.repository.CardRepository; import mage.collation.BoosterCollator; import mage.collation.BoosterStructure; import mage.collation.CardRun; import mage.collation.RarityConfiguration; +import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.SetType; +import mage.constants.SuperType; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** @@ -31,8 +37,9 @@ public final class Dominaria extends ExpansionSet { this.numBoosterCommon = 10; this.numBoosterUncommon = 3; this.numBoosterRare = 1; - this.ratioBoosterMythic = 8; - this.needsLegendCreature = true; + this.ratioBoosterMythic = 10; + this.ratioBoosterSpecialRare = 4; + this.ratioBoosterSpecialMythic = 5; this.maxCardNumberInBooster = 269; cards.add(new SetCardInfo("Academy Drake", 40, Rarity.COMMON, mage.cards.a.AcademyDrake.class)); @@ -317,6 +324,23 @@ public final class Dominaria extends ExpansionSet { cards.add(new SetCardInfo("Zhalfirin Void", 249, Rarity.UNCOMMON, mage.cards.z.ZhalfirinVoid.class)); } + // for sheet math reasons, four rare legendary creatures are collated as normal rares + // (a booster containing one of them will always contain an uncommon legend as well) + private static final List nonSpecialLegends = Arrays.asList("Darigaaz Reincarnated", "Josu Vess, Lich Knight", "Muldrotha, the Gravetide", "Shalai, Voice of Plenty"); + + @Override + protected List findSpecialCardsByRarity(Rarity rarity) { + List cardInfos = super.findSpecialCardsByRarity(rarity); + cardInfos.addAll(CardRepository.instance.findCards(new CardCriteria() + .setCodes(this.code) + .rarities(rarity) + .supertypes(SuperType.LEGENDARY) + .types(CardType.CREATURE) + .maxCardNumber(maxCardNumberInBooster))); + cardInfos.removeIf(cardInfo -> nonSpecialLegends.contains(cardInfo.getName())); + return cardInfos; + } + @Override public BoosterCollator createCollator() { return new DominariaCollator(); @@ -333,7 +357,6 @@ class DominariaCollator implements BoosterCollator { private final CardRun uncommonA = new CardRun(true, "235", "119", "179", "145", "99", "38", "219", "175", "243", "33", "116", "210", "70", "160", "152", "81", "23", "235", "186", "242", "14", "130", "210", "54", "159", "121", "90", "38", "219", "180", "245", "23", "145", "179", "65", "186", "116", "82", "75", "121", "175", "99", "14", "119", "235", "70", "159", "130", "81", "54", "243", "160", "33", "82", "152", "210", "65", "180", "145", "90", "75", "242", "159", "99", "245", "116", "219", "70", "175", "119", "81", "65", "243", "186", "82", "38", "130", "235", "75", "160", "33", "121", "70", "152", "179", "90", "23", "145", "180", "54", "159", "116", "14", "65", "242", "179", "99", "38", "119", "210", "245", "180", "152", "23", "54", "243", "175", "90", "14", "130", "219", "242", "160", "121", "82", "75", "245", "186", "81", "33"); private final CardRun uncommonB = new CardRun(true, "231", "103", "188", "31", "64", "222", "249", "161", "28", "185", "150", "213", "97", "8", "181", "49", "220", "246", "188", "228", "56", "123", "218", "93", "185", "30", "74", "231", "244", "181", "31", "64", "128", "222", "107", "161", "137", "51", "213", "249", "103", "28", "49", "150", "220", "97", "188", "123", "56", "228", "137", "246", "30", "64", "244", "218", "107", "161", "128", "74", "213", "249", "93", "31", "51", "150", "222", "103", "185", "123", "56", "231", "97", "8", "220", "74", "137", "228", "107", "181", "150", "49", "218", "244", "93", "28", "51", "128", "213", "188", "8", "137", "64", "222", "246", "97", "30", "56", "31", "231", "103", "28", "123", "74", "220", "249", "161", "107", "246", "51", "228", "185", "8", "128", "49", "218", "244", "93", "30", "181"); private final CardRun uncommonLegend = new CardRun(true, "203", "109", "204", "66", "191", "4", "205", "111", "206", "12", "196", "165", "202", "69", "203", "113", "190", "109", "208", "25", "194", "148", "196", "12", "204", "66", "208", "111", "191", "165", "202", "4", "205", "113", "194", "148", "206", "25", "190", "69"); - // Shalai (35), Josu Vess (95), Darigaaz (193) and Muldrotha (199) are on the non-legend sheet; boosters containing one of them will also contain an uncommon legend private final CardRun rare = new CardRun(false, "6", "13", "18", "35", "39", "42", "55", "57", "61", "68", "88", "95", "98", "102", "114", "122", "129", "131", "133", "143", "147", "166", "173", "182", "183", "184", "187", "200", "201", "211", "214", "215", "217", "223", "233", "238", "239", "240", "241", "247", "248", "6", "13", "18", "35", "39", "42", "55", "57", "61", "68", "88", "95", "98", "102", "114", "122", "129", "131", "133", "143", "147", "166", "173", "182", "183", "184", "187", "200", "201", "211", "214", "215", "217", "223", "233", "238", "239", "240", "241", "247", "248", "1", "21", "100", "132", "193", "199", "207", "224", "237"); private final CardRun rareLegend = new CardRun(false, "16", "36", "58", "76", "96", "108", "146", "172", "192", "195", "198", "234", "16", "36", "58", "76", "96", "108", "146", "172", "192", "195", "198", "234", "26", "59", "86", "149", "174", "197"); private final CardRun land = new CardRun(false, "250", "251", "252", "253", "254", "255", "256", "257", "258", "259", "260", "261", "262", "263", "264", "265", "266", "267", "268", "269"); diff --git a/Mage.Sets/src/mage/sets/DoubleMasters.java b/Mage.Sets/src/mage/sets/DoubleMasters.java index 9b368b93aa6..a5d19747f80 100644 --- a/Mage.Sets/src/mage/sets/DoubleMasters.java +++ b/Mage.Sets/src/mage/sets/DoubleMasters.java @@ -429,7 +429,6 @@ public final class DoubleMasters extends ExpansionSet { // Using USA collation for all rarities // Foil slot partially inferred to match standard booster rarity as best as possible // Foil odds (approximate): 10/14 to be common, 3/14 to be uncommon, 0.875/14 to be rare, 0.125/14 to be mythic -// Regular common sheets used for foil commons as foil common sheet is currently incomplete // TODO: write a test, not sure how right now class DoubleMastersCollator implements BoosterCollator { @@ -438,81 +437,72 @@ class DoubleMastersCollator implements BoosterCollator { private final CardRun commonC = new CardRun(true, "18", "35", "4", "17", "2", "30", "12", "27", "3", "13", "33", "30", "18", "12", "35", "27", "4", "3", "17", "13", "2", "33", "12", "4", "13", "18", "27", "17", "33", "30", "35", "3", "2", "18", "35", "4", "17", "2", "30", "12", "27", "3", "13", "33", "30", "18", "12", "35", "27", "4", "3", "17", "13", "2", "33", "12", "4", "13", "18", "27", "17", "33", "30", "35", "3", "2", "18", "35", "4", "17", "2", "30", "12", "27", "3", "13", "33", "30", "18", "12", "35", "27", "4", "3", "17", "13", "2", "33", "12", "4", "13", "18", "27", "17", "33", "30", "35", "3", "2", "18", "35", "4", "17", "2", "30", "12", "27", "3", "13", "33", "30", "18", "12", "35", "27", "4", "3", "17", "13", "2", "33"); private final CardRun uncommonA = new CardRun(true, "315", "244", "73", "274", "208", "147", "202", "169", "290", "102", "65", "285", "220", "67", "186", "246", "112", "222", "22", "301", "86", "62", "228", "161", "101", "302", "54", "184", "220", "307", "73", "93", "15", "119", "202", "291", "169", "323", "102", "244", "201", "172", "312", "290", "37", "208", "246", "6", "65", "307", "86", "186", "67", "222", "141", "15", "315", "22", "274", "161", "37", "101", "285", "228", "184", "112", "147", "302", "172", "54", "220", "141", "301", "6", "93", "119", "169", "323", "291", "312", "201", "62", "244", "184", "102", "15", "222", "37", "290", "112", "147", "65", "285", "101", "315", "67", "202", "186", "274", "208", "323", "73", "307", "228", "86", "161", "119", "246", "312", "6", "22", "172", "301", "62", "302", "141", "93", "291", "54", "201"); private final CardRun uncommonB = new CardRun(true, "217", "23", "49", "245", "91", "194", "148", "71", "16", "125", "238", "198", "180", "36", "278", "99", "224", "38", "232", "123", "68", "258", "229", "310", "120", "242", "188", "25", "66", "267", "138", "178", "281", "199", "89", "194", "241", "23", "49", "91", "245", "166", "134", "238", "217", "148", "36", "265", "16", "125", "198", "232", "71", "100", "267", "229", "180", "68", "278", "123", "25", "99", "241", "38", "120", "258", "199", "188", "224", "281", "310", "49", "23", "66", "138", "178", "245", "217", "166", "134", "242", "89", "36", "265", "148", "100", "242", "198", "180", "25", "238", "16", "194", "38", "91", "71", "125", "278", "229", "310", "68", "232", "123", "178", "99", "258", "188", "120", "267", "199", "89", "224", "241", "66", "134", "166", "281", "138", "100", "265"); - private final CardRun rareA = new CardRun(false, "76", "231", "153", "77", "117", "118", "10", "43", "313", "158", "48", "85", "124", "252", "14", "167", "316", "318", "196", "127", "320", "130", "321", "97", "175", "271", "210", "272", "282", "26", "139", "103", "64", "325", "104", "179", "289", "32", "293", "326", "299", "327", "72", "109", "223", "225", "226", "75", "332", "113", "76", "231", "153", "77", "117", "118", "10", "43", "313", "158", "48", "85", "124", "252", "14", "167", "316", "318", "196", "127", "320", "130", "321", "97", "175", "271", "210", "272", "282", "26", "139", "103", "64", "325", "104", "179", "289", "32", "293", "326", "299", "327", "72", "109", "223", "225", "226", "75", "332", "113", "190", "8", "192", "240", "81", "314", "248", "164", "253", "51", "131", "204", "205", "20", "206", "136", "275", "214", "218", "303"); - private final CardRun rareB = new CardRun(false, "122", "82", "317", "55", "264", "174", "324", "24", "284", "215", "328"); - private final CardRun rareC = new CardRun(false, "189", "7", "311", "155", "236", "193", "11", "47", "249", "195", "128", "170", "260", "132", "203", "21", "268", "98", "57", "322", "209", "177", "279", "61", "212", "219", "292", "34", "183", "110", "149", "227", "306"); - private final CardRun rareD = new CardRun(false, "309", "191", "233", "9", "156", "243", "88", "251", "319", "53", "129", "200", "171", "19", "266", "207", "58", "211", "276", "213", "142", "286", "106", "31", "221", "182", "39"); - private final CardRun rareE = new CardRun(false, "5", "41", "152", "234", "235", "197", "94", "56", "1", "270", "107", "145", "295", "296", "297", "298", "300", "216", "185", "308"); + private final CardRun rareSlot1 = new CardRun(false, "76", "231", "153", "77", "117", "118", "10", "43", "313", "158", "48", "85", "124", "252", "14", "167", "316", "318", "196", "127", "320", "130", "321", "97", "175", "271", "210", "272", "282", "26", "139", "103", "64", "325", "104", "179", "289", "32", "293", "326", "299", "327", "72", "109", "223", "225", "226", "75", "332", "113", "76", "231", "153", "77", "117", "118", "10", "43", "313", "158", "48", "85", "124", "252", "14", "167", "316", "318", "196", "127", "320", "130", "321", "97", "175", "271", "210", "272", "282", "26", "139", "103", "64", "325", "104", "179", "289", "32", "293", "326", "299", "327", "72", "109", "223", "225", "226", "75", "332", "113", "190", "8", "192", "240", "81", "314", "248", "164", "253", "51", "131", "204", "205", "20", "206", "136", "275", "214", "218", "303", "122", "82", "317", "55", "264", "174", "324", "24", "284", "215", "328", "122", "82", "317", "55", "264", "174", "324", "24", "284", "215", "328"); + private final CardRun rareSlot2 = new CardRun(false, "189", "7", "311", "155", "236", "193", "11", "47", "249", "195", "128", "170", "260", "132", "203", "21", "268", "98", "57", "322", "209", "177", "279", "61", "212", "219", "292", "34", "183", "110", "149", "227", "306", "189", "7", "311", "155", "236", "193", "11", "47", "249", "195", "128", "170", "260", "132", "203", "21", "268", "98", "57", "322", "209", "177", "279", "61", "212", "219", "292", "34", "183", "110", "149", "227", "306", "309", "191", "233", "9", "156", "243", "88", "251", "319", "53", "129", "200", "171", "19", "266", "207", "58", "211", "276", "213", "142", "286", "106", "31", "221", "182", "39", "309", "191", "233", "9", "156", "243", "88", "251", "319", "53", "129", "200", "171", "19", "266", "207", "58", "211", "276", "213", "142", "286", "106", "31", "221", "182", "39", "5", "41", "152", "234", "235", "197", "94", "56", "1", "270", "107", "145", "295", "296", "297", "298", "300", "216", "185", "308"); + private final CardRun foilCommonA = new CardRun(true, "126", "40", "28", "144", "69", "35", "114", "63", "4", "146", "74", "30", "143", "52", "33", "137", "60", "2", "121", "46", "269", "12", "135", "42", "29", "115", "59", "17", "140", "44", "18", "116", "70", "28", "133", "63", "13", "114", "52", "3", "150", "40", "27", "126", "50", "35", "135", "69", "4", "137", "269", "60", "30", "144", "74", "17", "18", "133", "44", "13", "150", "70", "29", "115", "59", "3", "116", "121", "45", "33", "146", "46", "12", "140", "42", "2", "143", "50", "144", "74", "12", "121", "50", "4", "269", "143", "63", "28", "17", "69", "35", "135", "45", "27", "126", "40", "30", "114", "52", "116", "60", "33", "137", "46", "2", "146", "42", "29", "133", "70", "13", "150", "44", "18", "140", "59", "3", "115", "45", "27"); + private final CardRun foilCommonB = new CardRun(true, "95", "160", "168", "277", "80", "159", "230", "108", "157", "330", "78", "163", "287", "83", "173", "329", "108", "168", "247", "90", "154", "283", "80", "261", "262", "105", "187", "294", "79", "165", "331", "304", "84", "163", "162", "277", "111", "151", "330", "92", "157", "304", "96", "160", "230", "95", "181", "331", "78", "176", "287", "84", "159", "256", "87", "165", "261", "79", "330", "154", "294", "105", "173", "259", "83", "187", "329", "111", "168", "283", "92", "151", "262", "108", "162", "230", "80", "163", "247", "95", "157", "277", "90", "159", "331", "96", "176", "287", "78", "154", "259", "84", "160", "256", "105", "329", "181", "261", "79", "165", "304", "83", "151", "262", "111", "173", "283", "87", "187", "294", "92", "90", "162", "259", "96", "181", "247", "87", "176", "256"); + private final CardRun foilCommonC = new CardRun(false, "237", "239", "250", "254", "255", "257", "263", "273", "280", "288", "305"); private final CardRun foilUncommonA = new CardRun(false, "6", "119", "312", "244", "161", "246", "315", "86", "93", "15", "169", "201", "54", "172", "202", "208", "22", "274", "323", "101", "102", "62", "141", "65", "285", "67", "220", "290", "291", "147", "222", "301", "302", "73", "184", "37", "112", "186", "228", "307"); private final CardRun foilUncommonB = new CardRun(false, "310", "232", "120", "238", "241", "242", "245", "194", "123", "89", "91", "166", "49", "16", "125", "198", "199", "258", "265", "134", "267", "99", "23", "278", "100", "25", "281", "138", "178", "66", "217", "68", "180", "71", "36", "148", "224", "38", "188", "229"); - private final CardRun foilRareA = new CardRun(false, "309", "231", "76", "189", "7", "153", "191", "233", "77", "9", "117", "311", "118", "155", "10", "236", "43", "193", "313", "156", "158", "243", "11", "122", "47", "82", "48", "85", "88", "124", "249", "251", "252", "14", "167", "195", "316", "317", "318", "196", "319", "127", "128", "53", "320", "170", "129", "260", "200", "171", "130", "321", "55", "132", "264", "203", "19", "266", "21", "174", "268", "207", "97", "98", "175", "57", "58", "271", "322", "209", "210", "211", "272", "276", "324", "177", "279", "24", "61", "282", "212", "26", "139", "284", "103", "64", "213", "142", "325", "104", "215", "286", "179", "219", "106", "289", "31", "32", "292", "293", "326", "221", "299", "34", "182", "327", "72", "109", "183", "223", "110", "149", "328", "225", "226", "227", "306", "75", "332", "113", "39"); - private final CardRun foilRareB = new CardRun(false, "5", "41", "190", "8", "152", "234", "235", "192", "240", "81", "314", "248", "164", "253", "51", "197", "94", "131", "56", "204", "1", "205", "20", "206", "270", "136", "275", "214", "218", "107", "145", "295", "296", "297", "298", "300", "216", "303", "185", "308"); + private final CardRun foilRare = new CardRun(false, "309", "231", "76", "189", "7", "153", "191", "233", "77", "9", "117", "311", "118", "155", "10", "236", "43", "193", "313", "156", "158", "243", "11", "122", "47", "82", "48", "85", "88", "124", "249", "251", "252", "14", "167", "195", "316", "317", "318", "196", "319", "127", "128", "53", "320", "170", "129", "260", "200", "171", "130", "321", "55", "132", "264", "203", "19", "266", "21", "174", "268", "207", "97", "98", "175", "57", "58", "271", "322", "209", "210", "211", "272", "276", "324", "177", "279", "24", "61", "282", "212", "26", "139", "284", "103", "64", "213", "142", "325", "104", "215", "286", "179", "219", "106", "289", "31", "32", "292", "293", "326", "221", "299", "34", "182", "327", "72", "109", "183", "223", "110", "149", "328", "225", "226", "227", "306", "75", "332", "113", "39"); + private final CardRun foilMythic = new CardRun(false, "5", "41", "190", "8", "152", "234", "235", "192", "240", "81", "314", "248", "164", "253", "51", "197", "94", "131", "56", "204", "1", "205", "20", "206", "270", "136", "275", "214", "218", "107", "145", "295", "296", "297", "298", "300", "216", "303", "185", "308"); - private final BoosterStructure C1 = new BoosterStructure( + private final BoosterStructure AAAABBBC = new BoosterStructure( commonA, commonA, commonA, commonA, commonB, commonB, commonB, commonC ); - private final BoosterStructure C2 = new BoosterStructure( + private final BoosterStructure AAABBBBC = new BoosterStructure( commonA, commonA, commonA, commonB, commonB, commonB, commonB, commonC ); - private final BoosterStructure C3 = new BoosterStructure( + private final BoosterStructure AAAABBBB = new BoosterStructure( commonA, commonA, commonA, commonA, commonB, commonB, commonB, commonB ); - private final BoosterStructure U1 = new BoosterStructure(uncommonA, uncommonB, uncommonB); - private final BoosterStructure U2 = new BoosterStructure(uncommonA, uncommonA, uncommonB); - private final BoosterStructure R1 = new BoosterStructure(rareA, rareC); - private final BoosterStructure R2 = new BoosterStructure(rareA, rareD); - private final BoosterStructure R3 = new BoosterStructure(rareA, rareE); - private final BoosterStructure R4 = new BoosterStructure(rareB, rareC); - private final BoosterStructure R5 = new BoosterStructure(rareB, rareD); - private final BoosterStructure R6 = new BoosterStructure(rareB, rareE); + private final BoosterStructure AAB = new BoosterStructure(uncommonA, uncommonA, uncommonB); + private final BoosterStructure ABB = new BoosterStructure(uncommonA, uncommonB, uncommonB); + private final BoosterStructure R1 = new BoosterStructure(rareSlot1, rareSlot2); - private final BoosterStructure F01 = new BoosterStructure(commonA, commonB); - private final BoosterStructure F02 = new BoosterStructure(commonA, commonC); - private final BoosterStructure F03 = new BoosterStructure(commonA, foilUncommonA); - private final BoosterStructure F04 = new BoosterStructure(commonA, foilUncommonB); - private final BoosterStructure F05 = new BoosterStructure(commonA, foilRareA); - private final BoosterStructure F06 = new BoosterStructure(commonA, foilRareB); - private final BoosterStructure F07 = new BoosterStructure(commonB, commonC); - private final BoosterStructure F08 = new BoosterStructure(commonB, foilUncommonA); - private final BoosterStructure F09 = new BoosterStructure(commonB, foilUncommonB); - private final BoosterStructure F10 = new BoosterStructure(commonB, foilRareA); - private final BoosterStructure F11 = new BoosterStructure(commonB, foilRareB); - private final BoosterStructure F12 = new BoosterStructure(commonC, foilUncommonA); - private final BoosterStructure F13 = new BoosterStructure(commonC, foilUncommonB); - private final BoosterStructure F14 = new BoosterStructure(commonC, foilRareA); - private final BoosterStructure F15 = new BoosterStructure(commonC, foilRareB); + private final BoosterStructure F01 = new BoosterStructure(foilCommonA, foilCommonB); + private final BoosterStructure F02 = new BoosterStructure(foilCommonA, foilCommonC); + private final BoosterStructure F03 = new BoosterStructure(foilCommonA, foilUncommonA); + private final BoosterStructure F04 = new BoosterStructure(foilCommonA, foilUncommonB); + private final BoosterStructure F05 = new BoosterStructure(foilCommonA, foilRare); + private final BoosterStructure F06 = new BoosterStructure(foilCommonA, foilMythic); + private final BoosterStructure F07 = new BoosterStructure(foilCommonB, foilCommonC); + private final BoosterStructure F08 = new BoosterStructure(foilCommonB, foilUncommonA); + private final BoosterStructure F09 = new BoosterStructure(foilCommonB, foilUncommonB); + private final BoosterStructure F10 = new BoosterStructure(foilCommonB, foilRare); + private final BoosterStructure F11 = new BoosterStructure(foilCommonB, foilMythic); + private final BoosterStructure F12 = new BoosterStructure(foilCommonC, foilUncommonA); + private final BoosterStructure F13 = new BoosterStructure(foilCommonC, foilUncommonB); + private final BoosterStructure F14 = new BoosterStructure(foilCommonC, foilRare); + private final BoosterStructure F15 = new BoosterStructure(foilCommonC, foilMythic); private final BoosterStructure F16 = new BoosterStructure(foilUncommonA, foilUncommonB); - private final BoosterStructure F17 = new BoosterStructure(foilUncommonA, foilRareA); - private final BoosterStructure F18 = new BoosterStructure(foilUncommonA, foilRareB); - private final BoosterStructure F19 = new BoosterStructure(foilUncommonB, foilRareA); - private final BoosterStructure F20 = new BoosterStructure(foilUncommonB, foilRareB); - private final BoosterStructure F21 = new BoosterStructure(foilRareA, foilRareB); + private final BoosterStructure F17 = new BoosterStructure(foilUncommonA, foilRare); + private final BoosterStructure F18 = new BoosterStructure(foilUncommonA, foilMythic); + private final BoosterStructure F19 = new BoosterStructure(foilUncommonB, foilRare); + private final BoosterStructure F20 = new BoosterStructure(foilUncommonB, foilMythic); + private final BoosterStructure F21 = new BoosterStructure(foilRare, foilMythic); + // In order for equal numbers of each common to exist, the average booster must contain: + // 3.52 A commons (320 / 91) + // 3.52 B commons (320 / 91) + // 0.97 C commons (88 / 91) private final RarityConfiguration commonRuns = new RarityConfiguration( - C1, C2, C1, C2, - C1, C2, C1, C2, - C1, C2, C1, C2, - C1, C2, C1, C2, - C1, C2, C1, C2, - C1, C2, C1, C2, - C1, C2, C1, C2, - C1, C2, C1, C2, - C1, C2, C1, C2, - C1, C2, C1, C2, - C1, C2, C1, C2, - C3, C3, C3 - ); - private final RarityConfiguration uncommonRuns = new RarityConfiguration( - U1, U2 - ); - private final RarityConfiguration rareRuns = new RarityConfiguration( - R1, R2, R3, - R4, R5, R6 + AAAABBBC, AAABBBBC, AAAABBBC, AAABBBBC, AAAABBBC, AAABBBBC, AAAABBBC, AAABBBBC, AAAABBBC, AAABBBBC, AAAABBBC, + AAABBBBC, AAAABBBC, AAABBBBC, AAAABBBC, AAABBBBC, AAAABBBC, AAABBBBC, AAAABBBC, AAABBBBC, AAAABBBC, AAABBBBC, + AAAABBBC, AAABBBBC, AAAABBBC, AAABBBBC, AAAABBBC, AAABBBBC, AAAABBBC, AAABBBBC, AAAABBBC, AAABBBBC, AAAABBBC, + AAABBBBC, AAAABBBC, AAABBBBC, AAAABBBC, AAABBBBC, AAAABBBC, AAABBBBC, AAAABBBC, AAABBBBC, AAAABBBC, AAABBBBC, + AAAABBBC, AAABBBBC, AAAABBBC, AAABBBBC, AAAABBBC, AAABBBBC, AAAABBBC, AAABBBBC, AAAABBBC, AAABBBBC, AAAABBBC, + AAABBBBC, AAAABBBC, AAABBBBC, AAAABBBC, AAABBBBC, AAAABBBC, AAABBBBC, AAAABBBC, AAABBBBC, AAAABBBC, AAABBBBC, + AAAABBBC, AAABBBBC, AAAABBBC, AAABBBBC, AAAABBBC, AAABBBBC, AAAABBBC, AAABBBBC, AAAABBBC, AAABBBBC, AAAABBBC, + AAABBBBC, AAAABBBC, AAABBBBC, AAAABBBC, AAABBBBC, AAAABBBC, AAABBBBC, AAAABBBC, AAABBBBC, AAAABBBC, AAABBBBC, + AAAABBBB, AAAABBBB, AAAABBBB ); + private final RarityConfiguration uncommonRuns = new RarityConfiguration(AAB, ABB); + private final RarityConfiguration rareRuns = new RarityConfiguration(R1); private final RarityConfiguration foilRuns = new RarityConfiguration( F01, F01, F01, F01, F01, F01, diff --git a/Mage.Sets/src/mage/sets/DragonsMaze.java b/Mage.Sets/src/mage/sets/DragonsMaze.java index 7e2f75227c7..8d9f9a2fcce 100644 --- a/Mage.Sets/src/mage/sets/DragonsMaze.java +++ b/Mage.Sets/src/mage/sets/DragonsMaze.java @@ -1,12 +1,18 @@ package mage.sets; +import mage.cards.Card; import mage.cards.ExpansionSet; import mage.cards.repository.CardCriteria; import mage.cards.repository.CardInfo; import mage.cards.repository.CardRepository; +import mage.collation.BoosterCollator; +import mage.collation.BoosterStructure; +import mage.collation.CardRun; +import mage.collation.RarityConfiguration; import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.SetType; +import mage.util.RandomUtil; import java.util.ArrayList; import java.util.List; @@ -22,8 +28,6 @@ public final class DragonsMaze extends ExpansionSet { return instance; } - private final List savedSpecialRares = new ArrayList<>(); - private DragonsMaze() { super("Dragon's Maze", "DGM", ExpansionSet.buildDate(2013, 5, 3), SetType.EXPANSION); this.blockName = "Return to Ravnica"; @@ -194,61 +198,105 @@ public final class DragonsMaze extends ExpansionSet { } @Override - public List getCardsByRarity(Rarity rarity) { - if (rarity == Rarity.COMMON) { - List savedCardsInfos = savedCards.get(rarity); - if (savedCardsInfos == null) { - CardCriteria criteria = new CardCriteria(); - criteria.setCodes(this.code).notTypes(CardType.LAND); - criteria.rarities(rarity).doubleFaced(false); - savedCardsInfos = CardRepository.instance.findCards(criteria); - if (maxCardNumberInBooster != Integer.MAX_VALUE) { - savedCardsInfos.removeIf(next -> next.getCardNumberAsInt() > maxCardNumberInBooster); - } - savedCards.put(rarity, savedCardsInfos); - } - // Return a copy of the saved cards information, as not to modify the original. - return new ArrayList<>(savedCardsInfos); + protected void addSpecialCards(List booster, int number) { + // number is here always 1 + // the land print sheets are believed to contain 23 copies of each Guildgate, + // one copy of each RTR and GTC shockland, and two copies of Maze's End + Rarity rarity; + int rarityKey = RandomUtil.nextInt(242); + if (rarityKey < 230) { + rarity = Rarity.COMMON; + } else if (rarityKey < 240) { + rarity = Rarity.RARE; } else { - return super.getCardsByRarity(rarity); + rarity = Rarity.MYTHIC; } + addToBooster(booster, getSpecialCardsByRarity(rarity)); } @Override - public List getSpecialCommon() { + protected List findSpecialCardsByRarity(Rarity rarity) { CardCriteria criteria = new CardCriteria(); - criteria.rarities(Rarity.COMMON).setCodes(this.code).types(CardType.LAND); - return CardRepository.instance.findCards(criteria); - } - - @Override - public List getSpecialRare() { - if (savedSpecialRares.isEmpty()) { - fillSpecialRares("GTC", "Breeding Pool"); - fillSpecialRares("GTC", "Godless Shrine"); - fillSpecialRares("GTC", "Sacred Foundry"); - fillSpecialRares("GTC", "Stomping Ground"); - fillSpecialRares("GTC", "Watery Grave"); - fillSpecialRares("RTR", "Blood Crypt"); - fillSpecialRares("RTR", "Hallowed Fountain"); - fillSpecialRares("RTR", "Overgrown Tomb"); - fillSpecialRares("RTR", "Steam Vents"); - fillSpecialRares("RTR", "Temple Garden"); + criteria.rarities(rarity).types(CardType.LAND); + if (rarity == Rarity.RARE) { + // shocklands + criteria.setCodes("RTR", "GTC"); + } else { + // Guildgates and Maze's End + criteria.setCodes(this.code); } - return new ArrayList<>(savedSpecialRares); - } - - private void fillSpecialRares(String setCode, String cardName) { - CardCriteria criteria = new CardCriteria(); - criteria.setCodes(setCode).name(cardName); - savedSpecialRares.addAll(CardRepository.instance.findCards(criteria)); + List cardInfos = CardRepository.instance.findCards(criteria); + cardInfos.removeIf(cardInfo -> (cardInfo.getName().equals("Grove of the Guardian") + || cardInfo.getName().equals("Thespian's Stage"))); + return cardInfos; } @Override - public List getSpecialMythic() { - CardCriteria criteria = new CardCriteria(); - criteria.rarities(Rarity.MYTHIC).setCodes(this.code).types(CardType.LAND); - return CardRepository.instance.findCards(criteria); + protected void generateBoosterMap() { + super.generateBoosterMap(); + CardRepository + .instance + .findCards(new CardCriteria().setCodes("RTR", "GTC").rarities(Rarity.RARE).types(CardType.LAND)) + .stream() + .forEach(cardInfo -> inBoosterMap.put(cardInfo.getSetCode() + "_" + cardInfo.getCardNumber(), cardInfo)); } + @Override + public BoosterCollator createCollator() { + return new DragonsMazeCollator(); + } +} + +// Booster collation info from https://www.lethe.xyz/mtg/collation/dgm.html +// Using USA collation for common/uncommon, rare collation inferred from other sets +class DragonsMazeCollator implements BoosterCollator { + private final CardRun commonA = new CardRun(true, "113", "43", "12", "137", "142", "28", "109", "39", "8", "44", "19", "139", "136", "25", "87", "33", "1", "46", "39", "52", "141", "38", "25", "67", "44", "9", "145", "17", "54", "33", "21", "142", "46", "2", "52", "136", "12", "30", "87", "141", "45", "8", "37", "137", "28", "17", "113", "14", "45", "9", "139", "54", "21", "138", "109", "2", "43", "14", "145", "37", "67", "30", "138", "1", "19", "38"); + private final CardRun commonB = new CardRun(true, "120", "10", "20", "32", "24", "40", "48", "15", "6", "35", "65", "140", "23", "16", "42", "10", "31", "41", "86", "4", "29", "13", "144", "32", "50", "40", "120", "3", "26", "13", "143", "48", "31", "23", "65", "6", "20", "86", "35", "50", "24", "15", "140", "4", "90", "143", "41", "26", "144", "16", "3", "90", "29", "42"); + private final CardRun uncommonA = new CardRun(true, "122", "75", "77", "76", "131", "83", "118", "106", "133", "103", "101", "110", "127", "70", "102", "118", "121", "60", "76", "111", "53", "83", "75", "127", "102", "110", "121", "77", "97", "122", "106", "76", "103", "102", "117", "133", "75", "53", "110", "131", "101", "60", "61", "117", "111", "121", "97", "83", "70", "118", "61", "122", "117", "106", "131", "103", "77", "101", "133", "60", "53", "97", "127", "61", "70", "111"); + private final CardRun uncommonB = new CardRun(true, "71", "129", "98", "74", "55", "105", "78", "130", "59", "56", "98", "135", "116", "129", "134", "78", "73", "74", "93", "130", "105", "79", "126", "59", "134", "55", "64", "56", "71", "73", "135", "79", "129", "93", "78", "116", "74", "126", "56", "79", "130", "55", "71", "98", "59", "135", "64", "93", "126", "105", "116", "73", "134", "64"); + // Trait Doctoring not implemented (text changing effect) + private final CardRun rare = new CardRun(false, "5", "7", "11", /*"18",*/ "22", "27", "34", "36", "47", "49", "51", "58", "66", "68", "69", "72", "80", "84", "85", "88", "89", "91", "96", "99", "104", "107", "108", "112", "115", "119", "123", "124", "125", "128", "132"); + private final CardRun mythic = new CardRun(false, "57", "62", "63", "81", "82", "92", "94", "95", "100", "114"); + private final CardRun landCommon = new CardRun(false, "146", "147", "148", "149", "150", "151", "153", "154", "155", "156"); + private final CardRun landRare = new CardRun(false, "152", "RTR_238", "RTR_241", "RTR_243", "RTR_247", "RTR_248", "152", "GTC_240", "GTC_242", "GTC_245", "GTC_247", "GTC_249"); + + private final BoosterStructure AAAAABBBBB = new BoosterStructure( + commonA, commonA, commonA, commonA, commonA, + commonB, commonB, commonB, commonB, commonB + ); + private final BoosterStructure AAAAAABBBB = new BoosterStructure( + commonA, commonA, commonA, commonA, commonA, commonA, + commonB, commonB, commonB, commonB + ); + private final BoosterStructure AAB = new BoosterStructure(uncommonA, uncommonA, uncommonB); + private final BoosterStructure ABB = new BoosterStructure(uncommonA, uncommonB, uncommonB); + private final BoosterStructure R1 = new BoosterStructure(rare); + private final BoosterStructure M1 = new BoosterStructure(mythic); + private final BoosterStructure L1 = new BoosterStructure(landCommon); + private final BoosterStructure L2 = new BoosterStructure(landRare); + + private final RarityConfiguration commonRuns = new RarityConfiguration(AAAAABBBBB, AAAAAABBBB); + // In order for equal numbers of each uncommon to exist, the average booster must contain: + // 1.65 A uncommons (33 / 20) + // 1.35 B uncommons (27 / 20) + // These numbers are the same for all sets with 40 uncommons in asymmetrical A/B print runs + private final RarityConfiguration uncommonRuns = new RarityConfiguration( + AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, + ABB, ABB, ABB, ABB, ABB, ABB, ABB + ); + private final RarityConfiguration rareRuns = new RarityConfiguration(R1, R1, R1, R1, R1, R1, R1, M1); + private final RarityConfiguration landRuns = new RarityConfiguration( + L1, L1, L1, L1, L1, L1, L1, L1, L1, L1, + L1, L1, L1, L1, L1, L1, L1, L1, L1, L2 + ); + + @Override + public List makeBooster() { + List booster = new ArrayList<>(); + booster.addAll(commonRuns.getNext().makeRun()); + booster.addAll(uncommonRuns.getNext().makeRun()); + booster.addAll(rareRuns.getNext().makeRun()); + booster.addAll(landRuns.getNext().makeRun()); + return booster; + } } diff --git a/Mage.Sets/src/mage/sets/DragonsOfTarkir.java b/Mage.Sets/src/mage/sets/DragonsOfTarkir.java index 61109fcfbc4..5f8cdf50faa 100644 --- a/Mage.Sets/src/mage/sets/DragonsOfTarkir.java +++ b/Mage.Sets/src/mage/sets/DragonsOfTarkir.java @@ -1,10 +1,16 @@ - package mage.sets; import mage.cards.ExpansionSet; +import mage.collation.BoosterCollator; +import mage.collation.BoosterStructure; +import mage.collation.CardRun; +import mage.collation.RarityConfiguration; import mage.constants.Rarity; import mage.constants.SetType; +import java.util.ArrayList; +import java.util.List; + /** * * @author fireshoes @@ -293,4 +299,113 @@ public final class DragonsOfTarkir extends ExpansionSet { cards.add(new SetCardInfo("Zurgo Bellstriker", 169, Rarity.RARE, mage.cards.z.ZurgoBellstriker.class)); } + @Override + public BoosterCollator createCollator() { + return new DragonsOfTarkirCollator(); + } +} + +// Booster collation info from https://www.lethe.xyz/mtg/collation/dtk.html +// Using USA collation for all rarities +// Foil rare sheet used for regular rares as regular rare sheet is not known +class DragonsOfTarkirCollator implements BoosterCollator { + private final CardRun commonA = new CardRun(true, "119", "148", "8", "91", "145", "40", "118", "140", "18", "113", "147", "9", "104", "135", "6", "110", "148", "32", "105", "139", "43", "109", "142", "34", "119", "155", "24", "127", "166", "14", "91", "143", "13", "99", "140", "40", "107", "145", "8", "113", "152", "9", "118", "135", "18", "109", "147", "43", "110", "142", "6", "105", "155", "32", "104", "139", "34", "107", "143", "24", "99", "166", "14", "127", "152", "13"); + private final CardRun commonB = new CardRun(true, "69", "200", "82", "188", "63", "178", "57", "179", "45", "174", "64", "211", "65", "183", "85", "206", "49", "195", "74", "203", "51", "183", "69", "179", "64", "171", "82", "174", "45", "200", "65", "211", "49", "188", "57", "178", "51", "206", "63", "203", "74", "171", "85", "195", "64", "200", "69", "174", "57", "211", "82", "183", "45", "178", "65", "188", "51", "203", "49", "171", "63", "179", "74", "206", "85", "195"); + private final CardRun commonC1 = new CardRun(true, "72", "11", "189", "153", "247", "90", "66", "22", "202", "161", "97", "72", "16", "185", "163", "240", "115", "53", "4", "170", "165", "103", "66", "11", "197", "153", "247", "90", "55", "29", "189", "128", "100", "53", "22", "170", "163", "240", "115", "44", "16", "202", "161", "97", "55", "4", "197", "165", "17", "100", "44", "29", "185", "128", "103"); + private final CardRun commonC2 = new CardRun(true, "157", "208", "234", "98", "25", "67", "244", "164", "81", "122", "248", "33", "98", "205", "68", "234", "157", "193", "122", "205", "67", "236", "160", "208", "244", "126", "25", "68", "248", "164", "193", "98", "234", "33", "67", "157", "208", "236", "126", "17", "25", "81", "160", "244", "164", "126", "205", "68", "248", "160", "193", "122", "236", "33", "81"); + private final CardRun uncommonA = new CardRun(true, "222", "239", "182", "75", "229", "7", "154", "50", "233", "241", "78", "1", "60", "123", "12", "196", "215", "125", "129", "191", "114", "182", "15", "154", "233", "75", "7", "241", "222", "239", "50", "78", "215", "191", "196", "182", "229", "1", "239", "123", "114", "12", "15", "129", "60", "229", "78", "50", "154", "241", "125", "75", "222", "7", "233", "129", "60", "123", "215", "191", "1", "125", "114", "196", "12", "15"); + private final CardRun uncommonB = new CardRun(true, "156", "199", "168", "186", "151", "176", "150", "231", "146", "190", "158", "172", "134", "207", "159", "184", "156", "201", "168", "192", "138", "198", "144", "199", "149", "186", "146", "190", "159", "231", "134", "176", "138", "207", "151", "172", "150", "184", "156", "201", "149", "198", "158", "186", "144", "192", "168", "199", "146", "192", "158", "231", "151", "176", "150", "190", "159", "172", "144", "207", "138", "198", "134", "201", "149", "184"); + private final CardRun uncommonC = new CardRun(true, "28", "77", "35", "79", "38", "83", "39", "56", "42", "76", "37", "54", "20", "79", "38", "71", "21", "84", "10", "76", "35", "46", "42", "77", "28", "83", "37", "56", "39", "54", "20", "84", "21", "46", "35", "79", "28", "71", "38", "83", "10", "76", "42", "56", "39", "77", "37", "46", "20", "54", "21", "84", "10", "71"); + private final CardRun uncommonD = new CardRun(true, "86", "243", "111", "235", "117", "246", "124", "245", "102", "5", "94", "95", "89", "237", "87", "238", "108", "242", "124", "243", "86", "95", "111", "5", "102", "246", "94", "245", "117", "238", "87", "235", "86", "242", "89", "237", "108", "243", "111", "246", "102", "245", "94", "124", "117", "5", "89", "235", "95", "238", "87", "242", "108", "237"); + private final CardRun rare = new CardRun(true, "137", "181", "31", "212", "19", "2", "106", "173", "216", "47", "210", "58", "92", "112", "213", "23", "27", "36", "175", "221", "93", "3", "59", "70", "218", "214", "26", "187", "249", "177", "223", "96", "116", "61", "88", "132", "221", "30", "131", "120", "220", "224", "101", "52", "62", "41", "136", "223", "73", "169", "92", "167", "226", "130", "58", "194", "230", "141", "224", "80", "181", "2", "209", "227", "180", "59", "121", "173", "162", "226", "19", "210", "217", "31", "228", "106", "61", "249", "175", "52", "48", "23", "47", "93", "3", "232", "132", "62", "219", "177", "112", "227", "26", "36", "96", "70", "133", "136", "214", "120", "131", "41", "228", "30", "225", "101", "209", "212", "141", "88", "167", "80", "204", "232", "169", "194", "130", "121", "213", "162", "187"); + private final CardRun land = new CardRun(false, "250", "251", "252", "253", "254", "255", "256", "257", "258", "259", "260", "261", "262", "263", "264"); + + private final BoosterStructure AABBC1C1C1C1C1C1 = new BoosterStructure( + commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AAABBC1C1C1C1C1 = new BoosterStructure( + commonA, commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AAAABBC2C2C2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, + commonC2, commonC2, commonC2, commonC2 + ); + private final BoosterStructure AAAABBBC2C2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, commonB, + commonC2, commonC2, commonC2 + ); + private final BoosterStructure AAAABBBBC2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, commonB, commonB, + commonC2, commonC2 + ); + private final BoosterStructure ABC = new BoosterStructure(uncommonA, uncommonB, uncommonC); + private final BoosterStructure ABD = new BoosterStructure(uncommonA, uncommonB, uncommonD); + private final BoosterStructure ACD = new BoosterStructure(uncommonA, uncommonC, uncommonD); + private final BoosterStructure BCD = new BoosterStructure(uncommonB, uncommonC, uncommonD); + private final BoosterStructure R1 = new BoosterStructure(rare); + private final BoosterStructure L1 = new BoosterStructure(land); + + // In order for equal numbers of each common to exist, the average booster must contain: + // 3.27 A commons (36 / 11) + // 2.18 B commons (24 / 11) + // 2.73 C1 commons (30 / 11, or 60 / 11 in each C1 booster) + // 1.82 C2 commons (20 / 11, or 40 / 11 in each C2 booster) + // These numbers are the same for all sets with 101 commons in A/B/C1/C2 print runs + // and with 10 common slots per booster + private final RarityConfiguration commonRuns = new RarityConfiguration( + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBBC2C2C2, + AAAABBBC2C2C2, + AAAABBBBC2C2 + ); + // In order for equal numbers of each uncommon to exist, the average booster must contain: + // 0.825 A uncommons (33 / 40) + // 0.825 B uncommons (33 / 40) + // 0.675 C uncommons (27 / 40) + // 0.675 D uncommons (27 / 40) + // These numbers are the same for all sets with 80 uncommons in asymmetrical A/B/C/D print runs + private final RarityConfiguration uncommonRuns = new RarityConfiguration( + ABC, ABD, ABC, ABD, ABC, ABD, ABC, ABD, ABC, ABD, ABC, ABD, ABC, + ABD, ABC, ABD, ABC, ABD, ABC, ABD, ABC, ABD, ABC, ABD, ABC, ABD, + ACD, BCD, ACD, BCD, ACD, BCD, ACD, + BCD, ACD, BCD, ACD, BCD, ACD, BCD + ); + private final RarityConfiguration rareRuns = new RarityConfiguration(R1); + private final RarityConfiguration landRuns = new RarityConfiguration(L1); + + @Override + public List makeBooster() { + List booster = new ArrayList<>(); + booster.addAll(commonRuns.getNext().makeRun()); + booster.addAll(uncommonRuns.getNext().makeRun()); + booster.addAll(rareRuns.getNext().makeRun()); + booster.addAll(landRuns.getNext().makeRun()); + return booster; + } } diff --git a/Mage.Sets/src/mage/sets/EighthEdition.java b/Mage.Sets/src/mage/sets/EighthEdition.java index 5419781c388..f135dca4eb4 100644 --- a/Mage.Sets/src/mage/sets/EighthEdition.java +++ b/Mage.Sets/src/mage/sets/EighthEdition.java @@ -1,9 +1,12 @@ package mage.sets; import mage.cards.ExpansionSet; +import mage.cards.repository.CardInfo; import mage.constants.Rarity; import mage.constants.SetType; +import java.util.List; + public final class EighthEdition extends ExpansionSet { private static final EighthEdition instance = new EighthEdition(); @@ -21,9 +24,6 @@ public final class EighthEdition extends ExpansionSet { this.numBoosterRare = 1; this.ratioBoosterMythic = 0; - // scryfall combines Eighth Edition and Eighth Edition Box sets in one, but xmage must split it - // reason: remove box's cards from booster? TODO: implement booster ignore settings for cards instead max card number - cards.add(new SetCardInfo("Abyssal Specter", 117, Rarity.UNCOMMON, mage.cards.a.AbyssalSpecter.class)); cards.add(new SetCardInfo("Air Elemental", 59, Rarity.UNCOMMON, mage.cards.a.AirElemental.class)); cards.add(new SetCardInfo("Aladdin's Ring", 291, Rarity.RARE, mage.cards.a.AladdinsRing.class)); @@ -101,6 +101,7 @@ public final class EighthEdition extends ExpansionSet { cards.add(new SetCardInfo("Drudge Skeletons", 129, Rarity.COMMON, mage.cards.d.DrudgeSkeletons.class)); cards.add(new SetCardInfo("Dusk Imp", 130, Rarity.COMMON, mage.cards.d.DuskImp.class)); cards.add(new SetCardInfo("Dwarven Demolition Team", 184, Rarity.UNCOMMON, mage.cards.d.DwarvenDemolitionTeam.class)); + cards.add(new SetCardInfo("Eager Cadet", "S1", Rarity.COMMON, mage.cards.e.EagerCadet.class)); cards.add(new SetCardInfo("Eastern Paladin", 131, Rarity.RARE, mage.cards.e.EasternPaladin.class)); cards.add(new SetCardInfo("Elfhame Palace", 324, Rarity.UNCOMMON, mage.cards.e.ElfhamePalace.class)); cards.add(new SetCardInfo("Elite Archers", 18, Rarity.RARE, mage.cards.e.EliteArchers.class)); @@ -111,6 +112,7 @@ public final class EighthEdition extends ExpansionSet { cards.add(new SetCardInfo("Elvish Piper", 244, Rarity.RARE, mage.cards.e.ElvishPiper.class)); cards.add(new SetCardInfo("Elvish Scrapper", 245, Rarity.UNCOMMON, mage.cards.e.ElvishScrapper.class)); cards.add(new SetCardInfo("Emperor Crocodile", 246, Rarity.RARE, mage.cards.e.EmperorCrocodile.class)); + cards.add(new SetCardInfo("Enormous Baloth", "S6", Rarity.UNCOMMON, mage.cards.e.EnormousBaloth.class)); cards.add(new SetCardInfo("Enrage", 185, Rarity.UNCOMMON, mage.cards.e.Enrage.class)); cards.add(new SetCardInfo("Ensnaring Bridge", 300, Rarity.RARE, mage.cards.e.EnsnaringBridge.class)); cards.add(new SetCardInfo("Evacuation", 76, Rarity.RARE, mage.cards.e.Evacuation.class)); @@ -139,6 +141,7 @@ public final class EighthEdition extends ExpansionSet { cards.add(new SetCardInfo("Giant Badger", 253, Rarity.COMMON, mage.cards.g.GiantBadger.class)); cards.add(new SetCardInfo("Giant Cockroach", 135, Rarity.COMMON, mage.cards.g.GiantCockroach.class)); cards.add(new SetCardInfo("Giant Growth", 254, Rarity.COMMON, mage.cards.g.GiantGrowth.class)); + cards.add(new SetCardInfo("Giant Octopus", "S3", Rarity.COMMON, mage.cards.g.GiantOctopus.class)); cards.add(new SetCardInfo("Giant Spider", 255, Rarity.COMMON, mage.cards.g.GiantSpider.class)); cards.add(new SetCardInfo("Glorious Anthem", 20, Rarity.RARE, mage.cards.g.GloriousAnthem.class)); cards.add(new SetCardInfo("Glory Seeker", 21, Rarity.COMMON, mage.cards.g.GlorySeeker.class)); @@ -281,6 +284,7 @@ public final class EighthEdition extends ExpansionSet { cards.add(new SetCardInfo("Sanctimony", 42, Rarity.UNCOMMON, mage.cards.s.Sanctimony.class)); cards.add(new SetCardInfo("Savannah Lions", 43, Rarity.RARE, mage.cards.s.SavannahLions.class)); cards.add(new SetCardInfo("Scathe Zombies", 160, Rarity.COMMON, mage.cards.s.ScatheZombies.class)); + cards.add(new SetCardInfo("Sea Eagle", "S4", Rarity.COMMON, mage.cards.s.SeaEagle.class)); cards.add(new SetCardInfo("Sea Monster", 99, Rarity.COMMON, mage.cards.s.SeaMonster.class)); cards.add(new SetCardInfo("Searing Wind", 218, Rarity.RARE, mage.cards.s.SearingWind.class)); cards.add(new SetCardInfo("Seasoned Marshal", 44, Rarity.UNCOMMON, mage.cards.s.SeasonedMarshal.class)); @@ -295,6 +299,7 @@ public final class EighthEdition extends ExpansionSet { cards.add(new SetCardInfo("Shivan Oasis", 326, Rarity.UNCOMMON, mage.cards.s.ShivanOasis.class)); cards.add(new SetCardInfo("Shock", 222, Rarity.COMMON, mage.cards.s.Shock.class)); cards.add(new SetCardInfo("Shock Troops", 223, Rarity.COMMON, mage.cards.s.ShockTroops.class)); + cards.add(new SetCardInfo("Silverback Ape", "S7", Rarity.UNCOMMON, mage.cards.s.SilverbackApe.class)); cards.add(new SetCardInfo("Sizzle", 224, Rarity.COMMON, mage.cards.s.Sizzle.class)); cards.add(new SetCardInfo("Skull of Orm", 313, Rarity.RARE, mage.cards.s.SkullOfOrm.class)); cards.add(new SetCardInfo("Slay", 164, Rarity.UNCOMMON, mage.cards.s.Slay.class)); @@ -350,12 +355,14 @@ public final class EighthEdition extends ExpansionSet { cards.add(new SetCardInfo("Urza's Tower", 330, Rarity.UNCOMMON, mage.cards.u.UrzasTower.class)); cards.add(new SetCardInfo("Vampiric Spirit", 170, Rarity.RARE, mage.cards.v.VampiricSpirit.class)); cards.add(new SetCardInfo("Venerable Monk", 55, Rarity.COMMON, mage.cards.v.VenerableMonk.class)); + cards.add(new SetCardInfo("Vengeance", "S2", Rarity.UNCOMMON, mage.cards.v.Vengeance.class)); cards.add(new SetCardInfo("Verduran Enchantress", 285, Rarity.RARE, mage.cards.v.VerduranEnchantress.class)); cards.add(new SetCardInfo("Vernal Bloom", 286, Rarity.RARE, mage.cards.v.VernalBloom.class)); cards.add(new SetCardInfo("Vexing Arcanix", 319, Rarity.RARE, mage.cards.v.VexingArcanix.class)); cards.add(new SetCardInfo("Viashino Sandstalker", 230, Rarity.UNCOMMON, mage.cards.v.ViashinoSandstalker.class)); cards.add(new SetCardInfo("Vicious Hunger", 171, Rarity.COMMON, mage.cards.v.ViciousHunger.class)); cards.add(new SetCardInfo("Vine Trellis", 287, Rarity.COMMON, mage.cards.v.VineTrellis.class)); + cards.add(new SetCardInfo("Vizzerdrix", "S5", Rarity.RARE, mage.cards.v.Vizzerdrix.class)); cards.add(new SetCardInfo("Volcanic Hammer", 231, Rarity.COMMON, mage.cards.v.VolcanicHammer.class)); cards.add(new SetCardInfo("Wall of Air", 113, Rarity.UNCOMMON, mage.cards.w.WallOfAir.class)); cards.add(new SetCardInfo("Wall of Spears", 320, Rarity.UNCOMMON, mage.cards.w.WallOfSpears.class)); @@ -373,15 +380,13 @@ public final class EighthEdition extends ExpansionSet { cards.add(new SetCardInfo("Yavimaya Enchantress", 290, Rarity.UNCOMMON, mage.cards.y.YavimayaEnchantress.class)); cards.add(new SetCardInfo("Zombify", 174, Rarity.UNCOMMON, mage.cards.z.Zombify.class)); cards.add(new SetCardInfo("Zur's Weirding", 116, Rarity.RARE, mage.cards.z.ZursWeirding.class)); - // 8ed Edition Box Set (we need to create own set) - // http://www.magiclibrarities.net/540-rarities-eighth-edition-box-set-cards-english-cards-index.html - // cards.add(new SetCardInfo("Eager Cadet", 1, Rarity.COMMON, mage.cards.e.EagerCadet.class)); - // cards.add(new SetCardInfo("Vengeance", 2, Rarity.UNCOMMON, mage.cards.v.Vengeance.class)); - // cards.add(new SetCardInfo("Sea Eagle", 4, Rarity.COMMON, mage.cards.s.SeaEagle.class)); - // cards.add(new SetCardInfo("Vizzerdrix", 5, Rarity.RARE, mage.cards.v.Vizzerdrix.class)); - // cards.add(new SetCardInfo("Enormous Baloth", 6, Rarity.UNCOMMON, mage.cards.e.EnormousBaloth.class)); - // cards.add(new SetCardInfo("Silverback Ape", 7, Rarity.UNCOMMON, mage.cards.s.SilverbackApe.class)); - } + @Override + protected List findCardsByRarity(Rarity rarity) { + List cardInfos = super.findCardsByRarity(rarity); + // card numbers containing S are Starter Set cards not found in boosters + cardInfos.removeIf(cardInfo -> cardInfo.getCardNumber().contains("S")); + return cardInfos; + } } diff --git a/Mage.Sets/src/mage/sets/EighthEditionBox.java b/Mage.Sets/src/mage/sets/EighthEditionBox.java deleted file mode 100644 index f9fbcce4b1a..00000000000 --- a/Mage.Sets/src/mage/sets/EighthEditionBox.java +++ /dev/null @@ -1,29 +0,0 @@ -package mage.sets; - -import mage.cards.ExpansionSet; -import mage.constants.Rarity; -import mage.constants.SetType; - -public final class EighthEditionBox extends ExpansionSet { - - private static final EighthEditionBox instance = new EighthEditionBox(); - - public static EighthEditionBox getInstance() { - return instance; - } - - private EighthEditionBox() { - super("Eighth Edition Box", "8EB", ExpansionSet.buildDate(2003, 7, 28), SetType.CORE); - this.hasBoosters = false; - this.hasBasicLands = false; - - // http://www.magiclibrarities.net/540-rarities-eighth-edition-box-set-cards-english-cards-index.html - cards.add(new SetCardInfo("Eager Cadet", "S1", Rarity.COMMON, mage.cards.e.EagerCadet.class)); - cards.add(new SetCardInfo("Enormous Baloth", "S6", Rarity.UNCOMMON, mage.cards.e.EnormousBaloth.class)); - cards.add(new SetCardInfo("Giant Octopus", "S3", Rarity.COMMON, mage.cards.g.GiantOctopus.class)); - cards.add(new SetCardInfo("Sea Eagle", "S4", Rarity.COMMON, mage.cards.s.SeaEagle.class)); - cards.add(new SetCardInfo("Silverback Ape", "S7", Rarity.UNCOMMON, mage.cards.s.SilverbackApe.class)); - cards.add(new SetCardInfo("Vengeance", "S2", Rarity.UNCOMMON, mage.cards.v.Vengeance.class)); - cards.add(new SetCardInfo("Vizzerdrix", "S5", Rarity.RARE, mage.cards.v.Vizzerdrix.class)); - } -} diff --git a/Mage.Sets/src/mage/sets/EldritchMoon.java b/Mage.Sets/src/mage/sets/EldritchMoon.java index a4c43aecacb..f52abdcc3e0 100644 --- a/Mage.Sets/src/mage/sets/EldritchMoon.java +++ b/Mage.Sets/src/mage/sets/EldritchMoon.java @@ -1,8 +1,20 @@ package mage.sets; +import mage.cards.Card; import mage.cards.ExpansionSet; +import mage.cards.repository.CardCriteria; +import mage.cards.repository.CardInfo; +import mage.cards.repository.CardRepository; +import mage.collation.BoosterCollator; +import mage.collation.BoosterStructure; +import mage.collation.CardRun; +import mage.collation.RarityConfiguration; import mage.constants.Rarity; import mage.constants.SetType; +import mage.util.RandomUtil; + +import java.util.ArrayList; +import java.util.List; /** * @author fireshoes @@ -26,6 +38,7 @@ public final class EldritchMoon extends ExpansionSet { this.numBoosterUncommon = 3; this.numBoosterRare = 1; this.ratioBoosterMythic = 8; + this.ratioBoosterSpecialCommon = 8; this.numBoosterDoubleFaced = 1; cards.add(new SetCardInfo("Abandon Reason", 115, Rarity.UNCOMMON, mage.cards.a.AbandonReason.class)); @@ -252,4 +265,194 @@ public final class EldritchMoon extends ExpansionSet { cards.add(new SetCardInfo("Woodland Patrol", 180, Rarity.COMMON, mage.cards.w.WoodlandPatrol.class)); cards.add(new SetCardInfo("Wretched Gryff", 12, Rarity.COMMON, mage.cards.w.WretchedGryff.class)); } + + // add common or uncommon double faced card to booster + // 60/120 packs contain one of 4 common DFCs and 60/120 packs contain one of 10 uncommon DFCs + @Override + protected void addDoubleFace(List booster) { + Rarity rarity; + for (int i = 0; i < numBoosterDoubleFaced; i++) { + if (RandomUtil.nextInt(120) < 60) { + rarity = Rarity.COMMON; + } else { + rarity = Rarity.UNCOMMON; + } + addToBooster(booster, getSpecialCardsByRarity(rarity)); + } + } + + // Then about an eighth of the packs will have a second double-faced card, which will be a rare or mythic rare + // 10/12 of such packs contain one of 5 rare DFCs and 2/12 packs contain one of 2 mythic DFCs + @Override + protected void addSpecialCards(List booster, int number) { + // number is here always 1 + Rarity rarity; + if (RandomUtil.nextInt(12) < 10) { + rarity = Rarity.RARE; + } else { + rarity = Rarity.MYTHIC; + } + addToBooster(booster, getSpecialCardsByRarity(rarity)); + } + + // xmage doesn't recognize meldable cards as DFCs, so have to add them manually for now + private static final String[] commonMeldCards = {"Graf Rats", "Midnight Scavengers"}; + private static final String[] rareMeldCards = {"Bruna, the Fading Light", "Hanweir Battlements", "Hanweir Garrison"}; + private static final String[] mythicMeldCards = {"Gisela, the Broken Blade"}; + + @Override + protected List findSpecialCardsByRarity(Rarity rarity) { + List cardInfos = super.findSpecialCardsByRarity(rarity); + String[] meldCardNames = {}; + if (rarity == Rarity.COMMON) { + meldCardNames = commonMeldCards; + } else if (rarity == Rarity.RARE) { + meldCardNames = rareMeldCards; + } else if (rarity == Rarity.MYTHIC) { + meldCardNames = mythicMeldCards; + } + for (String name : meldCardNames) { + cardInfos.addAll(CardRepository.instance.findCards(new CardCriteria() + .setCodes(this.code) + .nameExact(name))); + } + return cardInfos; + } + + @Override + public BoosterCollator createCollator() { + return new EldritchMoonCollator(); + } +} + +// Booster collation info from https://www.lethe.xyz/mtg/collation/emn.html +// Using USA collation for common/uncommon, rare collation inferred from other sets +class EldritchMoonCollator implements BoosterCollator { + private final CardRun commonA = new CardRun(true, "12", "126", "42", "81", "76", "77", "14", "177", "122", "57", "138", "19", "166", "194", "100", "21", "126", "12", "129", "105", "42", "150", "76", "66", "16", "172", "81", "77", "194", "19", "122", "177", "57", "14", "138", "166", "100", "76", "21", "66", "105", "150", "42", "194", "129", "12", "126", "16", "81", "172", "77", "19", "138", "122", "166", "177", "14", "57", "100", "21", "66", "172", "129", "16", "105", "150"); + private final CardRun commonB = new CardRun(true, "179", "84", "173", "83", "154", "108", "160", "89", "178", "107", "151", "112", "180", "113", "8", "82", "179", "84", "173", "83", "167", "108", "178", "104", "154", "89", "180", "107", "179", "112", "160", "113", "8", "84", "173", "89", "151", "82", "178", "104", "160", "108", "167", "83", "154", "112", "151", "82", "180", "113", "167", "104", "8", "107"); + private final CardRun commonC1 = new CardRun(true, "139", "48", "59", "121", "192", "35", "60", "145", "31", "52", "147", "26", "78", "116", "195", "43", "67", "139", "35", "60", "147", "26", "52", "121", "48", "192", "59", "145", "31", "78", "116", "43", "67", "139", "35", "60", "121", "192", "48", "59", "147", "26", "52", "145", "31", "78", "116", "195", "43", "67", "139", "35", "59", "121", "48", "52", "147", "26", "192", "60", "145", "43", "78", "116", "31", "67"); + private final CardRun commonC2 = new CardRun(true, "127", "32", "58", "120", "44", "74", "201", "144", "30", "53", "135", "32", "55", "127", "25", "195", "120", "44", "58", "144", "30", "53", "135", "25", "74", "144", "32", "55", "201", "120", "30", "58", "127", "44", "53", "135", "201", "55", "127", "32", "74", "144", "25", "58", "195", "44", "74", "120", "30", "55", "201", "135", "25", "53"); + private final CardRun uncommonA = new CardRun(true, "36", "71", "198", "128", "79", "114", "27", "68", "140", "205", "102", "34", "103", "119", "196", "61", "101", "182", "202", "13", "141", "92", "157", "38", "114", "128", "71", "198", "90", "36", "115", "68", "10", "205", "140", "101", "79", "27", "119", "102", "202", "10", "34", "103", "13", "115", "157", "92", "182", "38", "141", "61", "196", "90"); + private final CardRun uncommonB = new CardRun(true, "18", "149", "152", "11", "24", "159", "188", "134", "73", "158", "62", "164", "29", "97", "125", "187", "95", "4", "161", "45", "50", "169", "110", "22", "186", "134", "88", "153", "49", "159", "133", "1", "152", "18", "11", "97", "158", "9", "143", "73", "161", "24", "94", "164", "62", "95", "187", "149", "45", "153", "4", "110", "188", "22", "88", "50", "125", "169", "1", "186", "143", "49", "29", "133", "94", "9"); + private final CardRun commonDFC = new CardRun(true, "148", "124", "23", "63", "175", "91", "96a", "142", "148", "54", "91", "175", "163", "148", "33", "168", "91", "96a", "174", "175", "63", "193", "96a", "124", "54", "142", "148", "91", "96a", "23", "175", "163", "91", "148", "174", "175", "168", "63", "91", "96a", "193", "148", "33", "124", "96a", "142", "23", "54", "175", "91", "96a", "163", "148", "174", "33", "175", "168", "148", "193", "124", "91", "96a", "23", "175", "33", "148", "175", "142", "54", "63", "148", "91", "96a", "163", "175", "174", "91", "96a", "33", "148", "168", "23", "91", "96a", "193", "175", "63", "54", "148", "124", "142", "163", "175", "91", "96a", "174", "148", "168", "193", "96a", "63", "175", "124", "54", "91", "96a", "23", "148", "142", "91", "175", "163", "33", "174", "148", "91", "96a", "168", "175", "193"); + private final CardRun rare = new CardRun(false, "3", "5", "7", "17", "37", "39", "40", "41", "46", "47", "51", "64", "65", "69", "72", "75", "80", "85", "86", "87", "98", "99", "106", "117", "118", "123", "131", "132", "146", "155", "156", "165", "170", "171", "176", "181", "185", "189", "197", "199", "200", "203"); + private final CardRun mythic = new CardRun(false, "2", "6", "20", "70", "93", "109", "136", "137", "162", "183", "184", "190"); + private final CardRun rareDFC = new CardRun(false, "15a", "56", "111", "130a", "204", "15a", "56", "111", "130a", "204", "28", "191"); + private final CardRun land = new CardRun(false, "SOI_283", "SOI_284", "SOI_285", "SOI_286", "SOI_287", "SOI_288", "SOI_289", "SOI_290", "SOI_291", "SOI_292", "SOI_293", "SOI_294", "SOI_295", "SOI_296", "SOI_297"); + + private final BoosterStructure AABBC1C1C1C1C1 = new BoosterStructure( + commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AABBBC1C1C1C1 = new BoosterStructure( + commonA, commonA, + commonB, commonB, commonB, + commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AAABBC1C1C1C1 = new BoosterStructure( + commonA, commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AABBBC2C2C2C2 = new BoosterStructure( + commonA, commonA, + commonB, commonB, commonB, + commonC2, commonC2, commonC2, commonC2 + ); + private final BoosterStructure AAABBC2C2C2C2 = new BoosterStructure( + commonA, commonA, commonA, + commonB, commonB, + commonC2, commonC2, commonC2, commonC2 + ); + private final BoosterStructure AAABBBC2C2C2 = new BoosterStructure( + commonA, commonA, commonA, + commonB, commonB, commonB, + commonC2, commonC2, commonC2 + ); + private final BoosterStructure AAAABBC2C2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, + commonC2, commonC2, commonC2 + ); + private final BoosterStructure AABBC2C2C2C2D = new BoosterStructure( + commonA, commonA, + commonB, commonB, + commonC2, commonC2, commonC2, commonC2, + rareDFC + ); + private final BoosterStructure AABBBC2C2C2D = new BoosterStructure( + commonA, commonA, + commonB, commonB, commonB, + commonC2, commonC2, commonC2, + rareDFC + ); + private final BoosterStructure AAABBC2C2C2D = new BoosterStructure( + commonA, commonA, commonA, + commonB, commonB, + commonC2, commonC2, commonC2, + rareDFC + ); + private final BoosterStructure AAB = new BoosterStructure(uncommonA, uncommonA, uncommonB); + private final BoosterStructure ABB = new BoosterStructure(uncommonA, uncommonB, uncommonB); + private final BoosterStructure R1 = new BoosterStructure(rare); + private final BoosterStructure M1 = new BoosterStructure(mythic); + private final BoosterStructure D1 = new BoosterStructure(commonDFC); + private final BoosterStructure L1 = new BoosterStructure(land); + + // In order for equal numbers of each common to exist, the average booster must contain: + // 2.789 A commons (1562 / 560) (rounded to 67/24) + // 2.282 B commons (1278 / 560) (rounded to 55/24) + // 2.092 C1 commons (2343 / 1120) (rounded to 50/24) + // 1.712 C2 commons (1917 / 1120) (rounded to 41/24) + private final RarityConfiguration commonRuns = new RarityConfiguration( + AABBC1C1C1C1C1, + AABBC1C1C1C1C1, + AABBBC1C1C1C1, + AABBBC1C1C1C1, + AAABBC1C1C1C1, + AAABBC1C1C1C1, + AAABBC1C1C1C1, + AAABBC1C1C1C1, + AAABBC1C1C1C1, + AAABBC1C1C1C1, + AAABBC1C1C1C1, + AAABBC1C1C1C1, + + AABBBC2C2C2C2, + AABBBC2C2C2C2, + AAABBC2C2C2C2, + AAABBC2C2C2C2, + AAABBBC2C2C2, + AAABBBC2C2C2, + AAAABBC2C2C2, + AAAABBC2C2C2, + AAAABBC2C2C2, + + AABBC2C2C2C2D, + AABBBC2C2C2D, + AAABBC2C2C2D + ); + // In order for equal numbers of each uncommon to exist, the average booster must contain: + // 1.35 A uncommons (27 / 20) + // 1.65 B uncommons (33 / 20) + // These numbers are the same for all sets with 60 uncommons in asymmetrical A/B print runs + private final RarityConfiguration uncommonRuns = new RarityConfiguration( + AAB, AAB, AAB, AAB, AAB, AAB, AAB, + ABB, ABB, ABB, ABB, ABB, ABB, ABB, ABB, ABB, ABB, ABB, ABB, ABB + ); + private final RarityConfiguration rareRuns = new RarityConfiguration(R1, R1, R1, R1, R1, R1, R1, M1); + private final RarityConfiguration dfcRuns = new RarityConfiguration(D1); + private final RarityConfiguration landRuns = new RarityConfiguration(L1); + + @Override + public List makeBooster() { + List booster = new ArrayList<>(); + booster.addAll(commonRuns.getNext().makeRun()); + booster.addAll(uncommonRuns.getNext().makeRun()); + booster.addAll(rareRuns.getNext().makeRun()); + booster.addAll(dfcRuns.getNext().makeRun()); + booster.addAll(landRuns.getNext().makeRun()); + return booster; + } } diff --git a/Mage.Sets/src/mage/sets/FateReforged.java b/Mage.Sets/src/mage/sets/FateReforged.java index 3b6abc7104b..d18c19a1c61 100644 --- a/Mage.Sets/src/mage/sets/FateReforged.java +++ b/Mage.Sets/src/mage/sets/FateReforged.java @@ -1,16 +1,21 @@ - package mage.sets; -import java.util.ArrayList; -import java.util.List; - +import mage.cards.Card; import mage.cards.ExpansionSet; import mage.cards.repository.CardCriteria; import mage.cards.repository.CardInfo; import mage.cards.repository.CardRepository; +import mage.collation.BoosterCollator; +import mage.collation.BoosterStructure; +import mage.collation.CardRun; +import mage.collation.RarityConfiguration; import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.SetType; +import mage.util.RandomUtil; + +import java.util.ArrayList; +import java.util.List; /** * @author fireshoes @@ -19,9 +24,6 @@ public final class FateReforged extends ExpansionSet { private static final FateReforged instance = new FateReforged(); - private List savedSpecialRares = new ArrayList<>(); - private List savedSpecialCommon = new ArrayList<>(); - public static FateReforged getInstance() { return instance; } @@ -37,7 +39,6 @@ public final class FateReforged extends ExpansionSet { this.numBoosterCommon = 10; this.numBoosterUncommon = 3; this.numBoosterRare = 1; - this.numBoosterDoubleFaced = -1; this.ratioBoosterMythic = 8; cards.add(new SetCardInfo("Abzan Advantage", 2, Rarity.COMMON, mage.cards.a.AbzanAdvantage.class)); @@ -228,60 +229,92 @@ public final class FateReforged extends ExpansionSet { } @Override - public List getCardsByRarity(Rarity rarity) { - // Common cards retrievement of Fate Reforged boosters - prevent the retrievement of the common lands (e.g. Blossoming Sands) - if (rarity == Rarity.COMMON) { - List savedCardsInfos = savedCards.get(rarity); - if (savedCardsInfos == null) { - CardCriteria criteria = new CardCriteria(); - criteria.rarities(Rarity.COMMON); - criteria.setCodes(this.code).notTypes(CardType.LAND); - savedCardsInfos = CardRepository.instance.findCards(criteria); - if (maxCardNumberInBooster != Integer.MAX_VALUE) { - savedCardsInfos.removeIf(next -> next.getCardNumberAsInt() > maxCardNumberInBooster); - } - savedCards.put(rarity, savedCardsInfos); - } - // Return a copy of the saved cards information, as not to let modify the original. - return new ArrayList<>(savedCardsInfos); + protected void addSpecialCards(List booster, int number) { + // number is here always 1 + Rarity rarity; + if (RandomUtil.nextInt(24) < 23) { + rarity = Rarity.COMMON; } else { - return super.getCardsByRarity(rarity); + rarity = Rarity.RARE; } + addToBooster(booster, getSpecialCardsByRarity(rarity)); } @Override - public List getSpecialCommon() { - if (savedSpecialCommon.isEmpty()) { - // the 10 common lands from Fate Reforged can show up in the basic lands slot - // http://magic.wizards.com/en/articles/archive/feature/fetching-look-fate-reforged-2014-12-24 - CardCriteria criteria = new CardCriteria(); - criteria.rarities(Rarity.COMMON).setCodes(this.code).types(CardType.LAND); - savedSpecialCommon = CardRepository.instance.findCards(criteria); - criteria.rarities(Rarity.LAND).setCodes(this.code); - savedSpecialCommon.addAll(CardRepository.instance.findCards(criteria)); + protected List findSpecialCardsByRarity(Rarity rarity) { + CardCriteria criteria = new CardCriteria(); + criteria.rarities(rarity).types(CardType.LAND); + if (rarity == Rarity.RARE) { + // fetchlands + criteria.setCodes("KTK"); + } else { + // gainlands + criteria.setCodes(this.code); } - return new ArrayList<>(savedSpecialCommon); + return CardRepository.instance.findCards(criteria); } @Override - public List getSpecialRare() { - if (savedSpecialRares.isEmpty()) { - CardCriteria criteria = new CardCriteria(); - criteria.setCodes("KTK").name("Bloodstained Mire"); - savedSpecialRares.addAll(CardRepository.instance.findCards(criteria)); - criteria = new CardCriteria(); - criteria.setCodes("KTK").name("Flooded Strand"); - savedSpecialRares.addAll(CardRepository.instance.findCards(criteria)); - criteria = new CardCriteria(); - criteria.setCodes("KTK").name("Polluted Delta"); - savedSpecialRares.addAll(CardRepository.instance.findCards(criteria)); - criteria = new CardCriteria(); - criteria.setCodes("KTK").name("Windswept Heath"); - savedSpecialRares.addAll(CardRepository.instance.findCards(criteria)); - criteria = new CardCriteria(); - criteria.setCodes("KTK").name("Wooded Foothills"); - savedSpecialRares.addAll(CardRepository.instance.findCards(criteria)); - } - return new ArrayList<>(savedSpecialRares); + protected void generateBoosterMap() { + super.generateBoosterMap(); + CardRepository + .instance + .findCards(new CardCriteria().setCodes("KTK").rarities(Rarity.RARE).types(CardType.LAND)) + .stream() + .forEach(cardInfo -> inBoosterMap.put("KTK_" + cardInfo.getCardNumber(), cardInfo)); + } + + @Override + public BoosterCollator createCollator() { + return new FateReforgedCollator(); + } +} + +// Booster collation info from https://www.lethe.xyz/mtg/collation/frf.html +// Using USA collation for all rarities +// Foil rare sheets used for regular rares as regular rare sheet is not known +class FateReforgedCollator implements BoosterCollator { + private final CardRun commonA = new CardRun(true, "5", "57", "82", "53", "24", "38", "86", "153", "21", "6", "39", "73", "152", "2", "47", "68", "58", "4", "33", "89", "153", "10", "45", "72", "86", "3", "31", "61", "158", "26", "53", "81", "154", "21", "57", "85", "37", "24", "38", "82", "150", "5", "2", "31", "89", "152", "10", "33", "72", "47", "26", "58", "61", "150", "3", "45", "81", "73", "6", "39", "85", "154", "4", "37", "68", "158"); + private final CardRun commonB = new CardRun(true, "106", "121", "25", "115", "133", "107", "129", "88", "116", "135", "108", "124", "98", "128", "13", "92", "130", "96", "121", "60", "103", "144", "107", "122", "59", "102", "134", "95", "129", "25", "116", "133", "98", "140", "88", "108", "128", "106", "124", "115", "135", "13", "102", "122", "103", "130", "60", "96", "144", "95", "140", "59", "92", "134"); + private final CardRun uncommon = new CardRun(true, "104", "49", "32", "67", "46", "139", "14", "101", "83", "23", "160", "16", "76", "69", "126", "105", "35", "97", "30", "159", "80", "125", "118", "77", "34", "51", "7", "40", "94", "28", "78", "136", "114", "161", "48", "42", "12", "66", "83", "120", "117", "44", "18", "67", "127", "15", "112", "141", "32", "29", "132", "164", "63", "123", "119", "93", "71", "41", "101", "111", "17", "49", "162", "74", "80", "139", "147", "30", "104", "46", "69", "77", "14", "105", "159", "164", "118", "23", "63", "160", "123", "97", "93", "141", "41", "44", "126", "17", "136", "34", "112", "76", "78", "71", "162", "16", "114", "7", "161", "120", "51", "35", "74", "66", "18", "125", "40", "42", "132", "28", "147", "12", "94", "48", "119", "29", "15", "117", "127", "111"); + private final CardRun rareA = new CardRun(true, "62", "142", "109", "1", "54", "22", "163", "149", "75", "148", "56", "99", "52", "167", "11", "156", "87", "70", "137", "155", "90", "36", "157", "138", "145", "100", "19", "84", "62", "146", "91", "55", "148", "22", "50", "109", "149", "9", "113", "151", "138", "163", "142", "99", "87", "27", "11", "167", "137", "54", "90", "64", "75", "36", "155", "157", "52", "100", "143", "84", "156", "146", "19", "91", "20", "50", "9", "151"); + private final CardRun rareB = new CardRun(true, "65", "131", "8", "43", "79", "110"); + private final CardRun landCommon = new CardRun(true, "165", "169", "166", "171", "168", "165", "174", "171", "173", "172", "166", "170", "169", "175", "168", "173", "170", "165", "172", "169", "174", "166", "175", "171", "168", "170", "172", "174", "175", "173"); + private final CardRun landRare = new CardRun(false, "KTK_230", "KTK_233", "KTK_239", "KTK_248", "KTK_249"); + + private final BoosterStructure AAAAABBBBB = new BoosterStructure( + commonA, commonA, commonA, commonA, commonA, + commonB, commonB, commonB, commonB, commonB + ); + private final BoosterStructure AAAAAABBBB = new BoosterStructure( + commonA, commonA, commonA, commonA, commonA, commonA, + commonB, commonB, commonB, commonB + ); + private final BoosterStructure U1 = new BoosterStructure(uncommon, uncommon, uncommon); + private final BoosterStructure R1 = new BoosterStructure(rareA); + private final BoosterStructure R2 = new BoosterStructure(rareB); + private final BoosterStructure L1 = new BoosterStructure(landCommon); + private final BoosterStructure L2 = new BoosterStructure(landRare); + + private final RarityConfiguration commonRuns = new RarityConfiguration(AAAAABBBBB, AAAAAABBBB); + private final RarityConfiguration uncommonRuns = new RarityConfiguration(U1); + private final RarityConfiguration rareRuns = new RarityConfiguration( + R1, R1, R1, R1, R1, R1, R1, R1, R1, R1, + R1, R1, R1, R1, R1, R1, R1, R2, R2, R2 + ); + private final RarityConfiguration landRuns = new RarityConfiguration( + L1, L1, L1, L1, L1, L1, L1, L1, + L1, L1, L1, L1, L1, L1, L1, L1, + L1, L1, L1, L1, L1, L1, L1, L2 + ); + + @Override + public List makeBooster() { + List booster = new ArrayList<>(); + booster.addAll(commonRuns.getNext().makeRun()); + booster.addAll(uncommonRuns.getNext().makeRun()); + booster.addAll(rareRuns.getNext().makeRun()); + booster.addAll(landRuns.getNext().makeRun()); + return booster; } } diff --git a/Mage.Sets/src/mage/sets/ForgottenRealmsCommander.java b/Mage.Sets/src/mage/sets/ForgottenRealmsCommander.java index d75528bf51b..90c4f3ffec5 100644 --- a/Mage.Sets/src/mage/sets/ForgottenRealmsCommander.java +++ b/Mage.Sets/src/mage/sets/ForgottenRealmsCommander.java @@ -42,6 +42,7 @@ public final class ForgottenRealmsCommander extends ExpansionSet { cards.add(new SetCardInfo("Bedevil", 179, Rarity.RARE, mage.cards.b.Bedevil.class)); cards.add(new SetCardInfo("Behemoth Sledge", 180, Rarity.UNCOMMON, mage.cards.b.BehemothSledge.class)); cards.add(new SetCardInfo("Belt of Giant Strength", 38, Rarity.RARE, mage.cards.b.BeltOfGiantStrength.class)); + cards.add(new SetCardInfo("Berserker's Frenzy", 29, Rarity.RARE, mage.cards.b.BerserkersFrenzy.class)); cards.add(new SetCardInfo("Bituminous Blast", 181, Rarity.UNCOMMON, mage.cards.b.BituminousBlast.class)); cards.add(new SetCardInfo("Bogardan Hellkite", 115, Rarity.MYTHIC, mage.cards.b.BogardanHellkite.class)); cards.add(new SetCardInfo("Bojuka Bog", 226, Rarity.COMMON, mage.cards.b.BojukaBog.class)); @@ -78,6 +79,7 @@ public final class ForgottenRealmsCommander extends ExpansionSet { cards.add(new SetCardInfo("Dark-Dweller Oracle", 119, Rarity.RARE, mage.cards.d.DarkDwellerOracle.class)); cards.add(new SetCardInfo("Darkwater Catacombs", 232, Rarity.RARE, mage.cards.d.DarkwaterCatacombs.class)); cards.add(new SetCardInfo("Dead Man's Chest", 97, Rarity.RARE, mage.cards.d.DeadMansChest.class)); + cards.add(new SetCardInfo("Death Tyrant", 23, Rarity.RARE, mage.cards.d.DeathTyrant.class)); cards.add(new SetCardInfo("Decree of Savagery", 156, Rarity.RARE, mage.cards.d.DecreeOfSavagery.class)); cards.add(new SetCardInfo("Demanding Dragon", 120, Rarity.RARE, mage.cards.d.DemandingDragon.class)); cards.add(new SetCardInfo("Desert", 233, Rarity.UNCOMMON, mage.cards.d.Desert.class)); @@ -271,6 +273,7 @@ public final class ForgottenRealmsCommander extends ExpansionSet { cards.add(new SetCardInfo("Valorous Stance", 76, Rarity.UNCOMMON, mage.cards.v.ValorousStance.class)); cards.add(new SetCardInfo("Vandalblast", 148, Rarity.UNCOMMON, mage.cards.v.Vandalblast.class)); cards.add(new SetCardInfo("Vanish into Memory", 196, Rarity.UNCOMMON, mage.cards.v.VanishIntoMemory.class)); + cards.add(new SetCardInfo("Vengeful Ancestor", 35, Rarity.RARE, mage.cards.v.VengefulAncestor.class)); cards.add(new SetCardInfo("Verdant Embrace", 173, Rarity.RARE, mage.cards.v.VerdantEmbrace.class)); cards.add(new SetCardInfo("Victimize", 112, Rarity.UNCOMMON, mage.cards.v.Victimize.class)); cards.add(new SetCardInfo("Viridian Longbow", 221, Rarity.COMMON, mage.cards.v.ViridianLongbow.class)); diff --git a/Mage.Sets/src/mage/sets/GuildsOfRavnica.java b/Mage.Sets/src/mage/sets/GuildsOfRavnica.java index 2dcf4088b88..75cf56fd8e5 100644 --- a/Mage.Sets/src/mage/sets/GuildsOfRavnica.java +++ b/Mage.Sets/src/mage/sets/GuildsOfRavnica.java @@ -1,16 +1,17 @@ package mage.sets; +import mage.cards.ExpansionSet; +import mage.collation.BoosterCollator; +import mage.collation.BoosterStructure; +import mage.collation.CardRun; +import mage.collation.RarityConfiguration; +import mage.cards.repository.CardInfo; +import mage.constants.Rarity; +import mage.constants.SetType; + import java.util.ArrayList; import java.util.List; -import mage.cards.ExpansionSet; -import mage.cards.repository.CardCriteria; -import mage.cards.repository.CardInfo; -import mage.cards.repository.CardRepository; -import mage.constants.CardType; -import mage.constants.Rarity; -import mage.constants.SetType; - public final class GuildsOfRavnica extends ExpansionSet { private static final GuildsOfRavnica instance = new GuildsOfRavnica(); @@ -23,12 +24,12 @@ public final class GuildsOfRavnica extends ExpansionSet { super("Guilds of Ravnica", "GRN", ExpansionSet.buildDate(2018, 10, 5), SetType.EXPANSION); this.blockName = "Guilds of Ravnica"; this.hasBoosters = true; - this.numBoosterSpecial = 1; - this.numBoosterLands = 0; + this.numBoosterLands = 1; this.numBoosterCommon = 10; this.numBoosterUncommon = 3; this.numBoosterRare = 1; this.ratioBoosterMythic = 8; + this.ratioBoosterSpecialLand = 1; // replace all basic lands this.maxCardNumberInBooster = 259; cards.add(new SetCardInfo("Affectionate Indrik", 121, Rarity.UNCOMMON, mage.cards.a.AffectionateIndrik.class)); @@ -307,40 +308,107 @@ public final class GuildsOfRavnica extends ExpansionSet { } @Override - public List getCardsByRarity(Rarity rarity) { - if (rarity == Rarity.COMMON) { - List savedCardsInfos = savedCards.get(rarity); - if (savedCardsInfos == null) { - CardCriteria criteria = new CardCriteria(); - criteria.setCodes(this.code).notTypes(CardType.LAND); - criteria.rarities(rarity).doubleFaced(false); - savedCardsInfos = CardRepository.instance.findCards(criteria); - if (maxCardNumberInBooster != Integer.MAX_VALUE) { - savedCardsInfos.removeIf(next -> next.getCardNumberAsInt() > maxCardNumberInBooster); - } - criteria = new CardCriteria(); - // Gateway Plaza is a normal common: https://twitter.com/EliShffrn/status/1043156989218414593s - criteria.setCodes(this.code).nameExact("Gateway Plaza"); - savedCardsInfos.addAll(CardRepository.instance.findCards(criteria)); - savedCards.put(rarity, savedCardsInfos); - } - // Return a copy of the saved cards information, as not to modify the original. - return new ArrayList<>(savedCardsInfos); - } else { - return super.getCardsByRarity(rarity); + protected List findSpecialCardsByRarity(Rarity rarity) { + List cardInfos = super.findSpecialCardsByRarity(rarity); + if (rarity == Rarity.LAND) { + // Gateway Plaza is a normal common + cardInfos.removeIf(cardInfo -> "Gateway Plaza".equals(cardInfo.getName())); } + return cardInfos; } @Override - public List getSpecialCommon() { - List specialCards = getCardsByRarity(Rarity.SPECIAL); - if (specialCards.isEmpty()) { - CardCriteria criteria = new CardCriteria(); - criteria.rarities(Rarity.COMMON).setCodes(this.code).name("Guildgate"); - List specialCardsSave = CardRepository.instance.findCards(criteria); - savedCards.put(Rarity.SPECIAL, specialCardsSave); - specialCards.addAll(specialCardsSave); - } - return specialCards; + public BoosterCollator createCollator() { + return new GuildsOfRavnicaCollator(); + } +} + +// Booster collation info from https://www.lethe.xyz/mtg/collation/grn.html +// Using USA collation for all rarities +// Foil rare sheet used for regular rares as regular rare sheet is not known +class GuildsOfRavnicaCollator implements BoosterCollator { + private final CardRun commonA = new CardRun(true, "14", "60", "118", "5", "34", "101", "18", "37", "92", "29", "42", "100", "27", "43", "119", "15", "56", "95", "17", "52", "104", "20", "60", "118", "23", "57", "112", "25", "58", "120", "28", "36", "114", "5", "50", "111", "14", "34", "101", "17", "43", "92", "29", "37", "119", "15", "57", "100", "23", "42", "104", "18", "56", "95", "27", "58", "120", "28", "52", "112", "20", "36", "114", "25", "50", "111"); + private final CardRun commonB = new CardRun(true, "126", "61", "129", "62", "127", "89", "147", "85", "150", "65", "146", "64", "149", "78", "133", "70", "139", "88", "135", "62", "142", "80", "126", "61", "127", "89", "129", "85", "150", "86", "147", "64", "139", "65", "146", "62", "142", "70", "135", "78", "149", "88", "147", "80", "133", "61", "127", "89", "126", "85", "129", "86", "150", "65", "149", "70", "146", "78", "139", "88", "135", "64", "142", "80", "133", "86"); + private final CardRun commonC1 = new CardRun(true, "105", "234", "158", "164", "35", "169", "198", "220", "3", "199", "196", "216", "76", "1", "94", "31", "237", "110", "72", "219", "178", "143", "176", "234", "21", "3", "217", "218", "216", "151", "200", "35", "196", "198", "164", "158", "220", "31", "169", "110", "72", "1", "199", "237", "94", "76", "21", "219", "217", "218", "176", "151", "200", "178", "143"); + private final CardRun commonC2 = new CardRun(true, "105", "59", "140", "197", "238", "68", "134", "22", "172", "46", "240", "67", "210", "96", "247", "174", "144", "231", "193", "140", "59", "240", "96", "238", "68", "134", "197", "172", "46", "247", "67", "210", "22", "231", "144", "174", "193", "240", "59", "140", "197", "172", "68", "238", "96", "134", "46", "247", "67", "210", "22", "231", "144", "174", "193"); + private final CardRun uncommonA = new CardRun(true, "235", "108", "190", "6", "136", "32", "155", "83", "227", "13", "130", "90", "191", "103", "209", "48", "226", "24", "45", "186", "41", "185", "9", "228", "79", "182", "116", "161", "236", "177", "48", "137", "24", "163", "235", "97", "154", "121", "201", "122", "181", "6", "130", "155", "108", "191", "87", "156", "136", "45", "185", "103", "190", "32", "186", "137", "209", "13", "163", "116", "227", "83", "226", "90", "121", "228", "41", "236", "154", "9", "161", "97", "177", "79", "181", "122", "186", "6", "201", "108", "155", "136", "182", "45", "156", "48", "227", "87", "103", "191", "130", "235", "190", "32", "185", "83", "209", "13", "116", "226", "90", "182", "24", "163", "137", "236", "154", "41", "228", "9", "161", "122", "201", "79", "177", "97", "181", "87", "121", "156"); + private final CardRun uncommonB = new CardRun(true, "167", "128", "53", "187", "107", "16", "206", "145", "242", "102", "173", "26", "125", "166", "66", "55", "230", "12", "175", "131", "214", "93", "40", "162", "239", "106", "223", "33", "7", "194", "81", "167", "107", "12", "55", "215", "16", "117", "74", "187", "54", "202", "82", "102", "206", "242", "33", "173", "11", "145", "53", "230", "241", "175", "73", "40", "93", "223", "128", "66", "106", "166", "125", "162", "7", "187", "131", "194", "54", "202", "239", "117", "26", "214", "81", "16", "74", "167", "107", "242", "55", "215", "82", "12", "241", "173", "145", "230", "53", "102", "206", "11", "131", "166", "73", "40", "125", "194", "239", "66", "54", "26", "162", "128", "223", "33", "106", "81", "214", "74", "175", "7", "93", "215", "82", "202", "117", "11", "241", "73"); + private final CardRun rare = new CardRun(true, "152", "63", "51", "2", "153", "123", "253", "39", "69", "171", "98", "221", "71", "171", "4", "159", "124", "152", "44", "232", "179", "99", "157", "75", "179", "8", "184", "132", "221", "47", "138", "180", "109", "160", "77", "180", "233", "188", "141", "157", "49", "254", "183", "113", "222", "84", "183", "258", "189", "148", "160", "63", "19", "192", "115", "165", "30", "192", "2", "195", "39", "222", "71", "91", "229", "257", "168", "98", "229", "4", "207", "44", "165", "75", "232", "203", "123", "170", "99", "203", "8", "208", "47", "168", "77", "38", "204", "124", "224", "109", "204", "233", "211", "49", "170", "84", "259", "205", "132", "225", "113", "205", "250", "213", "51", "224", "30", "253", "212", "141", "254", "115", "212", "258", "19", "257", "225", "259", "10", "250", "148"); + private final CardRun land = new CardRun(false, "243", "244", "245", "246", "248", "249", "251", "252", "255", "256"); + + private final BoosterStructure AABBC1C1C1C1C1C1 = new BoosterStructure( + commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AAABBC1C1C1C1C1 = new BoosterStructure( + commonA, commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AAAABBC2C2C2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, + commonC2, commonC2, commonC2, commonC2 + ); + private final BoosterStructure AAAABBBC2C2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, commonB, + commonC2, commonC2, commonC2 + ); + private final BoosterStructure AAAABBBBC2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, commonB, commonB, + commonC2, commonC2 + ); + private final BoosterStructure AAB = new BoosterStructure(uncommonA, uncommonA, uncommonB); + private final BoosterStructure ABB = new BoosterStructure(uncommonA, uncommonB, uncommonB); + private final BoosterStructure R1 = new BoosterStructure(rare); + private final BoosterStructure L1 = new BoosterStructure(land); + + // In order for equal numbers of each common to exist, the average booster must contain: + // 3.27 A commons (36 / 11) + // 2.18 B commons (24 / 11) + // 2.73 C1 commons (30 / 11, or 60 / 11 in each C1 booster) + // 1.82 C2 commons (20 / 11, or 40 / 11 in each C2 booster) + // These numbers are the same for all sets with 101 commons in A/B/C1/C2 print runs + // and with 10 common slots per booster + private final RarityConfiguration commonRuns = new RarityConfiguration( + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBBC2C2C2, + AAAABBBC2C2C2, + AAAABBBBC2C2 + ); + private final RarityConfiguration uncommonRuns = new RarityConfiguration(AAB, ABB); + private final RarityConfiguration rareRuns = new RarityConfiguration(R1); + private final RarityConfiguration landRuns = new RarityConfiguration(L1); + + @Override + public List makeBooster() { + List booster = new ArrayList<>(); + booster.addAll(commonRuns.getNext().makeRun()); + booster.addAll(uncommonRuns.getNext().makeRun()); + booster.addAll(rareRuns.getNext().makeRun()); + booster.addAll(landRuns.getNext().makeRun()); + return booster; } } diff --git a/Mage.Sets/src/mage/sets/HistoricAnthology4.java b/Mage.Sets/src/mage/sets/HistoricAnthology4.java index dd7e3e73c4a..0b552ddfa17 100644 --- a/Mage.Sets/src/mage/sets/HistoricAnthology4.java +++ b/Mage.Sets/src/mage/sets/HistoricAnthology4.java @@ -39,7 +39,7 @@ public final class HistoricAnthology4 extends ExpansionSet { cards.add(new SetCardInfo("Inspiring Statuary", 23, Rarity.RARE, mage.cards.i.InspiringStatuary.class)); cards.add(new SetCardInfo("Lys Alana Huntmaster", 15, Rarity.COMMON, mage.cards.l.LysAlanaHuntmaster.class)); cards.add(new SetCardInfo("Marit Lage's Slumber", 6, Rarity.RARE, mage.cards.m.MaritLagesSlumber.class)); - cards.add(new SetCardInfo("Sawtusk Demolisher", 16, Rarity.RARE, mage.cards.s.SawtuskDemolisher.class)); + // cards.add(new SetCardInfo("Sawtusk Demolisher", 16, Rarity.RARE, mage.cards.s.SawtuskDemolisher.class)); TODO: reenable when mutate is implemented cards.add(new SetCardInfo("Spider Spawning", 17, Rarity.UNCOMMON, mage.cards.s.SpiderSpawning.class)); cards.add(new SetCardInfo("Sword of Body and Mind", 24, Rarity.MYTHIC, mage.cards.s.SwordOfBodyAndMind.class)); cards.add(new SetCardInfo("Think Twice", 7, Rarity.COMMON, mage.cards.t.ThinkTwice.class)); diff --git a/Mage.Sets/src/mage/sets/HourOfDevastation.java b/Mage.Sets/src/mage/sets/HourOfDevastation.java index 2e6fdebbbfb..470cef7ab56 100644 --- a/Mage.Sets/src/mage/sets/HourOfDevastation.java +++ b/Mage.Sets/src/mage/sets/HourOfDevastation.java @@ -1,10 +1,13 @@ - package mage.sets; import mage.cards.ExpansionSet; import mage.cards.repository.CardCriteria; import mage.cards.repository.CardInfo; import mage.cards.repository.CardRepository; +import mage.collation.BoosterCollator; +import mage.collation.BoosterStructure; +import mage.collation.CardRun; +import mage.collation.RarityConfiguration; import mage.constants.Rarity; import mage.constants.SetType; @@ -22,8 +25,6 @@ public final class HourOfDevastation extends ExpansionSet { return instance; } - private final List savedSpecialLand = new ArrayList<>(); - private HourOfDevastation() { super("Hour of Devastation", "HOU", ExpansionSet.buildDate(2017, 7, 14), SetType.EXPANSION); this.blockName = "Amonkhet"; @@ -35,7 +36,7 @@ public final class HourOfDevastation extends ExpansionSet { this.numBoosterUncommon = 3; this.numBoosterRare = 1; this.ratioBoosterMythic = 8; - this.ratioBoosterSpecialLand = 144; + this.ratioBoosterSpecialCommon = 129; this.maxCardNumberInBooster = 199; cards.add(new SetCardInfo("Abandoned Sarcophagus", 158, Rarity.RARE, mage.cards.a.AbandonedSarcophagus.class)); @@ -250,15 +251,127 @@ public final class HourOfDevastation extends ExpansionSet { } @Override - public List getSpecialLand() { - if (savedSpecialLand.isEmpty()) { - CardCriteria criteria = new CardCriteria(); - criteria.setCodes("MP2"); - criteria.minCardNumber(31); - criteria.maxCardNumber(54); - savedSpecialLand.addAll(CardRepository.instance.findCards(criteria)); + protected List findCardsByRarity(Rarity rarity) { + List cardInfos = super.findCardsByRarity(rarity); + if (rarity == Rarity.SPECIAL) { + cardInfos.addAll(CardRepository.instance.findCards(new CardCriteria().setCodes("MP2").minCardNumber(31))); } + return cardInfos; + } - return new ArrayList<>(savedSpecialLand); + @Override + protected void generateBoosterMap() { + super.generateBoosterMap(); + CardRepository + .instance + .findCards(new CardCriteria().setCodes("MP2").minCardNumber(31)) + .stream() + .forEach(cardInfo -> inBoosterMap.put("MP2_" + cardInfo.getCardNumber(), cardInfo)); + } + + @Override + public BoosterCollator createCollator() { + return new HourOfDevastationCollator(); + } + +} + +// Booster collation info from https://www.lethe.xyz/mtg/collation/hou.html +// Using USA collation for all rarities +// Foil rare sheet used for regular rares as regular rare sheet is not known +class HourOfDevastationCollator implements BoosterCollator { + private final CardRun commonA = new CardRun(true, "53", "23", "105", "32", "20", "92", "30", "9", "100", "51", "1", "106", "42", "5", "84", "29", "12", "93", "53", "16", "101", "46", "7", "92", "29", "9", "5", "32", "23", "105", "51", "20", "101", "30", "1", "106", "46", "7", "84", "53", "12", "100", "42", "16", "93", "51", "5", "92", "23", "9", "105", "32", "20", "101", "29", "1", "100", "46", "12", "106", "42", "7", "84", "30", "16", "93"); + private final CardRun commonB = new CardRun(true, "69", "116", "67", "111", "64", "110", "72", "112", "79", "134", "66", "128", "61", "124", "70", "112", "82", "118", "69", "116", "79", "134", "67", "110", "64", "112", "70", "111", "72", "133", "67", "124", "66", "128", "61", "116", "79", "118", "82", "111", "69", "133", "64", "128", "72", "110", "61", "134", "70", "124", "66", "118", "82", "133"); + private final CardRun commonC1 = new CardRun(true, "164", "108", "81", "33", "167", "184", "117", "88", "17", "47", "171", "76", "89", "11", "33", "168", "184", "17", "108", "81", "47", "173", "117", "88", "164", "54", "171", "76", "81", "89", "168", "33", "184", "11", "108", "54", "117", "164", "173", "17", "88", "167", "47", "184", "168", "89", "76", "11", "54", "171", "164", "108", "81", "33", "173", "17", "117", "88", "47", "168", "171", "76", "89", "11", "54", "173"); + private final CardRun commonC2 = new CardRun(true, "121", "95", "44", "170", "14", "94", "115", "172", "162", "48", "75", "24", "174", "121", "167", "95", "172", "44", "162", "24", "170", "115", "14", "48", "94", "172", "121", "75", "95", "170", "44", "162", "24", "115", "174", "94", "14", "167", "48", "172", "75", "121", "95", "44", "170", "14", "115", "94", "75", "174", "48", "162", "24", "174"); + private final CardRun uncommonA = new CardRun(true, "13", "45", "143", "119", "177", "74", "86", "27", "38", "142", "127", "159", "71", "91", "4", "49", "152", "123", "166", "62", "103", "25", "37", "138", "135", "169", "74", "86", "13", "38", "143", "119", "180", "56", "102", "25", "45", "152", "135", "159", "71", "103", "27", "49", "148", "127", "180", "62", "91", "4", "37", "142", "123", "177", "138", "166", "74", "86", "13", "38", "143", "119", "169", "56", "102", "25", "49", "152", "127", "159", "71", "91", "13", "45", "148", "135", "180", "62", "103", "4", "37", "142", "123", "177", "56", "102", "27", "38", "143", "119", "166", "74", "103", "25", "49", "152", "135", "159", "138", "169", "71", "86", "4", "45", "148", "127", "180", "62", "142", "177", "37", "138", "123", "166", "56", "91", "27", "169", "148", "102"); + private final CardRun uncommonB = new CardRun(true, "21", "55", "151", "113", "175", "141", "183", "78", "99", "26", "34", "150", "114", "179", "80", "147", "181", "41", "149", "83", "183", "59", "136", "8", "175", "151", "107", "28", "55", "147", "114", "160", "78", "85", "26", "43", "141", "125", "179", "68", "83", "21", "41", "150", "113", "181", "80", "99", "8", "34", "149", "136", "175", "59", "107", "28", "55", "151", "113", "183", "68", "83", "21", "41", "150", "114", "160", "78", "85", "26", "43", "141", "125", "175", "80", "107", "28", "55", "147", "136", "179", "149", "181", "59", "99", "8", "34", "151", "114", "183", "78", "83", "21", "43", "150", "125", "160", "68", "85", "26", "34", "141", "113", "179", "80", "99", "28", "41", "147", "136", "181", "59", "85", "8", "43", "149", "125", "160", "68", "107"); + private final CardRun rare = new CardRun(true, "87", "178", "153", "6", "120", "31", "156", "144", "57", "182", "161", "90", "2", "154", "19", "126", "35", "157", "165", "58", "120", "163", "96", "3", "155", "52", "129", "36", "158", "176", "60", "126", "31", "97", "10", "57", "73", "87", "39", "2", "145", "63", "129", "35", "98", "15", "58", "104", "90", "40", "3", "153", "65", "130", "36", "109", "18", "60", "122", "96", "50", "10", "154", "77", "131", "39", "130", "22", "63", "137", "97", "156", "15", "155", "165", "132", "40", "131", "161", "65", "139", "98", "157", "18", "146", "176", "178", "50", "132", "163", "77", "140", "109", "158", "22", "182"); + private final CardRun masterpiece = new CardRun(false, "MP2_31", "MP2_32", "MP2_33", "MP2_34", "MP2_35", "MP2_36", "MP2_37", "MP2_38", "MP2_39", "MP2_40", "MP2_41", "MP2_42", "MP2_43", "MP2_44", "MP2_45", "MP2_46", "MP2_47", "MP2_48", "MP2_49", "MP2_50", "MP2_51", "MP2_52", "MP2_53", "MP2_54"); + private final CardRun land = new CardRun(false, "185", "186", "187", "188", "189", "190", "191", "192", "193", "194", "195", "196", "197", "198", "199"); + + private final BoosterStructure AAABBC1C1C1C1C1 = new BoosterStructure( + commonA, commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AAABBBC1C1C1C1 = new BoosterStructure( + commonA, commonA, commonA, + commonB, commonB, commonB, + commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AAABBC1C1C1C1M = new BoosterStructure( + commonA, commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1, + masterpiece + ); + private final BoosterStructure AAABBBC2C2C2C2 = new BoosterStructure( + commonA, commonA, commonA, + commonB, commonB, commonB, + commonC2, commonC2, commonC2, commonC2 + ); + private final BoosterStructure AAAABBC2C2C2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, + commonC2, commonC2, commonC2, commonC2 + ); + private final BoosterStructure AAB = new BoosterStructure(uncommonA, uncommonA, uncommonB); + private final BoosterStructure ABB = new BoosterStructure(uncommonA, uncommonB, uncommonB); + private final BoosterStructure R1 = new BoosterStructure(rare); + private final BoosterStructure L1 = new BoosterStructure(land); + + // In order for equal numbers of each common to exist, the average booster must contain: + // 3.14 A commons (44 / 14) (rounded to 405/129) + // 2.57 B commons (36 / 14) (rounded to 332/129) + // 2.36 C1 commons (33 / 14) (rounded to 304/129) + // 1.93 C2 commons (27 / 14) (rounded to 248/129) + // These numbers are the same for all sets with 70 commons in A/B/C1/C2 print runs + // and with 10 common slots per booster + private final RarityConfiguration commonRuns = new RarityConfiguration( + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + + AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, + AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, + AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, + AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, + AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, + AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, + AAABBC1C1C1C1M, + + AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, + AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, + AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, + AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, + AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, + AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, + AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, + AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, + AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, + + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2 + ); + private final RarityConfiguration uncommonRuns = new RarityConfiguration(AAB, ABB); + private final RarityConfiguration rareRuns = new RarityConfiguration(R1); + private final RarityConfiguration landRuns = new RarityConfiguration(L1); + + @Override + public List makeBooster() { + List booster = new ArrayList<>(); + booster.addAll(commonRuns.getNext().makeRun()); + booster.addAll(uncommonRuns.getNext().makeRun()); + booster.addAll(rareRuns.getNext().makeRun()); + booster.addAll(landRuns.getNext().makeRun()); + return booster; } } diff --git a/Mage.Sets/src/mage/sets/IceAge.java b/Mage.Sets/src/mage/sets/IceAge.java index de09b4aefda..794f8bdc78e 100644 --- a/Mage.Sets/src/mage/sets/IceAge.java +++ b/Mage.Sets/src/mage/sets/IceAge.java @@ -196,6 +196,7 @@ public final class IceAge extends ExpansionSet { cards.add(new SetCardInfo("Karplusan Yeti", 197, Rarity.RARE, mage.cards.k.KarplusanYeti.class)); cards.add(new SetCardInfo("Kelsinko Ranger", 33, Rarity.COMMON, mage.cards.k.KelsinkoRanger.class)); cards.add(new SetCardInfo("Kjeldoran Dead", 137, Rarity.COMMON, mage.cards.k.KjeldoranDead.class)); + cards.add(new SetCardInfo("Kjeldoran Elite Guard", 34, Rarity.UNCOMMON, mage.cards.k.KjeldoranEliteGuard.class)); cards.add(new SetCardInfo("Kjeldoran Frostbeast", 296, Rarity.UNCOMMON, mage.cards.k.KjeldoranFrostbeast.class)); cards.add(new SetCardInfo("Kjeldoran Knight", 36, Rarity.RARE, mage.cards.k.KjeldoranKnight.class)); cards.add(new SetCardInfo("Kjeldoran Phalanx", 37, Rarity.RARE, mage.cards.k.KjeldoranPhalanx.class)); diff --git a/Mage.Sets/src/mage/sets/IkoriaLairOfBehemoths.java b/Mage.Sets/src/mage/sets/IkoriaLairOfBehemoths.java index 277192e12b1..675e014a507 100644 --- a/Mage.Sets/src/mage/sets/IkoriaLairOfBehemoths.java +++ b/Mage.Sets/src/mage/sets/IkoriaLairOfBehemoths.java @@ -1,14 +1,10 @@ package mage.sets; import mage.cards.ExpansionSet; -import mage.cards.repository.CardCriteria; import mage.cards.repository.CardInfo; -import mage.cards.repository.CardRepository; -import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.SetType; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -56,8 +52,6 @@ public final class IkoriaLairOfBehemoths extends ExpansionSet { return instance; } - private final List savedSpecialLand = new ArrayList<>(); - private IkoriaLairOfBehemoths() { super("Ikoria: Lair of Behemoths", "IKO", ExpansionSet.buildDate(2020, 4, 24), SetType.EXPANSION); this.blockName = "Ikoria: Lair of Behemoths"; @@ -463,41 +457,12 @@ public final class IkoriaLairOfBehemoths extends ExpansionSet { } @Override - public List getCardsByRarity(Rarity rarity) { - if (rarity != Rarity.COMMON) { - return super.getCardsByRarity(rarity); + protected List findSpecialCardsByRarity(Rarity rarity) { + List cardInfos = super.findSpecialCardsByRarity(rarity); + if (rarity == Rarity.LAND) { + // Evolving Wilds is a normal common + cardInfos.removeIf(cardInfo -> "Evolving Wilds".equals(cardInfo.getName())); } - List savedCardsInfos = savedCards.get(rarity); - if (savedCardsInfos != null) { - return new ArrayList(savedCardsInfos); - } - CardCriteria criteria = new CardCriteria(); - criteria.setCodes(this.code).notTypes(CardType.LAND); - criteria.rarities(rarity).doubleFaced(false); - savedCardsInfos = CardRepository.instance.findCards(criteria); - if (maxCardNumberInBooster != Integer.MAX_VALUE) { - savedCardsInfos.removeIf(next -> next.getCardNumberAsInt() > maxCardNumberInBooster); - } - criteria = new CardCriteria(); - criteria.setCodes(this.code).nameExact("Evolving Wilds"); - savedCardsInfos.addAll(CardRepository.instance.findCards(criteria)); - savedCards.put(rarity, savedCardsInfos); - // Return a copy of the saved cards information, as not to modify the original. - return new ArrayList(savedCardsInfos); - } - - @Override - // the common taplands replacing the basic land - public List getSpecialLand() { - if (savedSpecialLand.isEmpty()) { - CardCriteria criteria = new CardCriteria(); - criteria.setCodes(this.code); - criteria.rarities(Rarity.COMMON); - criteria.types(CardType.LAND); - savedSpecialLand.addAll(CardRepository.instance.findCards(criteria)); - savedSpecialLand.removeIf(cardInfo -> "Evolving Wilds".equals(cardInfo.getName())); - } - - return new ArrayList(savedSpecialLand); + return cardInfos; } } diff --git a/Mage.Sets/src/mage/sets/InnistradCrimsonVow.java b/Mage.Sets/src/mage/sets/InnistradCrimsonVow.java new file mode 100644 index 00000000000..d2e0b5fbf70 --- /dev/null +++ b/Mage.Sets/src/mage/sets/InnistradCrimsonVow.java @@ -0,0 +1,602 @@ +package mage.sets; + +import mage.cards.Card; +import mage.cards.ExpansionSet; +import mage.collation.BoosterCollator; +import mage.collation.BoosterStructure; +import mage.collation.CardRun; +import mage.collation.RarityConfiguration; +import mage.constants.Rarity; +import mage.constants.SetType; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author TheElk801 + */ +public final class InnistradCrimsonVow extends ExpansionSet { + + private static final InnistradCrimsonVow instance = new InnistradCrimsonVow(); + + public static InnistradCrimsonVow getInstance() { + return instance; + } + + private InnistradCrimsonVow() { + super("Innistrad: Crimson Vow", "VOW", ExpansionSet.buildDate(2021, 11, 19), SetType.EXPANSION); + this.blockName = "Innistrad: Midnight Hunt"; + this.hasBoosters = true; + this.hasBasicLands = true; + this.numBoosterLands = 1; + this.numBoosterCommon = 9; + this.numBoosterUncommon = 3; + this.numBoosterRare = 1; + this.ratioBoosterMythic = 8; + this.ratioBoosterSpecialRare = 5.5; + this.ratioBoosterSpecialMythic = 5.4; // 5 mythic DFCs, 11 rare DFCs + this.numBoosterDoubleFaced = 1; + this.maxCardNumberInBooster = 277; + + cards.add(new SetCardInfo("Abrade", 139, Rarity.COMMON, mage.cards.a.Abrade.class)); + cards.add(new SetCardInfo("Adamant Will", 1, Rarity.COMMON, mage.cards.a.AdamantWill.class)); + cards.add(new SetCardInfo("Aim for the Head", 92, Rarity.COMMON, mage.cards.a.AimForTheHead.class)); + cards.add(new SetCardInfo("Alchemist's Gambit", 140, Rarity.RARE, mage.cards.a.AlchemistsGambit.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Alchemist's Gambit", 374, Rarity.RARE, mage.cards.a.AlchemistsGambit.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Alchemist's Retrieval", 47, Rarity.COMMON, mage.cards.a.AlchemistsRetrieval.class)); + cards.add(new SetCardInfo("Alluring Suitor", 141, Rarity.UNCOMMON, mage.cards.a.AlluringSuitor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Alluring Suitor", 300, Rarity.UNCOMMON, mage.cards.a.AlluringSuitor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ancestor's Embrace", 22, Rarity.COMMON, mage.cards.a.AncestorsEmbrace.class)); + cards.add(new SetCardInfo("Ancestral Anger", 142, Rarity.COMMON, mage.cards.a.AncestralAnger.class)); + cards.add(new SetCardInfo("Ancient Lumberknot", 230, Rarity.UNCOMMON, mage.cards.a.AncientLumberknot.class)); + cards.add(new SetCardInfo("Angelic Quartermaster", 2, Rarity.UNCOMMON, mage.cards.a.AngelicQuartermaster.class)); + cards.add(new SetCardInfo("Anje, Maid of Dishonor", 231, Rarity.RARE, mage.cards.a.AnjeMaidOfDishonor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Anje, Maid of Dishonor", 309, Rarity.RARE, mage.cards.a.AnjeMaidOfDishonor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Apprentice Sharpshooter", 185, Rarity.COMMON, mage.cards.a.ApprenticeSharpshooter.class)); + cards.add(new SetCardInfo("Archghoul of Thraben", 93, Rarity.UNCOMMON, mage.cards.a.ArchghoulOfThraben.class)); + cards.add(new SetCardInfo("Arm the Cathars", 3, Rarity.UNCOMMON, mage.cards.a.ArmTheCathars.class)); + cards.add(new SetCardInfo("Ascendant Packleader", 186, Rarity.RARE, mage.cards.a.AscendantPackleader.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ascendant Packleader", 383, Rarity.RARE, mage.cards.a.AscendantPackleader.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Avabruck Caretaker", 187, Rarity.MYTHIC, mage.cards.a.AvabruckCaretaker.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Avabruck Caretaker", 384, Rarity.MYTHIC, mage.cards.a.AvabruckCaretaker.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ballista Watcher", 143, Rarity.UNCOMMON, mage.cards.b.BallistaWatcher.class)); + cards.add(new SetCardInfo("Ballista Wielder", 143, Rarity.UNCOMMON, mage.cards.b.BallistaWielder.class)); + cards.add(new SetCardInfo("Belligerent Guest", 144, Rarity.COMMON, mage.cards.b.BelligerentGuest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Belligerent Guest", 301, Rarity.COMMON, mage.cards.b.BelligerentGuest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Binding Geist", 48, Rarity.COMMON, mage.cards.b.BindingGeist.class)); + cards.add(new SetCardInfo("Biolume Egg", 49, Rarity.UNCOMMON, mage.cards.b.BiolumeEgg.class)); + cards.add(new SetCardInfo("Biolume Serpent", 49, Rarity.UNCOMMON, mage.cards.b.BiolumeSerpent.class)); + cards.add(new SetCardInfo("Bleed Dry", 94, Rarity.COMMON, mage.cards.b.BleedDry.class)); + cards.add(new SetCardInfo("Blood Fountain", 95, Rarity.COMMON, mage.cards.b.BloodFountain.class)); + cards.add(new SetCardInfo("Blood Hypnotist", 145, Rarity.UNCOMMON, mage.cards.b.BloodHypnotist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Blood Hypnotist", 302, Rarity.UNCOMMON, mage.cards.b.BloodHypnotist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Blood Petal Celebrant", 146, Rarity.COMMON, mage.cards.b.BloodPetalCelebrant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Blood Petal Celebrant", 303, Rarity.COMMON, mage.cards.b.BloodPetalCelebrant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Blood Servitor", 252, Rarity.COMMON, mage.cards.b.BloodServitor.class)); + cards.add(new SetCardInfo("Bloodbat Summoner", 137, Rarity.RARE, mage.cards.b.BloodbatSummoner.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bloodbat Summoner", 298, Rarity.RARE, mage.cards.b.BloodbatSummoner.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bloodbat Summoner", 338, Rarity.RARE, mage.cards.b.BloodbatSummoner.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bloodcrazed Socialite", 288, Rarity.COMMON, mage.cards.b.BloodcrazedSocialite.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bloodcrazed Socialite", 96, Rarity.COMMON, mage.cards.b.BloodcrazedSocialite.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bloodsoaked Reveler", 128, Rarity.UNCOMMON, mage.cards.b.BloodsoakedReveler.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bloodsoaked Reveler", 295, Rarity.UNCOMMON, mage.cards.b.BloodsoakedReveler.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bloodsworn Knight", 289, Rarity.UNCOMMON, mage.cards.b.BloodswornKnight.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bloodsworn Knight", 97, Rarity.UNCOMMON, mage.cards.b.BloodswornKnight.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bloodsworn Squire", 289, Rarity.UNCOMMON, mage.cards.b.BloodswornSquire.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bloodsworn Squire", 97, Rarity.UNCOMMON, mage.cards.b.BloodswornSquire.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bloodtithe Harvester", 232, Rarity.UNCOMMON, mage.cards.b.BloodtitheHarvester.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bloodtithe Harvester", 310, Rarity.UNCOMMON, mage.cards.b.BloodtitheHarvester.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bloodvial Purveyor", 290, Rarity.RARE, mage.cards.b.BloodvialPurveyor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bloodvial Purveyor", 98, Rarity.RARE, mage.cards.b.BloodvialPurveyor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bloody Betrayal", 147, Rarity.COMMON, mage.cards.b.BloodyBetrayal.class)); + cards.add(new SetCardInfo("Blossom-Clad Werewolf", 226, Rarity.COMMON, mage.cards.b.BlossomCladWerewolf.class)); + cards.add(new SetCardInfo("Boarded Window", 253, Rarity.UNCOMMON, mage.cards.b.BoardedWindow.class)); + cards.add(new SetCardInfo("Bramble Armor", 188, Rarity.COMMON, mage.cards.b.BrambleArmor.class)); + cards.add(new SetCardInfo("Bramble Wurm", 189, Rarity.UNCOMMON, mage.cards.b.BrambleWurm.class)); + cards.add(new SetCardInfo("Bride's Gown", 4, Rarity.UNCOMMON, mage.cards.b.BridesGown.class)); + cards.add(new SetCardInfo("Brine Comber", 233, Rarity.UNCOMMON, mage.cards.b.BrineComber.class)); + cards.add(new SetCardInfo("Brinebound Gift", 233, Rarity.UNCOMMON, mage.cards.b.BrineboundGift.class)); + cards.add(new SetCardInfo("By Invitation Only", 346, Rarity.RARE, mage.cards.b.ByInvitationOnly.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("By Invitation Only", 5, Rarity.RARE, mage.cards.b.ByInvitationOnly.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Cackling Culprit", 28, Rarity.UNCOMMON, mage.cards.c.CacklingCulprit.class)); + cards.add(new SetCardInfo("Cartographer's Survey", 190, Rarity.UNCOMMON, mage.cards.c.CartographersSurvey.class)); + cards.add(new SetCardInfo("Catapult Captain", 99, Rarity.UNCOMMON, mage.cards.c.CatapultCaptain.class)); + cards.add(new SetCardInfo("Catapult Fodder", 99, Rarity.UNCOMMON, mage.cards.c.CatapultFodder.class)); + cards.add(new SetCardInfo("Catlike Curiosity", 69, Rarity.UNCOMMON, mage.cards.c.CatlikeCuriosity.class)); + cards.add(new SetCardInfo("Cemetery Desecrator", 100, Rarity.MYTHIC, mage.cards.c.CemeteryDesecrator.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Cemetery Desecrator", 366, Rarity.MYTHIC, mage.cards.c.CemeteryDesecrator.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Cemetery Gatekeeper", 148, Rarity.MYTHIC, mage.cards.c.CemeteryGatekeeper.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Cemetery Gatekeeper", 304, Rarity.MYTHIC, mage.cards.c.CemeteryGatekeeper.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Cemetery Illuminator", 356, Rarity.MYTHIC, mage.cards.c.CemeteryIlluminator.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Cemetery Illuminator", 50, Rarity.MYTHIC, mage.cards.c.CemeteryIlluminator.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Cemetery Protector", 347, Rarity.MYTHIC, mage.cards.c.CemeteryProtector.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Cemetery Protector", 6, Rarity.MYTHIC, mage.cards.c.CemeteryProtector.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Cemetery Prowler", 191, Rarity.MYTHIC, mage.cards.c.CemeteryProwler.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Cemetery Prowler", 385, Rarity.MYTHIC, mage.cards.c.CemeteryProwler.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ceremonial Knife", 254, Rarity.COMMON, mage.cards.c.CeremonialKnife.class)); + cards.add(new SetCardInfo("Chandra, Dressed to Kill", 149, Rarity.MYTHIC, mage.cards.c.ChandraDressedToKill.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Chandra, Dressed to Kill", 279, Rarity.MYTHIC, mage.cards.c.ChandraDressedToKill.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Change of Fortune", 150, Rarity.RARE, mage.cards.c.ChangeOfFortune.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Change of Fortune", 375, Rarity.RARE, mage.cards.c.ChangeOfFortune.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Child of the Pack", 234, Rarity.UNCOMMON, mage.cards.c.ChildOfThePack.class)); + cards.add(new SetCardInfo("Chill of the Grave", 51, Rarity.COMMON, mage.cards.c.ChillOfTheGrave.class)); + cards.add(new SetCardInfo("Cipherbound Spirit", 79, Rarity.UNCOMMON, mage.cards.c.CipherboundSpirit.class)); + cards.add(new SetCardInfo("Circle of Confinement", 329, Rarity.UNCOMMON, mage.cards.c.CircleOfConfinement.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Circle of Confinement", 7, Rarity.UNCOMMON, mage.cards.c.CircleOfConfinement.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Clever Distraction", 9, Rarity.UNCOMMON, mage.cards.c.CleverDistraction.class)); + cards.add(new SetCardInfo("Cloaked Cadet", 192, Rarity.UNCOMMON, mage.cards.c.CloakedCadet.class)); + cards.add(new SetCardInfo("Cobbled Lancer", 52, Rarity.UNCOMMON, mage.cards.c.CobbledLancer.class)); + cards.add(new SetCardInfo("Concealing Curtains", 101, Rarity.RARE, mage.cards.c.ConcealingCurtains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Concealing Curtains", 367, Rarity.RARE, mage.cards.c.ConcealingCurtains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Consuming Tide", 357, Rarity.RARE, mage.cards.c.ConsumingTide.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Consuming Tide", 53, Rarity.RARE, mage.cards.c.ConsumingTide.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Courier Bat", 102, Rarity.COMMON, mage.cards.c.CourierBat.class)); + cards.add(new SetCardInfo("Cradle of Safety", 54, Rarity.COMMON, mage.cards.c.CradleOfSafety.class)); + cards.add(new SetCardInfo("Crawling Infestation", 193, Rarity.UNCOMMON, mage.cards.c.CrawlingInfestation.class)); + cards.add(new SetCardInfo("Creepy Puppeteer", 151, Rarity.RARE, mage.cards.c.CreepyPuppeteer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Creepy Puppeteer", 376, Rarity.RARE, mage.cards.c.CreepyPuppeteer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Cruel Witness", 55, Rarity.COMMON, mage.cards.c.CruelWitness.class)); + cards.add(new SetCardInfo("Crushing Canopy", 194, Rarity.COMMON, mage.cards.c.CrushingCanopy.class)); + cards.add(new SetCardInfo("Cultivator Colossus", 195, Rarity.MYTHIC, mage.cards.c.CultivatorColossus.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Cultivator Colossus", 386, Rarity.MYTHIC, mage.cards.c.CultivatorColossus.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Curse of Hospitality", 152, Rarity.RARE, mage.cards.c.CurseOfHospitality.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Curse of Hospitality", 377, Rarity.RARE, mage.cards.c.CurseOfHospitality.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dawnhart Disciple", 196, Rarity.COMMON, mage.cards.d.DawnhartDisciple.class)); + cards.add(new SetCardInfo("Dawnhart Geist", 8, Rarity.UNCOMMON, mage.cards.d.DawnhartGeist.class)); + cards.add(new SetCardInfo("Daybreak Combatants", 153, Rarity.COMMON, mage.cards.d.DaybreakCombatants.class)); + cards.add(new SetCardInfo("Deadly Dancer", 141, Rarity.UNCOMMON, mage.cards.d.DeadlyDancer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Deadly Dancer", 300, Rarity.UNCOMMON, mage.cards.d.DeadlyDancer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Deathcap Glade", 261, Rarity.RARE, mage.cards.d.DeathcapGlade.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Deathcap Glade", 281, Rarity.RARE, mage.cards.d.DeathcapGlade.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Demonic Bargain", 103, Rarity.RARE, mage.cards.d.DemonicBargain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Demonic Bargain", 368, Rarity.RARE, mage.cards.d.DemonicBargain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Depraved Harvester", 104, Rarity.COMMON, mage.cards.d.DepravedHarvester.class)); + cards.add(new SetCardInfo("Desperate Farmer", 104, Rarity.COMMON, mage.cards.d.DesperateFarmer.class)); + cards.add(new SetCardInfo("Dig Up", 197, Rarity.RARE, mage.cards.d.DigUp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dig Up", 387, Rarity.RARE, mage.cards.d.DigUp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dire-Strain Anarchist", 181, Rarity.MYTHIC, mage.cards.d.DireStrainAnarchist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dire-Strain Anarchist", 382, Rarity.MYTHIC, mage.cards.d.DireStrainAnarchist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Diregraf Scavenger", 105, Rarity.COMMON, mage.cards.d.DiregrafScavenger.class)); + cards.add(new SetCardInfo("Distracting Geist", 9, Rarity.UNCOMMON, mage.cards.d.DistractingGeist.class)); + cards.add(new SetCardInfo("Diver Skaab", 56, Rarity.UNCOMMON, mage.cards.d.DiverSkaab.class)); + cards.add(new SetCardInfo("Dollhouse of Horrors", 255, Rarity.RARE, mage.cards.d.DollhouseOfHorrors.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dollhouse of Horrors", 395, Rarity.RARE, mage.cards.d.DollhouseOfHorrors.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dominating Vampire", 154, Rarity.RARE, mage.cards.d.DominatingVampire.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dominating Vampire", 305, Rarity.RARE, mage.cards.d.DominatingVampire.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dominating Vampire", 407, Rarity.RARE, mage.cards.d.DominatingVampire.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Doomed Dissenter", 106, Rarity.COMMON, mage.cards.d.DoomedDissenter.class)); + cards.add(new SetCardInfo("Dormant Grove", 198, Rarity.UNCOMMON, mage.cards.d.DormantGrove.class)); + cards.add(new SetCardInfo("Dorothea's Retribution", 235, Rarity.RARE, mage.cards.d.DorotheasRetribution.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dorothea's Retribution", 322, Rarity.RARE, mage.cards.d.DorotheasRetribution.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dorothea, Vengeful Victim", 235, Rarity.RARE, mage.cards.d.DorotheaVengefulVictim.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dorothea, Vengeful Victim", 322, Rarity.RARE, mage.cards.d.DorotheaVengefulVictim.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dread Fugue", 107, Rarity.UNCOMMON, mage.cards.d.DreadFugue.class)); + cards.add(new SetCardInfo("Dreadfeast Demon", 108, Rarity.RARE, mage.cards.d.DreadfeastDemon.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dreadfeast Demon", 369, Rarity.RARE, mage.cards.d.DreadfeastDemon.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dreadlight Monstrosity", 57, Rarity.COMMON, mage.cards.d.DreadlightMonstrosity.class)); + cards.add(new SetCardInfo("Dreamroot Cascade", 262, Rarity.RARE, mage.cards.d.DreamrootCascade.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dreamroot Cascade", 282, Rarity.RARE, mage.cards.d.DreamrootCascade.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dreamshackle Geist", 358, Rarity.RARE, mage.cards.d.DreamshackleGeist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dreamshackle Geist", 58, Rarity.RARE, mage.cards.d.DreamshackleGeist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Drogskol Armaments", 10, Rarity.COMMON, mage.cards.d.DrogskolArmaments.class)); + cards.add(new SetCardInfo("Drogskol Infantry", 10, Rarity.COMMON, mage.cards.d.DrogskolInfantry.class)); + cards.add(new SetCardInfo("Dying to Serve", 109, Rarity.RARE, mage.cards.d.DyingToServe.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dying to Serve", 370, Rarity.RARE, mage.cards.d.DyingToServe.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Edgar Markov's Coffin", 236, Rarity.RARE, mage.cards.e.EdgarMarkovsCoffin.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Edgar Markov's Coffin", 311, Rarity.RARE, mage.cards.e.EdgarMarkovsCoffin.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Edgar Markov's Coffin", 341, Rarity.RARE, mage.cards.e.EdgarMarkovsCoffin.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Edgar's Awakening", 110, Rarity.UNCOMMON, mage.cards.e.EdgarsAwakening.class)); + cards.add(new SetCardInfo("Edgar, Charmed Groom", 236, Rarity.RARE, mage.cards.e.EdgarCharmedGroom.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Edgar, Charmed Groom", 311, Rarity.RARE, mage.cards.e.EdgarCharmedGroom.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Edgar, Charmed Groom", 341, Rarity.RARE, mage.cards.e.EdgarCharmedGroom.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("End the Festivities", 155, Rarity.COMMON, mage.cards.e.EndTheFestivities.class)); + cards.add(new SetCardInfo("Eruth, Tormented Prophet", 237, Rarity.RARE, mage.cards.e.EruthTormentedProphet.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Eruth, Tormented Prophet", 323, Rarity.RARE, mage.cards.e.EruthTormentedProphet.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Eruth, Tormented Prophet", 342, Rarity.RARE, mage.cards.e.EruthTormentedProphet.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Estwald Shieldbasher", 11, Rarity.COMMON, mage.cards.e.EstwaldShieldbasher.class)); + cards.add(new SetCardInfo("Evolving Wilds", 263, Rarity.COMMON, mage.cards.e.EvolvingWilds.class)); + cards.add(new SetCardInfo("Faithbound Judge", 12, Rarity.MYTHIC, mage.cards.f.FaithboundJudge.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Faithbound Judge", 348, Rarity.MYTHIC, mage.cards.f.FaithboundJudge.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Falkenrath Celebrants", 156, Rarity.COMMON, mage.cards.f.FalkenrathCelebrants.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Falkenrath Celebrants", 306, Rarity.COMMON, mage.cards.f.FalkenrathCelebrants.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Falkenrath Forebear", 111, Rarity.RARE, mage.cards.f.FalkenrathForebear.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Falkenrath Forebear", 291, Rarity.RARE, mage.cards.f.FalkenrathForebear.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Falkenrath Forebear", 334, Rarity.RARE, mage.cards.f.FalkenrathForebear.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fear of Death", 59, Rarity.COMMON, mage.cards.f.FearOfDeath.class)); + cards.add(new SetCardInfo("Fearful Villager", 157, Rarity.COMMON, mage.cards.f.FearfulVillager.class)); + cards.add(new SetCardInfo("Fearsome Werewolf", 157, Rarity.COMMON, mage.cards.f.FearsomeWerewolf.class)); + cards.add(new SetCardInfo("Fell Stinger", 112, Rarity.UNCOMMON, mage.cards.f.FellStinger.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fell Stinger", 406, Rarity.UNCOMMON, mage.cards.f.FellStinger.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fierce Retribution", 13, Rarity.COMMON, mage.cards.f.FierceRetribution.class)); + cards.add(new SetCardInfo("Flame-Blessed Bolt", 158, Rarity.COMMON, mage.cards.f.FlameBlessedBolt.class)); + cards.add(new SetCardInfo("Fleeting Spirit", 14, Rarity.UNCOMMON, mage.cards.f.FleetingSpirit.class)); + cards.add(new SetCardInfo("Flourishing Hunter", 199, Rarity.COMMON, mage.cards.f.FlourishingHunter.class)); + cards.add(new SetCardInfo("Foreboding Statue", 256, Rarity.UNCOMMON, mage.cards.f.ForebodingStatue.class)); + cards.add(new SetCardInfo("Forest", 276, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Forest", 277, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Forest", 402, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 412, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Forsaken Thresher", 256, Rarity.UNCOMMON, mage.cards.f.ForsakenThresher.class)); + cards.add(new SetCardInfo("Frenzied Devils", 159, Rarity.UNCOMMON, mage.cards.f.FrenziedDevils.class)); + cards.add(new SetCardInfo("Geistlight Snare", 405, Rarity.UNCOMMON, mage.cards.g.GeistlightSnare.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Geistlight Snare", 60, Rarity.UNCOMMON, mage.cards.g.GeistlightSnare.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Geralf, Visionary Stitcher", 319, Rarity.RARE, mage.cards.g.GeralfVisionaryStitcher.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Geralf, Visionary Stitcher", 61, Rarity.RARE, mage.cards.g.GeralfVisionaryStitcher.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ghastly Mimicry", 361, Rarity.RARE, mage.cards.g.GhastlyMimicry.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ghastly Mimicry", 68, Rarity.RARE, mage.cards.g.GhastlyMimicry.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gift of Fangs", 113, Rarity.COMMON, mage.cards.g.GiftOfFangs.class)); + cards.add(new SetCardInfo("Glorious Sunrise", 200, Rarity.RARE, mage.cards.g.GloriousSunrise.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Glorious Sunrise", 388, Rarity.RARE, mage.cards.g.GloriousSunrise.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gluttonous Guest", 114, Rarity.COMMON, mage.cards.g.GluttonousGuest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gluttonous Guest", 292, Rarity.COMMON, mage.cards.g.GluttonousGuest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gnarled Grovestrider", 198, Rarity.UNCOMMON, mage.cards.g.GnarledGrovestrider.class)); + cards.add(new SetCardInfo("Graf Reaver", 115, Rarity.RARE, mage.cards.g.GrafReaver.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Graf Reaver", 371, Rarity.RARE, mage.cards.g.GrafReaver.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Grisly Ritual", 116, Rarity.COMMON, mage.cards.g.GrislyRitual.class)); + cards.add(new SetCardInfo("Grolnok, the Omnivore", 238, Rarity.RARE, mage.cards.g.GrolnokTheOmnivore.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Grolnok, the Omnivore", 324, Rarity.RARE, mage.cards.g.GrolnokTheOmnivore.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Groom's Finery", 117, Rarity.UNCOMMON, mage.cards.g.GroomsFinery.class)); + cards.add(new SetCardInfo("Gryff Rider", 15, Rarity.COMMON, mage.cards.g.GryffRider.class)); + cards.add(new SetCardInfo("Gryffwing Cavalry", 16, Rarity.UNCOMMON, mage.cards.g.GryffwingCavalry.class)); + cards.add(new SetCardInfo("Gutter Shortcut", 62, Rarity.UNCOMMON, mage.cards.g.GutterShortcut.class)); + cards.add(new SetCardInfo("Gutter Skulker", 62, Rarity.UNCOMMON, mage.cards.g.GutterSkulker.class)); + cards.add(new SetCardInfo("Halana and Alena, Partners", 239, Rarity.RARE, mage.cards.h.HalanaAndAlenaPartners.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Halana and Alena, Partners", 325, Rarity.RARE, mage.cards.h.HalanaAndAlenaPartners.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hallowed Haunting", 17, Rarity.MYTHIC, mage.cards.h.HallowedHaunting.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hallowed Haunting", 349, Rarity.MYTHIC, mage.cards.h.HallowedHaunting.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hamlet Vanguard", 201, Rarity.RARE, mage.cards.h.HamletVanguard.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hamlet Vanguard", 389, Rarity.RARE, mage.cards.h.HamletVanguard.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hauken's Insight", 320, Rarity.MYTHIC, mage.cards.h.HaukensInsight.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hauken's Insight", 332, Rarity.MYTHIC, mage.cards.h.HaukensInsight.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hauken's Insight", 65, Rarity.MYTHIC, mage.cards.h.HaukensInsight.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Headless Rider", 118, Rarity.RARE, mage.cards.h.HeadlessRider.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Headless Rider", 372, Rarity.RARE, mage.cards.h.HeadlessRider.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Henrika Domnathi", 119, Rarity.MYTHIC, mage.cards.h.HenrikaDomnathi.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Henrika Domnathi", 293, Rarity.MYTHIC, mage.cards.h.HenrikaDomnathi.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Henrika Domnathi", 335, Rarity.MYTHIC, mage.cards.h.HenrikaDomnathi.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Henrika, Infernal Seer", 119, Rarity.MYTHIC, mage.cards.h.HenrikaInfernalSeer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Henrika, Infernal Seer", 293, Rarity.MYTHIC, mage.cards.h.HenrikaInfernalSeer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Henrika, Infernal Seer", 335, Rarity.MYTHIC, mage.cards.h.HenrikaInfernalSeer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hero's Downfall", 120, Rarity.UNCOMMON, mage.cards.h.HerosDownfall.class)); + cards.add(new SetCardInfo("Heron of Hope", 18, Rarity.COMMON, mage.cards.h.HeronOfHope.class)); + cards.add(new SetCardInfo("Heron-Blessed Geist", 19, Rarity.COMMON, mage.cards.h.HeronBlessedGeist.class)); + cards.add(new SetCardInfo("Hiveheart Shaman", 202, Rarity.RARE, mage.cards.h.HiveheartShaman.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hiveheart Shaman", 390, Rarity.RARE, mage.cards.h.HiveheartShaman.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hollowhenge Huntmaster", 187, Rarity.MYTHIC, mage.cards.h.HollowhengeHuntmaster.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hollowhenge Huntmaster", 384, Rarity.MYTHIC, mage.cards.h.HollowhengeHuntmaster.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Honeymoon Hearse", 160, Rarity.UNCOMMON, mage.cards.h.HoneymoonHearse.class)); + cards.add(new SetCardInfo("Honored Heirloom", 257, Rarity.COMMON, mage.cards.h.HonoredHeirloom.class)); + cards.add(new SetCardInfo("Hookhand Mariner", 203, Rarity.COMMON, mage.cards.h.HookhandMariner.class)); + cards.add(new SetCardInfo("Hopeful Initiate", 20, Rarity.RARE, mage.cards.h.HopefulInitiate.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hopeful Initiate", 350, Rarity.RARE, mage.cards.h.HopefulInitiate.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Howling Moon", 204, Rarity.RARE, mage.cards.h.HowlingMoon.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Howling Moon", 391, Rarity.RARE, mage.cards.h.HowlingMoon.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Howlpack Avenger", 162, Rarity.RARE, mage.cards.h.HowlpackAvenger.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Howlpack Avenger", 378, Rarity.RARE, mage.cards.h.HowlpackAvenger.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Howlpack Piper", 205, Rarity.RARE, mage.cards.h.HowlpackPiper.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Howlpack Piper", 392, Rarity.RARE, mage.cards.h.HowlpackPiper.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hullbreaker Horror", 359, Rarity.RARE, mage.cards.h.HullbreakerHorror.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hullbreaker Horror", 63, Rarity.RARE, mage.cards.h.HullbreakerHorror.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hungry Ridgewolf", 161, Rarity.COMMON, mage.cards.h.HungryRidgewolf.class)); + cards.add(new SetCardInfo("Ill-Tempered Loner", 162, Rarity.RARE, mage.cards.i.IllTemperedLoner.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ill-Tempered Loner", 378, Rarity.RARE, mage.cards.i.IllTemperedLoner.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Infestation Expert", 206, Rarity.UNCOMMON, mage.cards.i.InfestationExpert.class)); + cards.add(new SetCardInfo("Infested Werewolf", 206, Rarity.UNCOMMON, mage.cards.i.InfestedWerewolf.class)); + cards.add(new SetCardInfo("Innocent Traveler", 121, Rarity.UNCOMMON, mage.cards.i.InnocentTraveler.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Innocent Traveler", 294, Rarity.UNCOMMON, mage.cards.i.InnocentTraveler.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Innocent Traveler", 336, Rarity.UNCOMMON, mage.cards.i.InnocentTraveler.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Inspired Idea", 360, Rarity.RARE, mage.cards.i.InspiredIdea.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Inspired Idea", 64, Rarity.RARE, mage.cards.i.InspiredIdea.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Into the Night", 163, Rarity.UNCOMMON, mage.cards.i.IntoTheNight.class)); + cards.add(new SetCardInfo("Investigator's Journal", 258, Rarity.RARE, mage.cards.i.InvestigatorsJournal.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Investigator's Journal", 345, Rarity.RARE, mage.cards.i.InvestigatorsJournal.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Investigator's Journal", 396, Rarity.RARE, mage.cards.i.InvestigatorsJournal.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 270, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Island", 271, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Island", 399, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 409, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Jacob Hauken, Inspector", 320, Rarity.MYTHIC, mage.cards.j.JacobHaukenInspector.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Jacob Hauken, Inspector", 332, Rarity.MYTHIC, mage.cards.j.JacobHaukenInspector.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Jacob Hauken, Inspector", 65, Rarity.MYTHIC, mage.cards.j.JacobHaukenInspector.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Katilda's Rising Dawn", 21, Rarity.RARE, mage.cards.k.KatildasRisingDawn.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Katilda's Rising Dawn", 317, Rarity.RARE, mage.cards.k.KatildasRisingDawn.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Katilda, Dawnhart Martyr", 21, Rarity.RARE, mage.cards.k.KatildaDawnhartMartyr.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Katilda, Dawnhart Martyr", 317, Rarity.RARE, mage.cards.k.KatildaDawnhartMartyr.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kaya, Geist Hunter", 240, Rarity.MYTHIC, mage.cards.k.KayaGeistHunter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kaya, Geist Hunter", 280, Rarity.MYTHIC, mage.cards.k.KayaGeistHunter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kessig Flamebreather", 164, Rarity.COMMON, mage.cards.k.KessigFlamebreather.class)); + cards.add(new SetCardInfo("Kessig Wolfrider", 165, Rarity.RARE, mage.cards.k.KessigWolfrider.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kessig Wolfrider", 379, Rarity.RARE, mage.cards.k.KessigWolfrider.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kindly Ancestor", 22, Rarity.COMMON, mage.cards.k.KindlyAncestor.class)); + cards.add(new SetCardInfo("Krothuss, Lord of the Deep", 246, Rarity.RARE, mage.cards.k.KrothussLordOfTheDeep.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Krothuss, Lord of the Deep", 316, Rarity.RARE, mage.cards.k.KrothussLordOfTheDeep.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Krothuss, Lord of the Deep", 327, Rarity.RARE, mage.cards.k.KrothussLordOfTheDeep.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lacerate Flesh", 166, Rarity.COMMON, mage.cards.l.LacerateFlesh.class)); + cards.add(new SetCardInfo("Laid to Rest", 207, Rarity.UNCOMMON, mage.cards.l.LaidToRest.class)); + cards.add(new SetCardInfo("Lambholt Raconteur", 167, Rarity.UNCOMMON, mage.cards.l.LambholtRaconteur.class)); + cards.add(new SetCardInfo("Lambholt Ravager", 167, Rarity.UNCOMMON, mage.cards.l.LambholtRavager.class)); + cards.add(new SetCardInfo("Lantern Bearer", 66, Rarity.COMMON, mage.cards.l.LanternBearer.class)); + cards.add(new SetCardInfo("Lantern Flare", 23, Rarity.RARE, mage.cards.l.LanternFlare.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lantern Flare", 351, Rarity.RARE, mage.cards.l.LanternFlare.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lantern of the Lost", 259, Rarity.UNCOMMON, mage.cards.l.LanternOfTheLost.class)); + cards.add(new SetCardInfo("Lanterns' Lift", 66, Rarity.COMMON, mage.cards.l.LanternsLift.class)); + cards.add(new SetCardInfo("Lightning Wolf", 168, Rarity.COMMON, mage.cards.l.LightningWolf.class)); + cards.add(new SetCardInfo("Lunar Rejection", 67, Rarity.UNCOMMON, mage.cards.l.LunarRejection.class)); + cards.add(new SetCardInfo("Magma Pummeler", 169, Rarity.UNCOMMON, mage.cards.m.MagmaPummeler.class)); + cards.add(new SetCardInfo("Malicious Invader", 121, Rarity.UNCOMMON, mage.cards.m.MaliciousInvader.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Malicious Invader", 294, Rarity.UNCOMMON, mage.cards.m.MaliciousInvader.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Malicious Invader", 336, Rarity.UNCOMMON, mage.cards.m.MaliciousInvader.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Manaform Hellkite", 170, Rarity.MYTHIC, mage.cards.m.ManaformHellkite.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Manaform Hellkite", 380, Rarity.MYTHIC, mage.cards.m.ManaformHellkite.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Markov Purifier", 241, Rarity.UNCOMMON, mage.cards.m.MarkovPurifier.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Markov Purifier", 312, Rarity.UNCOMMON, mage.cards.m.MarkovPurifier.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Markov Retribution", 171, Rarity.UNCOMMON, mage.cards.m.MarkovRetribution.class)); + cards.add(new SetCardInfo("Markov Waltzer", 242, Rarity.UNCOMMON, mage.cards.m.MarkovWaltzer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Markov Waltzer", 313, Rarity.UNCOMMON, mage.cards.m.MarkovWaltzer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Massive Might", 208, Rarity.COMMON, mage.cards.m.MassiveMight.class)); + cards.add(new SetCardInfo("Militia Rallier", 24, Rarity.COMMON, mage.cards.m.MilitiaRallier.class)); + cards.add(new SetCardInfo("Mindleech Ghoul", 122, Rarity.COMMON, mage.cards.m.MindleechGhoul.class)); + cards.add(new SetCardInfo("Mirrorhall Mimic", 361, Rarity.RARE, mage.cards.m.MirrorhallMimic.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mirrorhall Mimic", 68, Rarity.RARE, mage.cards.m.MirrorhallMimic.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mischievous Catgeist", 69, Rarity.UNCOMMON, mage.cards.m.MischievousCatgeist.class)); + cards.add(new SetCardInfo("Moldgraf Millipede", 209, Rarity.COMMON, mage.cards.m.MoldgrafMillipede.class)); + cards.add(new SetCardInfo("Moonlit Ambusher", 212, Rarity.UNCOMMON, mage.cards.m.MoonlitAmbusher.class)); + cards.add(new SetCardInfo("Mountain", 274, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 275, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 401, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 411, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Mulch", 210, Rarity.COMMON, mage.cards.m.Mulch.class)); + cards.add(new SetCardInfo("Nature's Embrace", 211, Rarity.COMMON, mage.cards.n.NaturesEmbrace.class)); + cards.add(new SetCardInfo("Nebelgast Beguiler", 25, Rarity.COMMON, mage.cards.n.NebelgastBeguiler.class)); + cards.add(new SetCardInfo("Necroduality", 362, Rarity.MYTHIC, mage.cards.n.Necroduality.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Necroduality", 70, Rarity.MYTHIC, mage.cards.n.Necroduality.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Nurturing Presence", 26, Rarity.COMMON, mage.cards.n.NurturingPresence.class)); + cards.add(new SetCardInfo("Oakshade Stalker", 212, Rarity.UNCOMMON, mage.cards.o.OakshadeStalker.class)); + cards.add(new SetCardInfo("Odious Witch", 127, Rarity.COMMON, mage.cards.o.OdiousWitch.class)); + cards.add(new SetCardInfo("Odric, Blood-Cursed", 243, Rarity.RARE, mage.cards.o.OdricBloodCursed.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Odric, Blood-Cursed", 314, Rarity.RARE, mage.cards.o.OdricBloodCursed.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Old Rutstein", 244, Rarity.RARE, mage.cards.o.OldRutstein.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Old Rutstein", 326, Rarity.RARE, mage.cards.o.OldRutstein.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Olivia's Attendants", 172, Rarity.RARE, mage.cards.o.OliviasAttendants.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Olivia's Attendants", 307, Rarity.RARE, mage.cards.o.OliviasAttendants.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Olivia, Crimson Bride", 245, Rarity.MYTHIC, mage.cards.o.OliviaCrimsonBride.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Olivia, Crimson Bride", 315, Rarity.MYTHIC, mage.cards.o.OliviaCrimsonBride.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Olivia, Crimson Bride", 343, Rarity.MYTHIC, mage.cards.o.OliviaCrimsonBride.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ollenbock Escort", 27, Rarity.UNCOMMON, mage.cards.o.OllenbockEscort.class)); + cards.add(new SetCardInfo("Overcharged Amalgam", 363, Rarity.RARE, mage.cards.o.OverchargedAmalgam.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Overcharged Amalgam", 71, Rarity.RARE, mage.cards.o.OverchargedAmalgam.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Packsong Pup", 213, Rarity.UNCOMMON, mage.cards.p.PacksongPup.class)); + cards.add(new SetCardInfo("Panicked Bystander", 28, Rarity.UNCOMMON, mage.cards.p.PanickedBystander.class)); + cards.add(new SetCardInfo("Parasitic Grasp", 123, Rarity.UNCOMMON, mage.cards.p.ParasiticGrasp.class)); + cards.add(new SetCardInfo("Parish-Blade Trainee", 29, Rarity.COMMON, mage.cards.p.ParishBladeTrainee.class)); + cards.add(new SetCardInfo("Patchwork Crawler", 364, Rarity.RARE, mage.cards.p.PatchworkCrawler.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Patchwork Crawler", 72, Rarity.RARE, mage.cards.p.PatchworkCrawler.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Path of Peril", 124, Rarity.RARE, mage.cards.p.PathOfPeril.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Path of Peril", 373, Rarity.RARE, mage.cards.p.PathOfPeril.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Persistent Specimen", 125, Rarity.COMMON, mage.cards.p.PersistentSpecimen.class)); + cards.add(new SetCardInfo("Piercing Light", 30, Rarity.COMMON, mage.cards.p.PiercingLight.class)); + cards.add(new SetCardInfo("Plains", 268, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Plains", 269, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Plains", 398, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 408, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Pointed Discussion", 126, Rarity.COMMON, mage.cards.p.PointedDiscussion.class)); + cards.add(new SetCardInfo("Pyre Spawn", 173, Rarity.COMMON, mage.cards.p.PyreSpawn.class)); + cards.add(new SetCardInfo("Radiant Grace", 31, Rarity.UNCOMMON, mage.cards.r.RadiantGrace.class)); + cards.add(new SetCardInfo("Radiant Restraints", 31, Rarity.UNCOMMON, mage.cards.r.RadiantRestraints.class)); + cards.add(new SetCardInfo("Ragged Recluse", 127, Rarity.COMMON, mage.cards.r.RaggedRecluse.class)); + cards.add(new SetCardInfo("Reckless Impulse", 174, Rarity.COMMON, mage.cards.r.RecklessImpulse.class)); + cards.add(new SetCardInfo("Reclusive Taxidermist", 214, Rarity.UNCOMMON, mage.cards.r.ReclusiveTaxidermist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Reclusive Taxidermist", 340, Rarity.UNCOMMON, mage.cards.r.ReclusiveTaxidermist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rending Flame", 175, Rarity.UNCOMMON, mage.cards.r.RendingFlame.class)); + cards.add(new SetCardInfo("Repository Skaab", 73, Rarity.COMMON, mage.cards.r.RepositorySkaab.class)); + cards.add(new SetCardInfo("Resistance Squad", 32, Rarity.UNCOMMON, mage.cards.r.ResistanceSquad.class)); + cards.add(new SetCardInfo("Restless Bloodseeker", 128, Rarity.UNCOMMON, mage.cards.r.RestlessBloodseeker.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Restless Bloodseeker", 295, Rarity.UNCOMMON, mage.cards.r.RestlessBloodseeker.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Retrieve", 215, Rarity.UNCOMMON, mage.cards.r.Retrieve.class)); + cards.add(new SetCardInfo("Revealing Eye", 101, Rarity.RARE, mage.cards.r.RevealingEye.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Revealing Eye", 367, Rarity.RARE, mage.cards.r.RevealingEye.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Riphook Raider", 203, Rarity.COMMON, mage.cards.r.RiphookRaider.class)); + cards.add(new SetCardInfo("Rot-Tide Gargantua", 129, Rarity.COMMON, mage.cards.r.RotTideGargantua.class)); + cards.add(new SetCardInfo("Runebound Wolf", 176, Rarity.UNCOMMON, mage.cards.r.RuneboundWolf.class)); + cards.add(new SetCardInfo("Runo Stromkirk", 246, Rarity.RARE, mage.cards.r.RunoStromkirk.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Runo Stromkirk", 316, Rarity.RARE, mage.cards.r.RunoStromkirk.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Runo Stromkirk", 327, Rarity.RARE, mage.cards.r.RunoStromkirk.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rural Recruit", 216, Rarity.COMMON, mage.cards.r.RuralRecruit.class)); + cards.add(new SetCardInfo("Sanctify", 33, Rarity.COMMON, mage.cards.s.Sanctify.class)); + cards.add(new SetCardInfo("Sanguine Statuette", 177, Rarity.UNCOMMON, mage.cards.s.SanguineStatuette.class)); + cards.add(new SetCardInfo("Savage Packmate", 234, Rarity.UNCOMMON, mage.cards.s.SavagePackmate.class)); + cards.add(new SetCardInfo("Savior of Ollenbock", 330, Rarity.MYTHIC, mage.cards.s.SaviorOfOllenbock.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Savior of Ollenbock", 34, Rarity.MYTHIC, mage.cards.s.SaviorOfOllenbock.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Savior of Ollenbock", 352, Rarity.MYTHIC, mage.cards.s.SaviorOfOllenbock.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sawblade Slinger", 217, Rarity.UNCOMMON, mage.cards.s.SawbladeSlinger.class)); + cards.add(new SetCardInfo("Scattered Thoughts", 74, Rarity.COMMON, mage.cards.s.ScatteredThoughts.class)); + cards.add(new SetCardInfo("Screaming Swarm", 75, Rarity.UNCOMMON, mage.cards.s.ScreamingSwarm.class)); + cards.add(new SetCardInfo("Selhoff Entomber", 76, Rarity.COMMON, mage.cards.s.SelhoffEntomber.class)); + cards.add(new SetCardInfo("Serpentine Ambush", 77, Rarity.COMMON, mage.cards.s.SerpentineAmbush.class)); + cards.add(new SetCardInfo("Shattered Sanctum", 264, Rarity.RARE, mage.cards.s.ShatteredSanctum.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shattered Sanctum", 283, Rarity.RARE, mage.cards.s.ShatteredSanctum.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sheltering Boughs", 218, Rarity.COMMON, mage.cards.s.ShelteringBoughs.class)); + cards.add(new SetCardInfo("Sigarda's Imprisonment", 35, Rarity.COMMON, mage.cards.s.SigardasImprisonment.class)); + cards.add(new SetCardInfo("Sigarda's Summons", 353, Rarity.RARE, mage.cards.s.SigardasSummons.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sigarda's Summons", 36, Rarity.RARE, mage.cards.s.SigardasSummons.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sigarda's Summons", 404, Rarity.RARE, mage.cards.s.SigardasSummons.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sigardian Paladin", 247, Rarity.UNCOMMON, mage.cards.s.SigardianPaladin.class)); + cards.add(new SetCardInfo("Sinner's Judgment", 12, Rarity.MYTHIC, mage.cards.s.SinnersJudgment.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sinner's Judgment", 348, Rarity.MYTHIC, mage.cards.s.SinnersJudgment.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Skulking Killer", 130, Rarity.UNCOMMON, mage.cards.s.SkulkingKiller.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Skulking Killer", 296, Rarity.UNCOMMON, mage.cards.s.SkulkingKiller.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Skull Skaab", 248, Rarity.UNCOMMON, mage.cards.s.SkullSkaab.class)); + cards.add(new SetCardInfo("Skywarp Skaab", 78, Rarity.COMMON, mage.cards.s.SkywarpSkaab.class)); + cards.add(new SetCardInfo("Snarling Wolf", 219, Rarity.COMMON, mage.cards.s.SnarlingWolf.class)); + cards.add(new SetCardInfo("Sorin the Mirthless", 131, Rarity.MYTHIC, mage.cards.s.SorinTheMirthless.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sorin the Mirthless", 278, Rarity.MYTHIC, mage.cards.s.SorinTheMirthless.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sorin the Mirthless", 297, Rarity.MYTHIC, mage.cards.s.SorinTheMirthless.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sorin the Mirthless", 337, Rarity.MYTHIC, mage.cards.s.SorinTheMirthless.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Soulcipher Board", 79, Rarity.UNCOMMON, mage.cards.s.SoulcipherBoard.class)); + cards.add(new SetCardInfo("Spectral Binding", 48, Rarity.COMMON, mage.cards.s.SpectralBinding.class)); + cards.add(new SetCardInfo("Spiked Ripsaw", 220, Rarity.UNCOMMON, mage.cards.s.SpikedRipsaw.class)); + cards.add(new SetCardInfo("Splendid Reclamation", 221, Rarity.RARE, mage.cards.s.SplendidReclamation.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Splendid Reclamation", 393, Rarity.RARE, mage.cards.s.SplendidReclamation.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Spore Crawler", 222, Rarity.COMMON, mage.cards.s.SporeCrawler.class)); + cards.add(new SetCardInfo("Sporeback Wolf", 223, Rarity.COMMON, mage.cards.s.SporebackWolf.class)); + cards.add(new SetCardInfo("Steelclad Spirit", 80, Rarity.COMMON, mage.cards.s.SteelcladSpirit.class)); + cards.add(new SetCardInfo("Stensia Uprising", 178, Rarity.RARE, mage.cards.s.StensiaUprising.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Stensia Uprising", 381, Rarity.RARE, mage.cards.s.StensiaUprising.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Stitched Assistant", 81, Rarity.COMMON, mage.cards.s.StitchedAssistant.class)); + cards.add(new SetCardInfo("Stormcarved Coast", 265, Rarity.RARE, mage.cards.s.StormcarvedCoast.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Stormcarved Coast", 284, Rarity.RARE, mage.cards.s.StormcarvedCoast.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Stormchaser Drake", 82, Rarity.UNCOMMON, mage.cards.s.StormchaserDrake.class)); + cards.add(new SetCardInfo("Sundown Pass", 266, Rarity.RARE, mage.cards.s.SundownPass.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sundown Pass", 285, Rarity.RARE, mage.cards.s.SundownPass.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Supernatural Rescue", 37, Rarity.COMMON, mage.cards.s.SupernaturalRescue.class)); + cards.add(new SetCardInfo("Sure Strike", 179, Rarity.COMMON, mage.cards.s.SureStrike.class)); + cards.add(new SetCardInfo("Swamp", 272, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 273, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 400, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 410, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Syncopate", 83, Rarity.COMMON, mage.cards.s.Syncopate.class)); + cards.add(new SetCardInfo("Syphon Essence", 84, Rarity.COMMON, mage.cards.s.SyphonEssence.class)); + cards.add(new SetCardInfo("Thalia, Guardian of Thraben", 318, Rarity.RARE, mage.cards.t.ThaliaGuardianOfThraben.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Thalia, Guardian of Thraben", 331, Rarity.RARE, mage.cards.t.ThaliaGuardianOfThraben.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Thalia, Guardian of Thraben", 38, Rarity.RARE, mage.cards.t.ThaliaGuardianOfThraben.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Thirst for Discovery", 333, Rarity.UNCOMMON, mage.cards.t.ThirstForDiscovery.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Thirst for Discovery", 85, Rarity.UNCOMMON, mage.cards.t.ThirstForDiscovery.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Torens, Fist of the Angels", 249, Rarity.RARE, mage.cards.t.TorensFistOfTheAngels.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Torens, Fist of the Angels", 328, Rarity.RARE, mage.cards.t.TorensFistOfTheAngels.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Torens, Fist of the Angels", 344, Rarity.RARE, mage.cards.t.TorensFistOfTheAngels.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Toxic Scorpion", 224, Rarity.COMMON, mage.cards.t.ToxicScorpion.class)); + cards.add(new SetCardInfo("Toxrill, the Corrosive", 132, Rarity.MYTHIC, mage.cards.t.ToxrillTheCorrosive.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Toxrill, the Corrosive", 321, Rarity.MYTHIC, mage.cards.t.ToxrillTheCorrosive.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Traveling Minister", 39, Rarity.COMMON, mage.cards.t.TravelingMinister.class)); + cards.add(new SetCardInfo("Twinblade Geist", 40, Rarity.UNCOMMON, mage.cards.t.TwinbladeGeist.class)); + cards.add(new SetCardInfo("Twinblade Invocation", 40, Rarity.UNCOMMON, mage.cards.t.TwinbladeInvocation.class)); + cards.add(new SetCardInfo("Ulvenwald Behemoth", 225, Rarity.RARE, mage.cards.u.UlvenwaldBehemoth.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ulvenwald Behemoth", 394, Rarity.RARE, mage.cards.u.UlvenwaldBehemoth.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ulvenwald Oddity", 225, Rarity.RARE, mage.cards.u.UlvenwaldOddity.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ulvenwald Oddity", 394, Rarity.RARE, mage.cards.u.UlvenwaldOddity.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Undead Butler", 133, Rarity.UNCOMMON, mage.cards.u.UndeadButler.class)); + cards.add(new SetCardInfo("Undying Malice", 134, Rarity.COMMON, mage.cards.u.UndyingMalice.class)); + cards.add(new SetCardInfo("Unhallowed Phalanx", 135, Rarity.COMMON, mage.cards.u.UnhallowedPhalanx.class)); + cards.add(new SetCardInfo("Unholy Officiant", 286, Rarity.COMMON, mage.cards.u.UnholyOfficiant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Unholy Officiant", 41, Rarity.COMMON, mage.cards.u.UnholyOfficiant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Valorous Stance", 42, Rarity.UNCOMMON, mage.cards.v.ValorousStance.class)); + cards.add(new SetCardInfo("Vampire Slayer", 43, Rarity.COMMON, mage.cards.v.VampireSlayer.class)); + cards.add(new SetCardInfo("Vampire's Kiss", 136, Rarity.COMMON, mage.cards.v.VampiresKiss.class)); + cards.add(new SetCardInfo("Vampires' Vengeance", 180, Rarity.UNCOMMON, mage.cards.v.VampiresVengeance.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Vampires' Vengeance", 339, Rarity.UNCOMMON, mage.cards.v.VampiresVengeance.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Vilespawn Spider", 250, Rarity.UNCOMMON, mage.cards.v.VilespawnSpider.class)); + cards.add(new SetCardInfo("Voice of the Blessed", 354, Rarity.RARE, mage.cards.v.VoiceOfTheBlessed.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Voice of the Blessed", 44, Rarity.RARE, mage.cards.v.VoiceOfTheBlessed.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Volatile Arsonist", 181, Rarity.MYTHIC, mage.cards.v.VolatileArsonist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Volatile Arsonist", 382, Rarity.MYTHIC, mage.cards.v.VolatileArsonist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Voldaren Bloodcaster", 137, Rarity.RARE, mage.cards.v.VoldarenBloodcaster.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Voldaren Bloodcaster", 298, Rarity.RARE, mage.cards.v.VoldarenBloodcaster.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Voldaren Bloodcaster", 338, Rarity.RARE, mage.cards.v.VoldarenBloodcaster.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Voldaren Epicure", 182, Rarity.COMMON, mage.cards.v.VoldarenEpicure.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Voldaren Epicure", 308, Rarity.COMMON, mage.cards.v.VoldarenEpicure.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Voldaren Estate", 267, Rarity.RARE, mage.cards.v.VoldarenEstate.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Voldaren Estate", 397, Rarity.RARE, mage.cards.v.VoldarenEstate.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Voldaren Estate", 403, Rarity.RARE, mage.cards.v.VoldarenEstate.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Volt-Charged Berserker", 183, Rarity.UNCOMMON, mage.cards.v.VoltChargedBerserker.class)); + cards.add(new SetCardInfo("Voltaic Visionary", 183, Rarity.UNCOMMON, mage.cards.v.VoltaicVisionary.class)); + cards.add(new SetCardInfo("Wandering Mind", 251, Rarity.UNCOMMON, mage.cards.w.WanderingMind.class)); + cards.add(new SetCardInfo("Wanderlight Spirit", 86, Rarity.COMMON, mage.cards.w.WanderlightSpirit.class)); + cards.add(new SetCardInfo("Wash Away", 87, Rarity.UNCOMMON, mage.cards.w.WashAway.class)); + cards.add(new SetCardInfo("Weary Prisoner", 184, Rarity.COMMON, mage.cards.w.WearyPrisoner.class)); + cards.add(new SetCardInfo("Weaver of Blossoms", 226, Rarity.COMMON, mage.cards.w.WeaverOfBlossoms.class)); + cards.add(new SetCardInfo("Wedding Announcement", 355, Rarity.RARE, mage.cards.w.WeddingAnnouncement.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Wedding Announcement", 45, Rarity.RARE, mage.cards.w.WeddingAnnouncement.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Wedding Crasher", 229, Rarity.UNCOMMON, mage.cards.w.WeddingCrasher.class)); + cards.add(new SetCardInfo("Wedding Festivity", 355, Rarity.RARE, mage.cards.w.WeddingFestivity.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Wedding Festivity", 45, Rarity.RARE, mage.cards.w.WeddingFestivity.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Wedding Invitation", 260, Rarity.COMMON, mage.cards.w.WeddingInvitation.class)); + cards.add(new SetCardInfo("Wedding Security", 138, Rarity.UNCOMMON, mage.cards.w.WeddingSecurity.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Wedding Security", 299, Rarity.UNCOMMON, mage.cards.w.WeddingSecurity.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Welcoming Vampire", 287, Rarity.RARE, mage.cards.w.WelcomingVampire.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Welcoming Vampire", 46, Rarity.RARE, mage.cards.w.WelcomingVampire.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Whispering Wizard", 88, Rarity.UNCOMMON, mage.cards.w.WhisperingWizard.class)); + cards.add(new SetCardInfo("Wildsong Howler", 205, Rarity.RARE, mage.cards.w.WildsongHowler.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Wildsong Howler", 392, Rarity.RARE, mage.cards.w.WildsongHowler.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Winged Portent", 365, Rarity.RARE, mage.cards.w.WingedPortent.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Winged Portent", 89, Rarity.RARE, mage.cards.w.WingedPortent.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Witch's Web", 227, Rarity.COMMON, mage.cards.w.WitchsWeb.class)); + cards.add(new SetCardInfo("Witness the Future", 90, Rarity.UNCOMMON, mage.cards.w.WitnessTheFuture.class)); + cards.add(new SetCardInfo("Wolf Strike", 228, Rarity.COMMON, mage.cards.w.WolfStrike.class)); + cards.add(new SetCardInfo("Wolfkin Outcast", 229, Rarity.UNCOMMON, mage.cards.w.WolfkinOutcast.class)); + cards.add(new SetCardInfo("Wrathful Jailbreaker", 184, Rarity.COMMON, mage.cards.w.WrathfulJailbreaker.class)); + cards.add(new SetCardInfo("Wretched Throng", 91, Rarity.COMMON, mage.cards.w.WretchedThrong.class)); + } + + // add common double faced card to booster + @Override + protected void addDoubleFace(List booster) { + addToBooster(booster, getSpecialCardsByRarity(Rarity.COMMON)); + } + + @Override + public BoosterCollator createCollator() { + return new InnistradCrimsonVowCollator(); + } +} + +// Booster collation info from https://www.lethe.xyz/mtg/collation/vow.html +// Using USA collation for common/uncommon, rare collation inferred from other sets +class InnistradCrimsonVowCollator implements BoosterCollator { + private final CardRun commonA = new CardRun(true, "158", "59", "11", "174", "74", "1", "303", "25", "73", "26", "156", "91", "19", "164", "80", "43", "139", "54", "41", "166", "55", "30", "144", "29", "78", "18", "161", "57", "35", "179", "86", "11", "168", "81", "37", "142", "74", "13", "174", "59", "15", "147", "77", "25", "158", "47", "1", "146", "73", "26", "156", "91", "19", "164", "54", "43", "139", "80", "30", "166", "55", "41", "301", "18", "78", "29", "179", "57", "35", "161", "81", "37", "168", "86", "13", "147", "74", "11", "142", "77", "15", "174", "59", "1", "158", "47", "25", "306", "73", "43", "146", "91", "19", "164", "80", "26", "139", "54", "286", "166", "55", "30", "144", "81", "35", "179", "57", "29", "168", "86", "18", "161", "78", "13", "142", "77", "15", "147", "47", "37"); + private final CardRun commonB = new CardRun(true, "95", "208", "125", "185", "94", "218", "116", "194", "129", "219", "106", "227", "135", "228", "136", "223", "122", "224", "102", "210", "134", "209", "113", "211", "92", "188", "105", "199", "125", "216", "126", "218", "95", "208", "94", "194", "116", "185", "129", "219", "106", "227", "102", "223", "135", "210", "136", "228", "92", "224", "122", "211", "113", "209", "134", "199", "126", "216", "105", "188", "125", "208", "94", "185", "95", "218", "116", "194", "129", "219", "106", "227", "102", "223", "135", "224", "136", "228", "122", "210", "92", "209", "134", "199", "113", "211", "126", "188", "105", "218", "95", "216", "125", "208", "116", "185", "94", "194", "106", "227", "129", "219", "135", "210", "136", "228", "122", "223", "92", "224", "102", "211", "134", "209", "113", "199", "105", "188", "126", "216"); + private final CardRun commonC = new CardRun(true, "263", "173", "83", "96", "196", "33", "153", "260", "51", "24", "182", "292", "252", "84", "222", "39", "257", "76", "263", "155", "254", "24", "83", "96", "260", "173", "51", "33", "153", "196", "84", "252", "308", "39", "254", "155", "76", "222", "257", "114", "173", "83", "288", "263", "33", "153", "260", "196", "51", "182", "24", "252", "84", "114", "39", "257", "222", "76", "155", "254", "83", "173", "24", "260", "263", "153", "33", "96", "51", "182", "196", "252", "84", "39", "254", "155", "292", "76", "257", "222", "173", "83", "263", "96", "33", "153", "196", "260", "51", "24", "308", "252", "114", "84", "39", "222", "257", "76", "155", "254", "263", "83", "173", "24", "260", "288", "153", "33", "196", "51", "182", "39", "252", "84", "114", "254", "155", "76", "222", "257"); + private final CardRun commonDFC = new CardRun(true, "48", "10", "127", "22", "184", "203", "104", "226", "10", "66", "157", "48", "203", "22", "184", "127", "226", "66", "104", "157", "10", "48", "127", "203", "22", "184", "66", "104", "226", "10", "48", "127", "157", "22", "203", "66", "184", "226", "10", "104", "48", "157", "22", "184", "127", "203", "104", "226", "66", "10", "48", "157", "22", "127", "203", "184", "226", "104", "66", "48", "10", "203", "22", "157", "127", "184", "226", "66", "104", "157", "48", "10", "127", "22", "184", "203", "104", "66", "226", "127", "48", "10", "157", "22", "184", "203", "66", "104", "226", "10", "48", "22", "157", "203", "127", "184", "66", "226", "104", "10", "48", "22", "157", "203", "127", "184", "66", "226", "157", "104", "48", "10", "203", "22", "127", "226", "184", "66", "104", "157"); + private final CardRun uncommonA = new CardRun(true, "171", "133", "217", "67", "176", "7", "138", "189", "88", "313", "145", "4", "214", "93", "159", "56", "248", "123", "213", "180", "42", "192", "117", "52", "32", "220", "60", "230", "160", "110", "193", "85", "14", "207", "310", "169", "75", "215", "130", "241", "217", "171", "67", "133", "189", "176", "7", "299", "88", "242", "214", "145", "93", "4", "159", "213", "56", "248", "42", "192", "123", "52", "180", "32", "220", "117", "60", "160", "230", "193", "110", "14", "232", "85", "207", "169", "296", "241", "215", "75", "171", "217", "67", "133", "176", "7", "189", "88", "138", "302", "242", "214", "4", "93", "56", "159", "248", "213", "42", "123", "180", "192", "52", "32", "117", "220", "160", "230", "60", "14", "110", "193", "85", "232", "169", "207", "130", "75", "312", "215"); + private final CardRun uncommonB = new CardRun(true, "90", "2", "190", "16", "253", "82", "8", "247", "163", "3", "107", "251", "87", "250", "259", "120", "27", "112", "90", "16", "177", "247", "253", "175", "107", "2", "190", "3", "163", "82", "8", "251", "27", "120", "87", "250", "259", "175", "90", "16", "177", "112", "253", "2", "190", "247", "8", "82", "107", "3", "163", "251", "87", "120", "27", "250", "177", "112", "259", "175"); + private final CardRun uncommonDFC = new CardRun(true, "233", "256", "99", "206", "31", "289", "141", "62", "183", "128", "229", "49", "40", "167", "28", "198", "79", "9", "143", "69", "294", "234", "212", "256", "99", "233", "31", "97", "206", "300", "62", "229", "183", "49", "40", "167", "295", "28", "198", "9", "143", "79", "234", "69", "212", "121", "233", "256", "206", "99", "31", "141", "97", "62", "183", "229", "40", "49", "128", "28", "167", "198", "143", "9", "79", "69", "234", "121", "212"); + private final CardRun rare = new CardRun(false, "5", "20", "23", "36", "38", "44", "46", "53", "58", "61", "63", "64", "71", "72", "89", "98", "103", "108", "109", "111", "115", "118", "124", "140", "150", "151", "152", "154", "165", "172", "178", "186", "197", "200", "201", "202", "204", "221", "231", "237", "238", "239", "243", "244", "249", "255", "258", "261", "262", "264", "265", "266", "267", "5", "20", "23", "36", "38", "44", "46", "53", "58", "61", "63", "64", "71", "72", "89", "98", "103", "108", "109", "111", "115", "118", "124", "140", "150", "151", "152", "154", "165", "172", "178", "186", "197", "200", "201", "202", "204", "221", "231", "237", "238", "239", "243", "244", "249", "255", "258", "261", "262", "264", "265", "266", "267", "6", "17", "34", "50", "70", "100", "131", "132", "148", "149", "170", "191", "195", "240", "245"); + private final CardRun rareDFC = new CardRun(false, "21", "45", "68", "101", "137", "162", "205", "225", "235", "236", "246", "21", "45", "68", "101", "137", "162", "205", "225", "235", "236", "246", "12", "65", "119", "181", "187"); + private final CardRun land = new CardRun(false, "268", "269", "270", "271", "272", "273", "274", "275", "276", "277"); + + private final BoosterStructure AAAABBBCCD = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, commonB, + commonC, commonC, + commonDFC + ); + + private final BoosterStructure AAD = new BoosterStructure(uncommonA, uncommonA, uncommonDFC, rare); + private final BoosterStructure ABD = new BoosterStructure(uncommonA, uncommonB, uncommonDFC, rare); + private final BoosterStructure AAB = new BoosterStructure(uncommonA, uncommonA, uncommonB, rareDFC); + private final BoosterStructure ABB = new BoosterStructure(uncommonA, uncommonB, uncommonB, rareDFC); + private final BoosterStructure L1 = new BoosterStructure(land); + + private final RarityConfiguration commonRuns = new RarityConfiguration(AAAABBBCCD); + // In order for equal numbers of each uncommon to exist, the average booster must contain: + // 1.45 A uncommons (120 / 83) + // 0.72 B uncommons (60 / 83) + // 0.83 DFC uncommon (69 / 83) + private final RarityConfiguration uncommonRuns = new RarityConfiguration( + AAD, AAD, AAD, AAD, AAD, AAD, + AAD, AAD, AAD, AAD, AAD, AAD, + AAD, AAD, AAD, AAD, AAD, AAD, + AAD, AAD, AAD, AAD, AAD, AAD, + AAD, AAD, AAD, AAD, AAD, AAD, + + ABD, ABD, ABD, ABD, ABD, ABD, + ABD, ABD, ABD, ABD, ABD, ABD, + ABD, ABD, ABD, ABD, ABD, ABD, + ABD, ABD, ABD, ABD, ABD, ABD, + ABD, ABD, ABD, ABD, ABD, ABD, + ABD, ABD, ABD, ABD, ABD, ABD, + ABD, ABD, ABD, + + AAB, AAB, AAB, AAB, AAB, AAB, AAB, + ABB, ABB, ABB, ABB, ABB, ABB, ABB + ); + private final RarityConfiguration landRuns = new RarityConfiguration(L1); + + @Override + public List makeBooster() { + List booster = new ArrayList<>(); + booster.addAll(commonRuns.getNext().makeRun()); + booster.addAll(uncommonRuns.getNext().makeRun()); + booster.addAll(landRuns.getNext().makeRun()); + return booster; + } +} diff --git a/Mage.Sets/src/mage/sets/InnistradDoubleFeature.java b/Mage.Sets/src/mage/sets/InnistradDoubleFeature.java new file mode 100644 index 00000000000..0f57868bee1 --- /dev/null +++ b/Mage.Sets/src/mage/sets/InnistradDoubleFeature.java @@ -0,0 +1,657 @@ +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * @author TheElk801 + */ +public final class InnistradDoubleFeature extends ExpansionSet { + + private static final InnistradDoubleFeature instance = new InnistradDoubleFeature(); + + public static InnistradDoubleFeature getInstance() { + return instance; + } + + private InnistradDoubleFeature() { + super("Innistrad: Double Feature", "DBL", ExpansionSet.buildDate(2021, 1, 28), SetType.SUPPLEMENTAL); + this.hasBasicLands = false; + // TODO: add booster generation + + cards.add(new SetCardInfo("Abandon the Post", 127, Rarity.COMMON, mage.cards.a.AbandonThePost.class)); + cards.add(new SetCardInfo("Abrade", 406, Rarity.COMMON, mage.cards.a.Abrade.class)); + cards.add(new SetCardInfo("Adamant Will", 268, Rarity.COMMON, mage.cards.a.AdamantWill.class)); + cards.add(new SetCardInfo("Adeline, Resplendent Cathar", 1, Rarity.RARE, mage.cards.a.AdelineResplendentCathar.class)); + cards.add(new SetCardInfo("Aim for the Head", 359, Rarity.COMMON, mage.cards.a.AimForTheHead.class)); + cards.add(new SetCardInfo("Alchemist's Gambit", 407, Rarity.RARE, mage.cards.a.AlchemistsGambit.class)); + cards.add(new SetCardInfo("Alchemist's Retrieval", 314, Rarity.COMMON, mage.cards.a.AlchemistsRetrieval.class)); + cards.add(new SetCardInfo("Alluring Suitor", 408, Rarity.UNCOMMON, mage.cards.a.AlluringSuitor.class)); + cards.add(new SetCardInfo("Ambitious Farmhand", 2, Rarity.UNCOMMON, mage.cards.a.AmbitiousFarmhand.class)); + cards.add(new SetCardInfo("Ancestor's Embrace", 289, Rarity.COMMON, mage.cards.a.AncestorsEmbrace.class)); + cards.add(new SetCardInfo("Ancestral Anger", 409, Rarity.COMMON, mage.cards.a.AncestralAnger.class)); + cards.add(new SetCardInfo("Ancient Lumberknot", 497, Rarity.UNCOMMON, mage.cards.a.AncientLumberknot.class)); + cards.add(new SetCardInfo("Angelfire Ignition", 209, Rarity.RARE, mage.cards.a.AngelfireIgnition.class)); + cards.add(new SetCardInfo("Angelic Enforcer", 17, Rarity.MYTHIC, mage.cards.a.AngelicEnforcer.class)); + cards.add(new SetCardInfo("Angelic Quartermaster", 269, Rarity.UNCOMMON, mage.cards.a.AngelicQuartermaster.class)); + cards.add(new SetCardInfo("Anje, Maid of Dishonor", 498, Rarity.RARE, mage.cards.a.AnjeMaidOfDishonor.class)); + cards.add(new SetCardInfo("Apprentice Sharpshooter", 452, Rarity.COMMON, mage.cards.a.ApprenticeSharpshooter.class)); + cards.add(new SetCardInfo("Arcane Infusion", 210, Rarity.UNCOMMON, mage.cards.a.ArcaneInfusion.class)); + cards.add(new SetCardInfo("Archghoul of Thraben", 360, Rarity.UNCOMMON, mage.cards.a.ArchghoulOfThraben.class)); + cards.add(new SetCardInfo("Archive Haunt", 68, Rarity.UNCOMMON, mage.cards.a.ArchiveHaunt.class)); + cards.add(new SetCardInfo("Ardent Elementalist", 128, Rarity.COMMON, mage.cards.a.ArdentElementalist.class)); + cards.add(new SetCardInfo("Arlinn, the Moon's Fury", 211, Rarity.MYTHIC, mage.cards.a.ArlinnTheMoonsFury.class)); + cards.add(new SetCardInfo("Arlinn, the Pack's Hope", 211, Rarity.MYTHIC, mage.cards.a.ArlinnThePacksHope.class)); + cards.add(new SetCardInfo("Arm the Cathars", 270, Rarity.UNCOMMON, mage.cards.a.ArmTheCathars.class)); + cards.add(new SetCardInfo("Arrogant Outlaw", 84, Rarity.COMMON, mage.cards.a.ArrogantOutlaw.class)); + cards.add(new SetCardInfo("Ascendant Packleader", 453, Rarity.RARE, mage.cards.a.AscendantPackleader.class)); + cards.add(new SetCardInfo("Ashmouth Dragon", 159, Rarity.RARE, mage.cards.a.AshmouthDragon.class)); + cards.add(new SetCardInfo("Augur of Autumn", 168, Rarity.RARE, mage.cards.a.AugurOfAutumn.class)); + cards.add(new SetCardInfo("Avabruck Caretaker", 454, Rarity.MYTHIC, mage.cards.a.AvabruckCaretaker.class)); + cards.add(new SetCardInfo("Awoken Demon", 100, Rarity.COMMON, mage.cards.a.AwokenDemon.class)); + cards.add(new SetCardInfo("Baithook Angler", 42, Rarity.COMMON, mage.cards.b.BaithookAngler.class)); + cards.add(new SetCardInfo("Ballista Watcher", 410, Rarity.UNCOMMON, mage.cards.b.BallistaWatcher.class)); + cards.add(new SetCardInfo("Ballista Wielder", 410, Rarity.UNCOMMON, mage.cards.b.BallistaWielder.class)); + cards.add(new SetCardInfo("Baneblade Scoundrel", 85, Rarity.UNCOMMON, mage.cards.b.BanebladeScoundrel.class)); + cards.add(new SetCardInfo("Baneclaw Marauder", 85, Rarity.UNCOMMON, mage.cards.b.BaneclawMarauder.class)); + cards.add(new SetCardInfo("Bat Whisperer", 86, Rarity.COMMON, mage.cards.b.BatWhisperer.class)); + cards.add(new SetCardInfo("Belligerent Guest", 411, Rarity.COMMON, mage.cards.b.BelligerentGuest.class)); + cards.add(new SetCardInfo("Beloved Beggar", 3, Rarity.UNCOMMON, mage.cards.b.BelovedBeggar.class)); + cards.add(new SetCardInfo("Benevolent Geist", 61, Rarity.RARE, mage.cards.b.BenevolentGeist.class)); + cards.add(new SetCardInfo("Bereaved Survivor", 4, Rarity.UNCOMMON, mage.cards.b.BereavedSurvivor.class)); + cards.add(new SetCardInfo("Binding Geist", 315, Rarity.COMMON, mage.cards.b.BindingGeist.class)); + cards.add(new SetCardInfo("Biolume Egg", 316, Rarity.UNCOMMON, mage.cards.b.BiolumeEgg.class)); + cards.add(new SetCardInfo("Biolume Serpent", 316, Rarity.UNCOMMON, mage.cards.b.BiolumeSerpent.class)); + cards.add(new SetCardInfo("Bird Admirer", 169, Rarity.COMMON, mage.cards.b.BirdAdmirer.class)); + cards.add(new SetCardInfo("Bladebrand", 87, Rarity.COMMON, mage.cards.b.Bladebrand.class)); + cards.add(new SetCardInfo("Bladestitched Skaab", 212, Rarity.UNCOMMON, mage.cards.b.BladestitchedSkaab.class)); + cards.add(new SetCardInfo("Bleed Dry", 361, Rarity.COMMON, mage.cards.b.BleedDry.class)); + cards.add(new SetCardInfo("Blessed Defiance", 5, Rarity.COMMON, mage.cards.b.BlessedDefiance.class)); + cards.add(new SetCardInfo("Blood Fountain", 362, Rarity.COMMON, mage.cards.b.BloodFountain.class)); + cards.add(new SetCardInfo("Blood Hypnotist", 412, Rarity.UNCOMMON, mage.cards.b.BloodHypnotist.class)); + cards.add(new SetCardInfo("Blood Pact", 88, Rarity.COMMON, mage.cards.b.BloodPact.class)); + cards.add(new SetCardInfo("Blood Petal Celebrant", 413, Rarity.COMMON, mage.cards.b.BloodPetalCelebrant.class)); + cards.add(new SetCardInfo("Blood Servitor", 519, Rarity.COMMON, mage.cards.b.BloodServitor.class)); + cards.add(new SetCardInfo("Bloodbat Summoner", 404, Rarity.RARE, mage.cards.b.BloodbatSummoner.class)); + cards.add(new SetCardInfo("Bloodcrazed Socialite", 363, Rarity.COMMON, mage.cards.b.BloodcrazedSocialite.class)); + cards.add(new SetCardInfo("Bloodline Culling", 89, Rarity.RARE, mage.cards.b.BloodlineCulling.class)); + cards.add(new SetCardInfo("Bloodsoaked Reveler", 395, Rarity.UNCOMMON, mage.cards.b.BloodsoakedReveler.class)); + cards.add(new SetCardInfo("Bloodsworn Knight", 364, Rarity.UNCOMMON, mage.cards.b.BloodswornKnight.class)); + cards.add(new SetCardInfo("Bloodsworn Squire", 364, Rarity.UNCOMMON, mage.cards.b.BloodswornSquire.class)); + cards.add(new SetCardInfo("Bloodthirsty Adversary", 129, Rarity.MYTHIC, mage.cards.b.BloodthirstyAdversary.class)); + cards.add(new SetCardInfo("Bloodtithe Collector", 90, Rarity.UNCOMMON, mage.cards.b.BloodtitheCollector.class)); + cards.add(new SetCardInfo("Bloodtithe Harvester", 499, Rarity.UNCOMMON, mage.cards.b.BloodtitheHarvester.class)); + cards.add(new SetCardInfo("Bloodvial Purveyor", 365, Rarity.RARE, mage.cards.b.BloodvialPurveyor.class)); + cards.add(new SetCardInfo("Bloody Betrayal", 414, Rarity.COMMON, mage.cards.b.BloodyBetrayal.class)); + cards.add(new SetCardInfo("Blossom-Clad Werewolf", 493, Rarity.COMMON, mage.cards.b.BlossomCladWerewolf.class)); + cards.add(new SetCardInfo("Boarded Window", 520, Rarity.UNCOMMON, mage.cards.b.BoardedWindow.class)); + cards.add(new SetCardInfo("Borrowed Time", 6, Rarity.UNCOMMON, mage.cards.b.BorrowedTime.class)); + cards.add(new SetCardInfo("Bounding Wolf", 170, Rarity.COMMON, mage.cards.b.BoundingWolf.class)); + cards.add(new SetCardInfo("Bramble Armor", 171, Rarity.COMMON, mage.cards.b.BrambleArmor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bramble Armor", 455, Rarity.COMMON, mage.cards.b.BrambleArmor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bramble Wurm", 456, Rarity.UNCOMMON, mage.cards.b.BrambleWurm.class)); + cards.add(new SetCardInfo("Briarbridge Tracker", 172, Rarity.RARE, mage.cards.b.BriarbridgeTracker.class)); + cards.add(new SetCardInfo("Bride's Gown", 271, Rarity.UNCOMMON, mage.cards.b.BridesGown.class)); + cards.add(new SetCardInfo("Brimstone Vandal", 130, Rarity.COMMON, mage.cards.b.BrimstoneVandal.class)); + cards.add(new SetCardInfo("Brine Comber", 500, Rarity.UNCOMMON, mage.cards.b.BrineComber.class)); + cards.add(new SetCardInfo("Brinebound Gift", 500, Rarity.UNCOMMON, mage.cards.b.BrineboundGift.class)); + cards.add(new SetCardInfo("Brood Weaver", 173, Rarity.UNCOMMON, mage.cards.b.BroodWeaver.class)); + cards.add(new SetCardInfo("Brutal Cathar", 7, Rarity.RARE, mage.cards.b.BrutalCathar.class)); + cards.add(new SetCardInfo("Burly Breaker", 174, Rarity.UNCOMMON, mage.cards.b.BurlyBreaker.class)); + cards.add(new SetCardInfo("Burn Down the House", 131, Rarity.RARE, mage.cards.b.BurnDownTheHouse.class)); + cards.add(new SetCardInfo("Burn the Accursed", 132, Rarity.COMMON, mage.cards.b.BurnTheAccursed.class)); + cards.add(new SetCardInfo("By Invitation Only", 272, Rarity.RARE, mage.cards.b.ByInvitationOnly.class)); + cards.add(new SetCardInfo("Cackling Culprit", 295, Rarity.UNCOMMON, mage.cards.c.CacklingCulprit.class)); + cards.add(new SetCardInfo("Can't Stay Away", 213, Rarity.RARE, mage.cards.c.CantStayAway.class)); + cards.add(new SetCardInfo("Candlegrove Witch", 8, Rarity.COMMON, mage.cards.c.CandlegroveWitch.class)); + cards.add(new SetCardInfo("Candlelit Cavalry", 175, Rarity.COMMON, mage.cards.c.CandlelitCavalry.class)); + cards.add(new SetCardInfo("Candletrap", 9, Rarity.COMMON, mage.cards.c.Candletrap.class)); + cards.add(new SetCardInfo("Cartographer's Survey", 457, Rarity.UNCOMMON, mage.cards.c.CartographersSurvey.class)); + cards.add(new SetCardInfo("Catapult Captain", 366, Rarity.UNCOMMON, mage.cards.c.CatapultCaptain.class)); + cards.add(new SetCardInfo("Catapult Fodder", 366, Rarity.UNCOMMON, mage.cards.c.CatapultFodder.class)); + cards.add(new SetCardInfo("Cathar Commando", 10, Rarity.COMMON, mage.cards.c.CatharCommando.class)); + cards.add(new SetCardInfo("Cathar's Call", 11, Rarity.UNCOMMON, mage.cards.c.CatharsCall.class)); + cards.add(new SetCardInfo("Cathartic Pyre", 133, Rarity.UNCOMMON, mage.cards.c.CatharticPyre.class)); + cards.add(new SetCardInfo("Catlike Curiosity", 336, Rarity.UNCOMMON, mage.cards.c.CatlikeCuriosity.class)); + cards.add(new SetCardInfo("Celestus Sanctifier", 12, Rarity.COMMON, mage.cards.c.CelestusSanctifier.class)); + cards.add(new SetCardInfo("Cemetery Desecrator", 367, Rarity.MYTHIC, mage.cards.c.CemeteryDesecrator.class)); + cards.add(new SetCardInfo("Cemetery Gatekeeper", 415, Rarity.MYTHIC, mage.cards.c.CemeteryGatekeeper.class)); + cards.add(new SetCardInfo("Cemetery Illuminator", 317, Rarity.MYTHIC, mage.cards.c.CemeteryIlluminator.class)); + cards.add(new SetCardInfo("Cemetery Protector", 273, Rarity.MYTHIC, mage.cards.c.CemeteryProtector.class)); + cards.add(new SetCardInfo("Cemetery Prowler", 458, Rarity.MYTHIC, mage.cards.c.CemeteryProwler.class)); + cards.add(new SetCardInfo("Ceremonial Knife", 521, Rarity.COMMON, mage.cards.c.CeremonialKnife.class)); + cards.add(new SetCardInfo("Champion of the Perished", 91, Rarity.RARE, mage.cards.c.ChampionOfThePerished.class)); + cards.add(new SetCardInfo("Chandra, Dressed to Kill", 416, Rarity.MYTHIC, mage.cards.c.ChandraDressedToKill.class)); + cards.add(new SetCardInfo("Change of Fortune", 417, Rarity.RARE, mage.cards.c.ChangeOfFortune.class)); + cards.add(new SetCardInfo("Chapel Shieldgeist", 13, Rarity.UNCOMMON, mage.cards.c.ChapelShieldgeist.class)); + cards.add(new SetCardInfo("Chaplain of Alms", 13, Rarity.UNCOMMON, mage.cards.c.ChaplainOfAlms.class)); + cards.add(new SetCardInfo("Child of the Pack", 501, Rarity.UNCOMMON, mage.cards.c.ChildOfThePack.class)); + cards.add(new SetCardInfo("Chill of the Grave", 318, Rarity.COMMON, mage.cards.c.ChillOfTheGrave.class)); + cards.add(new SetCardInfo("Chilling Chronicle", 63, Rarity.UNCOMMON, mage.cards.c.ChillingChronicle.class)); + cards.add(new SetCardInfo("Cipherbound Spirit", 346, Rarity.UNCOMMON, mage.cards.c.CipherboundSpirit.class)); + cards.add(new SetCardInfo("Circle of Confinement", 274, Rarity.UNCOMMON, mage.cards.c.CircleOfConfinement.class)); + cards.add(new SetCardInfo("Clarion Cathars", 14, Rarity.COMMON, mage.cards.c.ClarionCathars.class)); + cards.add(new SetCardInfo("Clear Shot", 176, Rarity.UNCOMMON, mage.cards.c.ClearShot.class)); + cards.add(new SetCardInfo("Clever Distraction", 276, Rarity.UNCOMMON, mage.cards.c.CleverDistraction.class)); + cards.add(new SetCardInfo("Cloaked Cadet", 459, Rarity.UNCOMMON, mage.cards.c.CloakedCadet.class)); + cards.add(new SetCardInfo("Cobbled Lancer", 319, Rarity.UNCOMMON, mage.cards.c.CobbledLancer.class)); + cards.add(new SetCardInfo("Component Collector", 43, Rarity.COMMON, mage.cards.c.ComponentCollector.class)); + cards.add(new SetCardInfo("Concealing Curtains", 368, Rarity.RARE, mage.cards.c.ConcealingCurtains.class)); + cards.add(new SetCardInfo("Consider", 44, Rarity.COMMON, mage.cards.c.Consider.class)); + cards.add(new SetCardInfo("Consuming Blob", 177, Rarity.MYTHIC, mage.cards.c.ConsumingBlob.class)); + cards.add(new SetCardInfo("Consuming Tide", 320, Rarity.RARE, mage.cards.c.ConsumingTide.class)); + cards.add(new SetCardInfo("Contortionist Troupe", 178, Rarity.UNCOMMON, mage.cards.c.ContortionistTroupe.class)); + cards.add(new SetCardInfo("Corpse Cobble", 214, Rarity.UNCOMMON, mage.cards.c.CorpseCobble.class)); + cards.add(new SetCardInfo("Courier Bat", 369, Rarity.COMMON, mage.cards.c.CourierBat.class)); + cards.add(new SetCardInfo("Covert Cutpurse", 92, Rarity.UNCOMMON, mage.cards.c.CovertCutpurse.class)); + cards.add(new SetCardInfo("Covetous Castaway", 45, Rarity.UNCOMMON, mage.cards.c.CovetousCastaway.class)); + cards.add(new SetCardInfo("Covetous Geist", 92, Rarity.UNCOMMON, mage.cards.c.CovetousGeist.class)); + cards.add(new SetCardInfo("Cradle of Safety", 321, Rarity.COMMON, mage.cards.c.CradleOfSafety.class)); + cards.add(new SetCardInfo("Crawl from the Cellar", 93, Rarity.COMMON, mage.cards.c.CrawlFromTheCellar.class)); + cards.add(new SetCardInfo("Crawling Infestation", 460, Rarity.UNCOMMON, mage.cards.c.CrawlingInfestation.class)); + cards.add(new SetCardInfo("Creeping Inn", 264, Rarity.MYTHIC, mage.cards.c.CreepingInn.class)); + cards.add(new SetCardInfo("Creepy Puppeteer", 418, Rarity.RARE, mage.cards.c.CreepyPuppeteer.class)); + cards.add(new SetCardInfo("Croaking Counterpart", 215, Rarity.RARE, mage.cards.c.CroakingCounterpart.class)); + cards.add(new SetCardInfo("Crossroads Candleguide", 253, Rarity.COMMON, mage.cards.c.CrossroadsCandleguide.class)); + cards.add(new SetCardInfo("Cruel Witness", 322, Rarity.COMMON, mage.cards.c.CruelWitness.class)); + cards.add(new SetCardInfo("Crushing Canopy", 461, Rarity.COMMON, mage.cards.c.CrushingCanopy.class)); + cards.add(new SetCardInfo("Cultivator Colossus", 462, Rarity.MYTHIC, mage.cards.c.CultivatorColossus.class)); + cards.add(new SetCardInfo("Curse of Hospitality", 419, Rarity.RARE, mage.cards.c.CurseOfHospitality.class)); + cards.add(new SetCardInfo("Curse of Leeches", 94, Rarity.RARE, mage.cards.c.CurseOfLeeches.class)); + cards.add(new SetCardInfo("Curse of Shaken Faith", 134, Rarity.RARE, mage.cards.c.CurseOfShakenFaith.class)); + cards.add(new SetCardInfo("Curse of Silence", 15, Rarity.RARE, mage.cards.c.CurseOfSilence.class)); + cards.add(new SetCardInfo("Curse of Surveillance", 46, Rarity.RARE, mage.cards.c.CurseOfSurveillance.class)); + cards.add(new SetCardInfo("Dauntless Avenger", 4, Rarity.UNCOMMON, mage.cards.d.DauntlessAvenger.class)); + cards.add(new SetCardInfo("Dawnhart Disciple", 463, Rarity.COMMON, mage.cards.d.DawnhartDisciple.class)); + cards.add(new SetCardInfo("Dawnhart Geist", 275, Rarity.UNCOMMON, mage.cards.d.DawnhartGeist.class)); + cards.add(new SetCardInfo("Dawnhart Mentor", 179, Rarity.UNCOMMON, mage.cards.d.DawnhartMentor.class)); + cards.add(new SetCardInfo("Dawnhart Rejuvenator", 180, Rarity.COMMON, mage.cards.d.DawnhartRejuvenator.class)); + cards.add(new SetCardInfo("Dawnhart Wardens", 216, Rarity.UNCOMMON, mage.cards.d.DawnhartWardens.class)); + cards.add(new SetCardInfo("Daybreak Combatants", 420, Rarity.COMMON, mage.cards.d.DaybreakCombatants.class)); + cards.add(new SetCardInfo("Deadly Dancer", 408, Rarity.UNCOMMON, mage.cards.d.DeadlyDancer.class)); + cards.add(new SetCardInfo("Deathbonnet Hulk", 181, Rarity.UNCOMMON, mage.cards.d.DeathbonnetHulk.class)); + cards.add(new SetCardInfo("Deathbonnet Sprout", 181, Rarity.UNCOMMON, mage.cards.d.DeathbonnetSprout.class)); + cards.add(new SetCardInfo("Deathcap Glade", 528, Rarity.RARE, mage.cards.d.DeathcapGlade.class)); + cards.add(new SetCardInfo("Defend the Celestus", 182, Rarity.UNCOMMON, mage.cards.d.DefendTheCelestus.class)); + cards.add(new SetCardInfo("Defenestrate", 95, Rarity.COMMON, mage.cards.d.Defenestrate.class)); + cards.add(new SetCardInfo("Delver of Secrets", 47, Rarity.UNCOMMON, mage.cards.d.DelverOfSecrets.class)); + cards.add(new SetCardInfo("Demonic Bargain", 370, Rarity.RARE, mage.cards.d.DemonicBargain.class)); + cards.add(new SetCardInfo("Dennick, Pious Apparition", 217, Rarity.RARE, mage.cards.d.DennickPiousApparition.class)); + cards.add(new SetCardInfo("Dennick, Pious Apprentice", 217, Rarity.RARE, mage.cards.d.DennickPiousApprentice.class)); + cards.add(new SetCardInfo("Departed Soulkeeper", 218, Rarity.UNCOMMON, mage.cards.d.DepartedSoulkeeper.class)); + cards.add(new SetCardInfo("Depraved Harvester", 371, Rarity.COMMON, mage.cards.d.DepravedHarvester.class)); + cards.add(new SetCardInfo("Deserted Beach", 260, Rarity.RARE, mage.cards.d.DesertedBeach.class)); + cards.add(new SetCardInfo("Desperate Farmer", 371, Rarity.COMMON, mage.cards.d.DesperateFarmer.class)); + cards.add(new SetCardInfo("Devious Cover-Up", 48, Rarity.COMMON, mage.cards.d.DeviousCoverUp.class)); + cards.add(new SetCardInfo("Devoted Grafkeeper", 218, Rarity.UNCOMMON, mage.cards.d.DevotedGrafkeeper.class)); + cards.add(new SetCardInfo("Dig Up", 464, Rarity.RARE, mage.cards.d.DigUp.class)); + cards.add(new SetCardInfo("Dire-Strain Anarchist", 448, Rarity.MYTHIC, mage.cards.d.DireStrainAnarchist.class)); + cards.add(new SetCardInfo("Dire-Strain Brawler", 203, Rarity.COMMON, mage.cards.d.DireStrainBrawler.class)); + cards.add(new SetCardInfo("Dire-Strain Demolisher", 174, Rarity.UNCOMMON, mage.cards.d.DireStrainDemolisher.class)); + cards.add(new SetCardInfo("Dire-Strain Rampage", 219, Rarity.RARE, mage.cards.d.DireStrainRampage.class)); + cards.add(new SetCardInfo("Diregraf Horde", 96, Rarity.COMMON, mage.cards.d.DiregrafHorde.class)); + cards.add(new SetCardInfo("Diregraf Rebirth", 220, Rarity.UNCOMMON, mage.cards.d.DiregrafRebirth.class)); + cards.add(new SetCardInfo("Diregraf Scavenger", 372, Rarity.COMMON, mage.cards.d.DiregrafScavenger.class)); + cards.add(new SetCardInfo("Dissipate", 49, Rarity.UNCOMMON, mage.cards.d.Dissipate.class)); + cards.add(new SetCardInfo("Distracting Geist", 276, Rarity.UNCOMMON, mage.cards.d.DistractingGeist.class)); + cards.add(new SetCardInfo("Diver Skaab", 323, Rarity.UNCOMMON, mage.cards.d.DiverSkaab.class)); + cards.add(new SetCardInfo("Dollhouse of Horrors", 522, Rarity.RARE, mage.cards.d.DollhouseOfHorrors.class)); + cards.add(new SetCardInfo("Dominating Vampire", 421, Rarity.RARE, mage.cards.d.DominatingVampire.class)); + cards.add(new SetCardInfo("Doomed Dissenter", 373, Rarity.COMMON, mage.cards.d.DoomedDissenter.class)); + cards.add(new SetCardInfo("Dormant Grove", 465, Rarity.UNCOMMON, mage.cards.d.DormantGrove.class)); + cards.add(new SetCardInfo("Dorothea's Retribution", 502, Rarity.RARE, mage.cards.d.DorotheasRetribution.class)); + cards.add(new SetCardInfo("Dorothea, Vengeful Victim", 502, Rarity.RARE, mage.cards.d.DorotheaVengefulVictim.class)); + cards.add(new SetCardInfo("Dread Fugue", 374, Rarity.UNCOMMON, mage.cards.d.DreadFugue.class)); + cards.add(new SetCardInfo("Dreadfeast Demon", 375, Rarity.RARE, mage.cards.d.DreadfeastDemon.class)); + cards.add(new SetCardInfo("Dreadhound", 97, Rarity.UNCOMMON, mage.cards.d.Dreadhound.class)); + cards.add(new SetCardInfo("Dreadlight Monstrosity", 324, Rarity.COMMON, mage.cards.d.DreadlightMonstrosity.class)); + cards.add(new SetCardInfo("Dreamroot Cascade", 529, Rarity.RARE, mage.cards.d.DreamrootCascade.class)); + cards.add(new SetCardInfo("Dreamshackle Geist", 325, Rarity.RARE, mage.cards.d.DreamshackleGeist.class)); + cards.add(new SetCardInfo("Drogskol Armaments", 277, Rarity.COMMON, mage.cards.d.DrogskolArmaments.class)); + cards.add(new SetCardInfo("Drogskol Infantry", 277, Rarity.COMMON, mage.cards.d.DrogskolInfantry.class)); + cards.add(new SetCardInfo("Drownyard Amalgam", 50, Rarity.COMMON, mage.cards.d.DrownyardAmalgam.class)); + cards.add(new SetCardInfo("Dryad's Revival", 183, Rarity.UNCOMMON, mage.cards.d.DryadsRevival.class)); + cards.add(new SetCardInfo("Duel for Dominance", 184, Rarity.COMMON, mage.cards.d.DuelForDominance.class)); + cards.add(new SetCardInfo("Duelcraft Trainer", 16, Rarity.UNCOMMON, mage.cards.d.DuelcraftTrainer.class)); + cards.add(new SetCardInfo("Duress", 98, Rarity.COMMON, mage.cards.d.Duress.class)); + cards.add(new SetCardInfo("Dying to Serve", 376, Rarity.RARE, mage.cards.d.DyingToServe.class)); + cards.add(new SetCardInfo("Eaten Alive", 99, Rarity.COMMON, mage.cards.e.EatenAlive.class)); + cards.add(new SetCardInfo("Eccentric Farmer", 185, Rarity.COMMON, mage.cards.e.EccentricFarmer.class)); + cards.add(new SetCardInfo("Ecstatic Awakener", 100, Rarity.COMMON, mage.cards.e.EcstaticAwakener.class)); + cards.add(new SetCardInfo("Edgar Markov's Coffin", 503, Rarity.RARE, mage.cards.e.EdgarMarkovsCoffin.class)); + cards.add(new SetCardInfo("Edgar's Awakening", 377, Rarity.UNCOMMON, mage.cards.e.EdgarsAwakening.class)); + cards.add(new SetCardInfo("Edgar, Charmed Groom", 503, Rarity.RARE, mage.cards.e.EdgarCharmedGroom.class)); + cards.add(new SetCardInfo("Electric Revelation", 135, Rarity.COMMON, mage.cards.e.ElectricRevelation.class)); + cards.add(new SetCardInfo("Embodiment of Flame", 141, Rarity.UNCOMMON, mage.cards.e.EmbodimentOfFlame.class)); + cards.add(new SetCardInfo("End the Festivities", 422, Rarity.COMMON, mage.cards.e.EndTheFestivities.class)); + cards.add(new SetCardInfo("Endless Ranks of the Dead", 535, Rarity.RARE, mage.cards.e.EndlessRanksOfTheDead.class)); + cards.add(new SetCardInfo("Enduring Angel", 17, Rarity.MYTHIC, mage.cards.e.EnduringAngel.class)); + cards.add(new SetCardInfo("Eruth, Tormented Prophet", 504, Rarity.RARE, mage.cards.e.EruthTormentedProphet.class)); + cards.add(new SetCardInfo("Estwald Shieldbasher", 278, Rarity.COMMON, mage.cards.e.EstwaldShieldbasher.class)); + cards.add(new SetCardInfo("Evolving Wilds", 261, Rarity.COMMON, mage.cards.e.EvolvingWilds.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Evolving Wilds", 530, Rarity.COMMON, mage.cards.e.EvolvingWilds.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fading Hope", 51, Rarity.UNCOMMON, mage.cards.f.FadingHope.class)); + cards.add(new SetCardInfo("Faithbound Judge", 279, Rarity.MYTHIC, mage.cards.f.FaithboundJudge.class)); + cards.add(new SetCardInfo("Faithful Mending", 221, Rarity.UNCOMMON, mage.cards.f.FaithfulMending.class)); + cards.add(new SetCardInfo("Falcon Abomination", 52, Rarity.COMMON, mage.cards.f.FalconAbomination.class)); + cards.add(new SetCardInfo("Falkenrath Celebrants", 423, Rarity.COMMON, mage.cards.f.FalkenrathCelebrants.class)); + cards.add(new SetCardInfo("Falkenrath Forebear", 378, Rarity.RARE, mage.cards.f.FalkenrathForebear.class)); + cards.add(new SetCardInfo("Falkenrath Perforator", 136, Rarity.COMMON, mage.cards.f.FalkenrathPerforator.class)); + cards.add(new SetCardInfo("Falkenrath Pit Fighter", 137, Rarity.RARE, mage.cards.f.FalkenrathPitFighter.class)); + cards.add(new SetCardInfo("Famished Foragers", 138, Rarity.COMMON, mage.cards.f.FamishedForagers.class)); + cards.add(new SetCardInfo("Fangblade Brigand", 139, Rarity.UNCOMMON, mage.cards.f.FangbladeBrigand.class)); + cards.add(new SetCardInfo("Fangblade Eviscerator", 139, Rarity.UNCOMMON, mage.cards.f.FangbladeEviscerator.class)); + cards.add(new SetCardInfo("Fateful Absence", 18, Rarity.RARE, mage.cards.f.FatefulAbsence.class)); + cards.add(new SetCardInfo("Fear of Death", 326, Rarity.COMMON, mage.cards.f.FearOfDeath.class)); + cards.add(new SetCardInfo("Fearful Villager", 424, Rarity.COMMON, mage.cards.f.FearfulVillager.class)); + cards.add(new SetCardInfo("Fearsome Werewolf", 424, Rarity.COMMON, mage.cards.f.FearsomeWerewolf.class)); + cards.add(new SetCardInfo("Fell Stinger", 379, Rarity.UNCOMMON, mage.cards.f.FellStinger.class)); + cards.add(new SetCardInfo("Festival Crasher", 140, Rarity.COMMON, mage.cards.f.FestivalCrasher.class)); + cards.add(new SetCardInfo("Field of Ruin", 262, Rarity.UNCOMMON, mage.cards.f.FieldOfRuin.class)); + cards.add(new SetCardInfo("Fierce Retribution", 280, Rarity.COMMON, mage.cards.f.FierceRetribution.class)); + cards.add(new SetCardInfo("Firmament Sage", 53, Rarity.UNCOMMON, mage.cards.f.FirmamentSage.class)); + cards.add(new SetCardInfo("Flame Channeler", 141, Rarity.UNCOMMON, mage.cards.f.FlameChanneler.class)); + cards.add(new SetCardInfo("Flame-Blessed Bolt", 425, Rarity.COMMON, mage.cards.f.FlameBlessedBolt.class)); + cards.add(new SetCardInfo("Flare of Faith", 19, Rarity.COMMON, mage.cards.f.FlareOfFaith.class)); + cards.add(new SetCardInfo("Fleeting Spirit", 281, Rarity.UNCOMMON, mage.cards.f.FleetingSpirit.class)); + cards.add(new SetCardInfo("Fleshtaker", 222, Rarity.UNCOMMON, mage.cards.f.Fleshtaker.class)); + cards.add(new SetCardInfo("Flip the Switch", 54, Rarity.COMMON, mage.cards.f.FlipTheSwitch.class)); + cards.add(new SetCardInfo("Florian, Voldaren Scion", 223, Rarity.RARE, mage.cards.f.FlorianVoldarenScion.class)); + cards.add(new SetCardInfo("Flourishing Hunter", 466, Rarity.COMMON, mage.cards.f.FlourishingHunter.class)); + cards.add(new SetCardInfo("Foreboding Statue", 523, Rarity.UNCOMMON, mage.cards.f.ForebodingStatue.class)); + cards.add(new SetCardInfo("Forsaken Thresher", 523, Rarity.UNCOMMON, mage.cards.f.ForsakenThresher.class)); + cards.add(new SetCardInfo("Foul Play", 101, Rarity.UNCOMMON, mage.cards.f.FoulPlay.class)); + cards.add(new SetCardInfo("Frenzied Devils", 426, Rarity.UNCOMMON, mage.cards.f.FrenziedDevils.class)); + cards.add(new SetCardInfo("Frenzied Trapbreaker", 190, Rarity.UNCOMMON, mage.cards.f.FrenziedTrapbreaker.class)); + cards.add(new SetCardInfo("Galedrifter", 55, Rarity.COMMON, mage.cards.g.Galedrifter.class)); + cards.add(new SetCardInfo("Galvanic Iteration", 224, Rarity.RARE, mage.cards.g.GalvanicIteration.class)); + cards.add(new SetCardInfo("Gavony Dawnguard", 20, Rarity.UNCOMMON, mage.cards.g.GavonyDawnguard.class)); + cards.add(new SetCardInfo("Gavony Silversmith", 21, Rarity.COMMON, mage.cards.g.GavonySilversmith.class)); + cards.add(new SetCardInfo("Gavony Trapper", 22, Rarity.COMMON, mage.cards.g.GavonyTrapper.class)); + cards.add(new SetCardInfo("Geistflame Reservoir", 142, Rarity.RARE, mage.cards.g.GeistflameReservoir.class)); + cards.add(new SetCardInfo("Geistlight Snare", 327, Rarity.UNCOMMON, mage.cards.g.GeistlightSnare.class)); + cards.add(new SetCardInfo("Geistwave", 56, Rarity.COMMON, mage.cards.g.Geistwave.class)); + cards.add(new SetCardInfo("Generous Soul", 3, Rarity.UNCOMMON, mage.cards.g.GenerousSoul.class)); + cards.add(new SetCardInfo("Geralf, Visionary Stitcher", 328, Rarity.RARE, mage.cards.g.GeralfVisionaryStitcher.class)); + cards.add(new SetCardInfo("Ghastly Mimicry", 335, Rarity.RARE, mage.cards.g.GhastlyMimicry.class)); + cards.add(new SetCardInfo("Ghostly Castigator", 45, Rarity.UNCOMMON, mage.cards.g.GhostlyCastigator.class)); + cards.add(new SetCardInfo("Ghoulcaller's Harvest", 225, Rarity.RARE, mage.cards.g.GhoulcallersHarvest.class)); + cards.add(new SetCardInfo("Ghoulish Procession", 102, Rarity.UNCOMMON, mage.cards.g.GhoulishProcession.class)); + cards.add(new SetCardInfo("Gift of Fangs", 380, Rarity.COMMON, mage.cards.g.GiftOfFangs.class)); + cards.add(new SetCardInfo("Gisa, Glorious Resurrector", 103, Rarity.RARE, mage.cards.g.GisaGloriousResurrector.class)); + cards.add(new SetCardInfo("Glorious Sunrise", 467, Rarity.RARE, mage.cards.g.GloriousSunrise.class)); + cards.add(new SetCardInfo("Gluttonous Guest", 381, Rarity.COMMON, mage.cards.g.GluttonousGuest.class)); + cards.add(new SetCardInfo("Gnarled Grovestrider", 465, Rarity.UNCOMMON, mage.cards.g.GnarledGrovestrider.class)); + cards.add(new SetCardInfo("Graf Reaver", 382, Rarity.RARE, mage.cards.g.GrafReaver.class)); + cards.add(new SetCardInfo("Grafted Identity", 57, Rarity.RARE, mage.cards.g.GraftedIdentity.class)); + cards.add(new SetCardInfo("Graveyard Glutton", 104, Rarity.RARE, mage.cards.g.GraveyardGlutton.class)); + cards.add(new SetCardInfo("Graveyard Trespasser", 104, Rarity.RARE, mage.cards.g.GraveyardTrespasser.class)); + cards.add(new SetCardInfo("Grisly Ritual", 383, Rarity.COMMON, mage.cards.g.GrislyRitual.class)); + cards.add(new SetCardInfo("Grizzly Ghoul", 226, Rarity.UNCOMMON, mage.cards.g.GrizzlyGhoul.class)); + cards.add(new SetCardInfo("Grolnok, the Omnivore", 505, Rarity.RARE, mage.cards.g.GrolnokTheOmnivore.class)); + cards.add(new SetCardInfo("Groom's Finery", 384, Rarity.UNCOMMON, mage.cards.g.GroomsFinery.class)); + cards.add(new SetCardInfo("Gryff Rider", 282, Rarity.COMMON, mage.cards.g.GryffRider.class)); + cards.add(new SetCardInfo("Gryffwing Cavalry", 283, Rarity.UNCOMMON, mage.cards.g.GryffwingCavalry.class)); + cards.add(new SetCardInfo("Gutter Shortcut", 329, Rarity.UNCOMMON, mage.cards.g.GutterShortcut.class)); + cards.add(new SetCardInfo("Gutter Skulker", 329, Rarity.UNCOMMON, mage.cards.g.GutterSkulker.class)); + cards.add(new SetCardInfo("Halana and Alena, Partners", 506, Rarity.RARE, mage.cards.h.HalanaAndAlenaPartners.class)); + cards.add(new SetCardInfo("Hallowed Haunting", 284, Rarity.MYTHIC, mage.cards.h.HallowedHaunting.class)); + cards.add(new SetCardInfo("Hallowed Respite", 227, Rarity.RARE, mage.cards.h.HallowedRespite.class)); + cards.add(new SetCardInfo("Hamlet Vanguard", 468, Rarity.RARE, mage.cards.h.HamletVanguard.class)); + cards.add(new SetCardInfo("Harvesttide Assailant", 143, Rarity.COMMON, mage.cards.h.HarvesttideAssailant.class)); + cards.add(new SetCardInfo("Harvesttide Infiltrator", 143, Rarity.COMMON, mage.cards.h.HarvesttideInfiltrator.class)); + cards.add(new SetCardInfo("Harvesttide Sentry", 186, Rarity.COMMON, mage.cards.h.HarvesttideSentry.class)); + cards.add(new SetCardInfo("Hauken's Insight", 332, Rarity.MYTHIC, mage.cards.h.HaukensInsight.class)); + cards.add(new SetCardInfo("Haunted Ridge", 263, Rarity.RARE, mage.cards.h.HauntedRidge.class)); + cards.add(new SetCardInfo("Headless Rider", 385, Rarity.RARE, mage.cards.h.HeadlessRider.class)); + cards.add(new SetCardInfo("Hedgewitch's Mask", 23, Rarity.COMMON, mage.cards.h.HedgewitchsMask.class)); + cards.add(new SetCardInfo("Heirloom Mirror", 105, Rarity.UNCOMMON, mage.cards.h.HeirloomMirror.class)); + cards.add(new SetCardInfo("Henrika Domnathi", 386, Rarity.MYTHIC, mage.cards.h.HenrikaDomnathi.class)); + cards.add(new SetCardInfo("Henrika, Infernal Seer", 386, Rarity.MYTHIC, mage.cards.h.HenrikaInfernalSeer.class)); + cards.add(new SetCardInfo("Hero's Downfall", 387, Rarity.UNCOMMON, mage.cards.h.HerosDownfall.class)); + cards.add(new SetCardInfo("Heron of Hope", 285, Rarity.COMMON, mage.cards.h.HeronOfHope.class)); + cards.add(new SetCardInfo("Heron-Blessed Geist", 286, Rarity.COMMON, mage.cards.h.HeronBlessedGeist.class)); + cards.add(new SetCardInfo("Hiveheart Shaman", 469, Rarity.RARE, mage.cards.h.HiveheartShaman.class)); + cards.add(new SetCardInfo("Hobbling Zombie", 106, Rarity.COMMON, mage.cards.h.HobblingZombie.class)); + cards.add(new SetCardInfo("Hollowhenge Huntmaster", 454, Rarity.MYTHIC, mage.cards.h.HollowhengeHuntmaster.class)); + cards.add(new SetCardInfo("Homestead Courage", 24, Rarity.COMMON, mage.cards.h.HomesteadCourage.class)); + cards.add(new SetCardInfo("Honeymoon Hearse", 427, Rarity.UNCOMMON, mage.cards.h.HoneymoonHearse.class)); + cards.add(new SetCardInfo("Honored Heirloom", 524, Rarity.COMMON, mage.cards.h.HonoredHeirloom.class)); + cards.add(new SetCardInfo("Hook-Haunt Drifter", 42, Rarity.COMMON, mage.cards.h.HookHauntDrifter.class)); + cards.add(new SetCardInfo("Hookhand Mariner", 470, Rarity.COMMON, mage.cards.h.HookhandMariner.class)); + cards.add(new SetCardInfo("Hopeful Initiate", 287, Rarity.RARE, mage.cards.h.HopefulInitiate.class)); + cards.add(new SetCardInfo("Hostile Hostel", 264, Rarity.MYTHIC, mage.cards.h.HostileHostel.class)); + cards.add(new SetCardInfo("Hound Tamer", 187, Rarity.UNCOMMON, mage.cards.h.HoundTamer.class)); + cards.add(new SetCardInfo("Howl of the Hunt", 188, Rarity.COMMON, mage.cards.h.HowlOfTheHunt.class)); + cards.add(new SetCardInfo("Howling Moon", 471, Rarity.RARE, mage.cards.h.HowlingMoon.class)); + cards.add(new SetCardInfo("Howlpack Avenger", 429, Rarity.RARE, mage.cards.h.HowlpackAvenger.class)); + cards.add(new SetCardInfo("Howlpack Piper", 472, Rarity.RARE, mage.cards.h.HowlpackPiper.class)); + cards.add(new SetCardInfo("Hullbreaker Horror", 330, Rarity.RARE, mage.cards.h.HullbreakerHorror.class)); + cards.add(new SetCardInfo("Hungry Ridgewolf", 428, Rarity.COMMON, mage.cards.h.HungryRidgewolf.class)); + cards.add(new SetCardInfo("Hungry for More", 228, Rarity.UNCOMMON, mage.cards.h.HungryForMore.class)); + cards.add(new SetCardInfo("Ill-Tempered Loner", 429, Rarity.RARE, mage.cards.i.IllTemperedLoner.class)); + cards.add(new SetCardInfo("Immolation", 144, Rarity.COMMON, mage.cards.i.Immolation.class)); + cards.add(new SetCardInfo("Infernal Grasp", 107, Rarity.UNCOMMON, mage.cards.i.InfernalGrasp.class)); + cards.add(new SetCardInfo("Infestation Expert", 473, Rarity.UNCOMMON, mage.cards.i.InfestationExpert.class)); + cards.add(new SetCardInfo("Infested Werewolf", 473, Rarity.UNCOMMON, mage.cards.i.InfestedWerewolf.class)); + cards.add(new SetCardInfo("Inherited Fiend", 105, Rarity.UNCOMMON, mage.cards.i.InheritedFiend.class)); + cards.add(new SetCardInfo("Innocent Traveler", 388, Rarity.UNCOMMON, mage.cards.i.InnocentTraveler.class)); + cards.add(new SetCardInfo("Insectile Aberration", 47, Rarity.UNCOMMON, mage.cards.i.InsectileAberration.class)); + cards.add(new SetCardInfo("Inspired Idea", 331, Rarity.RARE, mage.cards.i.InspiredIdea.class)); + cards.add(new SetCardInfo("Into the Night", 430, Rarity.UNCOMMON, mage.cards.i.IntoTheNight.class)); + cards.add(new SetCardInfo("Intrepid Adversary", 25, Rarity.MYTHIC, mage.cards.i.IntrepidAdversary.class)); + cards.add(new SetCardInfo("Investigator's Journal", 525, Rarity.RARE, mage.cards.i.InvestigatorsJournal.class)); + cards.add(new SetCardInfo("Jack-o'-Lantern", 254, Rarity.COMMON, mage.cards.j.JackOLantern.class)); + cards.add(new SetCardInfo("Jacob Hauken, Inspector", 332, Rarity.MYTHIC, mage.cards.j.JacobHaukenInspector.class)); + cards.add(new SetCardInfo("Jadar, Ghoulcaller of Nephalia", 108, Rarity.RARE, mage.cards.j.JadarGhoulcallerOfNephalia.class)); + cards.add(new SetCardInfo("Jerren, Corrupted Bishop", 109, Rarity.MYTHIC, mage.cards.j.JerrenCorruptedBishop.class)); + cards.add(new SetCardInfo("Join the Dance", 229, Rarity.UNCOMMON, mage.cards.j.JoinTheDance.class)); + cards.add(new SetCardInfo("Katilda's Rising Dawn", 288, Rarity.RARE, mage.cards.k.KatildasRisingDawn.class)); + cards.add(new SetCardInfo("Katilda, Dawnhart Martyr", 288, Rarity.RARE, mage.cards.k.KatildaDawnhartMartyr.class)); + cards.add(new SetCardInfo("Katilda, Dawnhart Prime", 230, Rarity.RARE, mage.cards.k.KatildaDawnhartPrime.class)); + cards.add(new SetCardInfo("Kaya, Geist Hunter", 507, Rarity.MYTHIC, mage.cards.k.KayaGeistHunter.class)); + cards.add(new SetCardInfo("Kessig Flamebreather", 431, Rarity.COMMON, mage.cards.k.KessigFlamebreather.class)); + cards.add(new SetCardInfo("Kessig Naturalist", 231, Rarity.UNCOMMON, mage.cards.k.KessigNaturalist.class)); + cards.add(new SetCardInfo("Kessig Wolfrider", 432, Rarity.RARE, mage.cards.k.KessigWolfrider.class)); + cards.add(new SetCardInfo("Kindly Ancestor", 289, Rarity.COMMON, mage.cards.k.KindlyAncestor.class)); + cards.add(new SetCardInfo("Krothuss, Lord of the Deep", 513, Rarity.RARE, mage.cards.k.KrothussLordOfTheDeep.class)); + cards.add(new SetCardInfo("Lacerate Flesh", 433, Rarity.COMMON, mage.cards.l.LacerateFlesh.class)); + cards.add(new SetCardInfo("Laid to Rest", 474, Rarity.UNCOMMON, mage.cards.l.LaidToRest.class)); + cards.add(new SetCardInfo("Lambholt Harrier", 145, Rarity.COMMON, mage.cards.l.LambholtHarrier.class)); + cards.add(new SetCardInfo("Lambholt Raconteur", 434, Rarity.UNCOMMON, mage.cards.l.LambholtRaconteur.class)); + cards.add(new SetCardInfo("Lambholt Ravager", 434, Rarity.UNCOMMON, mage.cards.l.LambholtRavager.class)); + cards.add(new SetCardInfo("Lantern Bearer", 333, Rarity.COMMON, mage.cards.l.LanternBearer.class)); + cards.add(new SetCardInfo("Lantern Flare", 290, Rarity.RARE, mage.cards.l.LanternFlare.class)); + cards.add(new SetCardInfo("Lantern of the Lost", 526, Rarity.UNCOMMON, mage.cards.l.LanternOfTheLost.class)); + cards.add(new SetCardInfo("Lanterns' Lift", 333, Rarity.COMMON, mage.cards.l.LanternsLift.class)); + cards.add(new SetCardInfo("Larder Zombie", 58, Rarity.COMMON, mage.cards.l.LarderZombie.class)); + cards.add(new SetCardInfo("Leeching Lurker", 94, Rarity.RARE, mage.cards.l.LeechingLurker.class)); + cards.add(new SetCardInfo("Lier, Disciple of the Drowned", 59, Rarity.MYTHIC, mage.cards.l.LierDiscipleOfTheDrowned.class)); + cards.add(new SetCardInfo("Liesa, Forgotten Archangel", 232, Rarity.RARE, mage.cards.l.LiesaForgottenArchangel.class)); + cards.add(new SetCardInfo("Light Up the Night", 146, Rarity.RARE, mage.cards.l.LightUpTheNight.class)); + cards.add(new SetCardInfo("Lightning Wolf", 435, Rarity.COMMON, mage.cards.l.LightningWolf.class)); + cards.add(new SetCardInfo("Locked in the Cemetery", 60, Rarity.COMMON, mage.cards.l.LockedInTheCemetery.class)); + cards.add(new SetCardInfo("Lord of the Forsaken", 110, Rarity.MYTHIC, mage.cards.l.LordOfTheForsaken.class)); + cards.add(new SetCardInfo("Lord of the Ulvenwald", 231, Rarity.UNCOMMON, mage.cards.l.LordOfTheUlvenwald.class)); + cards.add(new SetCardInfo("Loyal Gryff", 26, Rarity.UNCOMMON, mage.cards.l.LoyalGryff.class)); + cards.add(new SetCardInfo("Ludevic, Necrogenius", 233, Rarity.RARE, mage.cards.l.LudevicNecrogenius.class)); + cards.add(new SetCardInfo("Luminous Phantom", 27, Rarity.COMMON, mage.cards.l.LuminousPhantom.class)); + cards.add(new SetCardInfo("Lunar Frenzy", 147, Rarity.UNCOMMON, mage.cards.l.LunarFrenzy.class)); + cards.add(new SetCardInfo("Lunar Rejection", 334, Rarity.UNCOMMON, mage.cards.l.LunarRejection.class)); + cards.add(new SetCardInfo("Lunarch Veteran", 27, Rarity.COMMON, mage.cards.l.LunarchVeteran.class)); + cards.add(new SetCardInfo("Magma Pummeler", 436, Rarity.UNCOMMON, mage.cards.m.MagmaPummeler.class)); + cards.add(new SetCardInfo("Malevolent Hermit", 61, Rarity.RARE, mage.cards.m.MalevolentHermit.class)); + cards.add(new SetCardInfo("Malicious Invader", 388, Rarity.UNCOMMON, mage.cards.m.MaliciousInvader.class)); + cards.add(new SetCardInfo("Manaform Hellkite", 437, Rarity.MYTHIC, mage.cards.m.ManaformHellkite.class)); + cards.add(new SetCardInfo("Markov Purifier", 508, Rarity.UNCOMMON, mage.cards.m.MarkovPurifier.class)); + cards.add(new SetCardInfo("Markov Retribution", 438, Rarity.UNCOMMON, mage.cards.m.MarkovRetribution.class)); + cards.add(new SetCardInfo("Markov Waltzer", 509, Rarity.UNCOMMON, mage.cards.m.MarkovWaltzer.class)); + cards.add(new SetCardInfo("Mask of Griselbrand", 111, Rarity.RARE, mage.cards.m.MaskOfGriselbrand.class)); + cards.add(new SetCardInfo("Massive Might", 475, Rarity.COMMON, mage.cards.m.MassiveMight.class)); + cards.add(new SetCardInfo("Memory Deluge", 62, Rarity.RARE, mage.cards.m.MemoryDeluge.class)); + cards.add(new SetCardInfo("Might of the Old Ways", 189, Rarity.COMMON, mage.cards.m.MightOfTheOldWays.class)); + cards.add(new SetCardInfo("Militia Rallier", 291, Rarity.COMMON, mage.cards.m.MilitiaRallier.class)); + cards.add(new SetCardInfo("Mindleech Ghoul", 389, Rarity.COMMON, mage.cards.m.MindleechGhoul.class)); + cards.add(new SetCardInfo("Mirrorhall Mimic", 335, Rarity.RARE, mage.cards.m.MirrorhallMimic.class)); + cards.add(new SetCardInfo("Mischievous Catgeist", 336, Rarity.UNCOMMON, mage.cards.m.MischievousCatgeist.class)); + cards.add(new SetCardInfo("Moldgraf Millipede", 476, Rarity.COMMON, mage.cards.m.MoldgrafMillipede.class)); + cards.add(new SetCardInfo("Moonlit Ambusher", 479, Rarity.UNCOMMON, mage.cards.m.MoonlitAmbusher.class)); + cards.add(new SetCardInfo("Moonrage Brute", 7, Rarity.RARE, mage.cards.m.MoonrageBrute.class)); + cards.add(new SetCardInfo("Moonrager's Slash", 148, Rarity.COMMON, mage.cards.m.MoonragersSlash.class)); + cards.add(new SetCardInfo("Moonsilver Key", 255, Rarity.UNCOMMON, mage.cards.m.MoonsilverKey.class)); + cards.add(new SetCardInfo("Moonveil Regent", 149, Rarity.MYTHIC, mage.cards.m.MoonveilRegent.class)); + cards.add(new SetCardInfo("Morbid Opportunist", 113, Rarity.UNCOMMON, mage.cards.m.MorbidOpportunist.class)); + cards.add(new SetCardInfo("Morkrut Behemoth", 114, Rarity.COMMON, mage.cards.m.MorkrutBehemoth.class)); + cards.add(new SetCardInfo("Morning Apparition", 28, Rarity.COMMON, mage.cards.m.MorningApparition.class)); + cards.add(new SetCardInfo("Mounted Dreadknight", 150, Rarity.COMMON, mage.cards.m.MountedDreadknight.class)); + cards.add(new SetCardInfo("Mourning Patrol", 28, Rarity.COMMON, mage.cards.m.MourningPatrol.class)); + cards.add(new SetCardInfo("Mulch", 477, Rarity.COMMON, mage.cards.m.Mulch.class)); + cards.add(new SetCardInfo("Mysterious Tome", 63, Rarity.UNCOMMON, mage.cards.m.MysteriousTome.class)); + cards.add(new SetCardInfo("Mystic Monstrosity", 256, Rarity.UNCOMMON, mage.cards.m.MysticMonstrosity.class)); + cards.add(new SetCardInfo("Mystic Skull", 256, Rarity.UNCOMMON, mage.cards.m.MysticSkull.class)); + cards.add(new SetCardInfo("Nature's Embrace", 478, Rarity.COMMON, mage.cards.n.NaturesEmbrace.class)); + cards.add(new SetCardInfo("Nebelgast Beguiler", 292, Rarity.COMMON, mage.cards.n.NebelgastBeguiler.class)); + cards.add(new SetCardInfo("Nebelgast Intruder", 64, Rarity.UNCOMMON, mage.cards.n.NebelgastIntruder.class)); + cards.add(new SetCardInfo("Necroduality", 337, Rarity.MYTHIC, mage.cards.n.Necroduality.class)); + cards.add(new SetCardInfo("Necrosynthesis", 115, Rarity.UNCOMMON, mage.cards.n.Necrosynthesis.class)); + cards.add(new SetCardInfo("Neonate's Rush", 151, Rarity.COMMON, mage.cards.n.NeonatesRush.class)); + cards.add(new SetCardInfo("No Way Out", 116, Rarity.COMMON, mage.cards.n.NoWayOut.class)); + cards.add(new SetCardInfo("Novice Occultist", 117, Rarity.COMMON, mage.cards.n.NoviceOccultist.class)); + cards.add(new SetCardInfo("Nurturing Presence", 293, Rarity.COMMON, mage.cards.n.NurturingPresence.class)); + cards.add(new SetCardInfo("Oakshade Stalker", 479, Rarity.UNCOMMON, mage.cards.o.OakshadeStalker.class)); + cards.add(new SetCardInfo("Obsessive Astronomer", 152, Rarity.UNCOMMON, mage.cards.o.ObsessiveAstronomer.class)); + cards.add(new SetCardInfo("Odious Witch", 394, Rarity.COMMON, mage.cards.o.OdiousWitch.class)); + cards.add(new SetCardInfo("Odric's Outrider", 29, Rarity.UNCOMMON, mage.cards.o.OdricsOutrider.class)); + cards.add(new SetCardInfo("Odric, Blood-Cursed", 510, Rarity.RARE, mage.cards.o.OdricBloodCursed.class)); + cards.add(new SetCardInfo("Olag, Ludevic's Hubris", 233, Rarity.RARE, mage.cards.o.OlagLudevicsHubris.class)); + cards.add(new SetCardInfo("Old Rutstein", 511, Rarity.RARE, mage.cards.o.OldRutstein.class)); + cards.add(new SetCardInfo("Old Stickfingers", 234, Rarity.RARE, mage.cards.o.OldStickfingers.class)); + cards.add(new SetCardInfo("Olivia's Attendants", 439, Rarity.RARE, mage.cards.o.OliviasAttendants.class)); + cards.add(new SetCardInfo("Olivia's Midnight Ambush", 118, Rarity.COMMON, mage.cards.o.OliviasMidnightAmbush.class)); + cards.add(new SetCardInfo("Olivia, Crimson Bride", 512, Rarity.MYTHIC, mage.cards.o.OliviaCrimsonBride.class)); + cards.add(new SetCardInfo("Ollenbock Escort", 294, Rarity.UNCOMMON, mage.cards.o.OllenbockEscort.class)); + cards.add(new SetCardInfo("Ominous Roost", 65, Rarity.UNCOMMON, mage.cards.o.OminousRoost.class)); + cards.add(new SetCardInfo("Organ Hoarder", 66, Rarity.COMMON, mage.cards.o.OrganHoarder.class)); + cards.add(new SetCardInfo("Ormendahl, the Corrupter", 109, Rarity.MYTHIC, mage.cards.o.OrmendahlTheCorrupter.class)); + cards.add(new SetCardInfo("Otherworldly Gaze", 67, Rarity.COMMON, mage.cards.o.OtherworldlyGaze.class)); + cards.add(new SetCardInfo("Outland Liberator", 190, Rarity.UNCOMMON, mage.cards.o.OutlandLiberator.class)); + cards.add(new SetCardInfo("Overcharged Amalgam", 338, Rarity.RARE, mage.cards.o.OverchargedAmalgam.class)); + cards.add(new SetCardInfo("Overgrown Farmland", 265, Rarity.RARE, mage.cards.o.OvergrownFarmland.class)); + cards.add(new SetCardInfo("Overwhelmed Archivist", 68, Rarity.UNCOMMON, mage.cards.o.OverwhelmedArchivist.class)); + cards.add(new SetCardInfo("Pack's Betrayal", 153, Rarity.COMMON, mage.cards.p.PacksBetrayal.class)); + cards.add(new SetCardInfo("Packsong Pup", 480, Rarity.UNCOMMON, mage.cards.p.PacksongPup.class)); + cards.add(new SetCardInfo("Panicked Bystander", 295, Rarity.UNCOMMON, mage.cards.p.PanickedBystander.class)); + cards.add(new SetCardInfo("Parasitic Grasp", 390, Rarity.UNCOMMON, mage.cards.p.ParasiticGrasp.class)); + cards.add(new SetCardInfo("Parish-Blade Trainee", 296, Rarity.COMMON, mage.cards.p.ParishBladeTrainee.class)); + cards.add(new SetCardInfo("Patchwork Crawler", 339, Rarity.RARE, mage.cards.p.PatchworkCrawler.class)); + cards.add(new SetCardInfo("Path of Peril", 391, Rarity.RARE, mage.cards.p.PathOfPeril.class)); + cards.add(new SetCardInfo("Path to the Festival", 191, Rarity.COMMON, mage.cards.p.PathToTheFestival.class)); + cards.add(new SetCardInfo("Patrician Geist", 69, Rarity.RARE, mage.cards.p.PatricianGeist.class)); + cards.add(new SetCardInfo("Persistent Specimen", 392, Rarity.COMMON, mage.cards.p.PersistentSpecimen.class)); + cards.add(new SetCardInfo("Pestilent Wolf", 192, Rarity.COMMON, mage.cards.p.PestilentWolf.class)); + cards.add(new SetCardInfo("Phantom Carriage", 70, Rarity.UNCOMMON, mage.cards.p.PhantomCarriage.class)); + cards.add(new SetCardInfo("Piercing Light", 297, Rarity.COMMON, mage.cards.p.PiercingLight.class)); + cards.add(new SetCardInfo("Pithing Needle", 257, Rarity.RARE, mage.cards.p.PithingNeedle.class)); + cards.add(new SetCardInfo("Play with Fire", 154, Rarity.UNCOMMON, mage.cards.p.PlayWithFire.class)); + cards.add(new SetCardInfo("Plummet", 193, Rarity.COMMON, mage.cards.p.Plummet.class)); + cards.add(new SetCardInfo("Pointed Discussion", 393, Rarity.COMMON, mage.cards.p.PointedDiscussion.class)); + cards.add(new SetCardInfo("Poppet Factory", 71, Rarity.MYTHIC, mage.cards.p.PoppetFactory.class)); + cards.add(new SetCardInfo("Poppet Stitcher", 71, Rarity.MYTHIC, mage.cards.p.PoppetStitcher.class)); + cards.add(new SetCardInfo("Primal Adversary", 194, Rarity.MYTHIC, mage.cards.p.PrimalAdversary.class)); + cards.add(new SetCardInfo("Purifying Dragon", 155, Rarity.UNCOMMON, mage.cards.p.PurifyingDragon.class)); + cards.add(new SetCardInfo("Pyre Spawn", 440, Rarity.COMMON, mage.cards.p.PyreSpawn.class)); + cards.add(new SetCardInfo("Radiant Grace", 298, Rarity.UNCOMMON, mage.cards.r.RadiantGrace.class)); + cards.add(new SetCardInfo("Radiant Restraints", 298, Rarity.UNCOMMON, mage.cards.r.RadiantRestraints.class)); + cards.add(new SetCardInfo("Ragged Recluse", 394, Rarity.COMMON, mage.cards.r.RaggedRecluse.class)); + cards.add(new SetCardInfo("Raze the Effigy", 156, Rarity.COMMON, mage.cards.r.RazeTheEffigy.class)); + cards.add(new SetCardInfo("Reckless Impulse", 441, Rarity.COMMON, mage.cards.r.RecklessImpulse.class)); + cards.add(new SetCardInfo("Reckless Stormseeker", 157, Rarity.RARE, mage.cards.r.RecklessStormseeker.class)); + cards.add(new SetCardInfo("Reclusive Taxidermist", 481, Rarity.UNCOMMON, mage.cards.r.ReclusiveTaxidermist.class)); + cards.add(new SetCardInfo("Rem Karolus, Stalwart Slayer", 235, Rarity.RARE, mage.cards.r.RemKarolusStalwartSlayer.class)); + cards.add(new SetCardInfo("Rending Flame", 442, Rarity.UNCOMMON, mage.cards.r.RendingFlame.class)); + cards.add(new SetCardInfo("Repository Skaab", 340, Rarity.COMMON, mage.cards.r.RepositorySkaab.class)); + cards.add(new SetCardInfo("Resistance Squad", 299, Rarity.UNCOMMON, mage.cards.r.ResistanceSquad.class)); + cards.add(new SetCardInfo("Restless Bloodseeker", 395, Rarity.UNCOMMON, mage.cards.r.RestlessBloodseeker.class)); + cards.add(new SetCardInfo("Retrieve", 482, Rarity.UNCOMMON, mage.cards.r.Retrieve.class)); + cards.add(new SetCardInfo("Return to Nature", 195, Rarity.COMMON, mage.cards.r.ReturnToNature.class)); + cards.add(new SetCardInfo("Revealing Eye", 368, Rarity.RARE, mage.cards.r.RevealingEye.class)); + cards.add(new SetCardInfo("Revenge of the Drowned", 72, Rarity.COMMON, mage.cards.r.RevengeOfTheDrowned.class)); + cards.add(new SetCardInfo("Riphook Raider", 470, Rarity.COMMON, mage.cards.r.RiphookRaider.class)); + cards.add(new SetCardInfo("Rise of the Ants", 196, Rarity.UNCOMMON, mage.cards.r.RiseOfTheAnts.class)); + cards.add(new SetCardInfo("Rite of Harmony", 236, Rarity.RARE, mage.cards.r.RiteOfHarmony.class)); + cards.add(new SetCardInfo("Rite of Oblivion", 237, Rarity.UNCOMMON, mage.cards.r.RiteOfOblivion.class)); + cards.add(new SetCardInfo("Ritual Guardian", 30, Rarity.COMMON, mage.cards.r.RitualGuardian.class)); + cards.add(new SetCardInfo("Ritual of Hope", 31, Rarity.UNCOMMON, mage.cards.r.RitualOfHope.class)); + cards.add(new SetCardInfo("Rockfall Vale", 266, Rarity.RARE, mage.cards.r.RockfallVale.class)); + cards.add(new SetCardInfo("Rootcoil Creeper", 238, Rarity.UNCOMMON, mage.cards.r.RootcoilCreeper.class)); + cards.add(new SetCardInfo("Rot-Tide Gargantua", 396, Rarity.COMMON, mage.cards.r.RotTideGargantua.class)); + cards.add(new SetCardInfo("Rotten Reunion", 119, Rarity.COMMON, mage.cards.r.RottenReunion.class)); + cards.add(new SetCardInfo("Runebound Wolf", 443, Rarity.UNCOMMON, mage.cards.r.RuneboundWolf.class)); + cards.add(new SetCardInfo("Runo Stromkirk", 513, Rarity.RARE, mage.cards.r.RunoStromkirk.class)); + cards.add(new SetCardInfo("Rural Recruit", 483, Rarity.COMMON, mage.cards.r.RuralRecruit.class)); + cards.add(new SetCardInfo("Sacred Fire", 239, Rarity.UNCOMMON, mage.cards.s.SacredFire.class)); + cards.add(new SetCardInfo("Sanctify", 300, Rarity.COMMON, mage.cards.s.Sanctify.class)); + cards.add(new SetCardInfo("Sanguine Statuette", 444, Rarity.UNCOMMON, mage.cards.s.SanguineStatuette.class)); + cards.add(new SetCardInfo("Saryth, the Viper's Fang", 197, Rarity.RARE, mage.cards.s.SarythTheVipersFang.class)); + cards.add(new SetCardInfo("Savage Packmate", 501, Rarity.UNCOMMON, mage.cards.s.SavagePackmate.class)); + cards.add(new SetCardInfo("Savior of Ollenbock", 301, Rarity.MYTHIC, mage.cards.s.SaviorOfOllenbock.class)); + cards.add(new SetCardInfo("Sawblade Slinger", 484, Rarity.UNCOMMON, mage.cards.s.SawbladeSlinger.class)); + cards.add(new SetCardInfo("Scattered Thoughts", 341, Rarity.COMMON, mage.cards.s.ScatteredThoughts.class)); + cards.add(new SetCardInfo("Screaming Swarm", 342, Rarity.UNCOMMON, mage.cards.s.ScreamingSwarm.class)); + cards.add(new SetCardInfo("Seafaring Werewolf", 80, Rarity.RARE, mage.cards.s.SeafaringWerewolf.class)); + cards.add(new SetCardInfo("Search Party Captain", 32, Rarity.COMMON, mage.cards.s.SearchPartyCaptain.class)); + cards.add(new SetCardInfo("Seasoned Cathar", 2, Rarity.UNCOMMON, mage.cards.s.SeasonedCathar.class)); + cards.add(new SetCardInfo("Secrets of the Key", 73, Rarity.COMMON, mage.cards.s.SecretsOfTheKey.class)); + cards.add(new SetCardInfo("Seize the Storm", 158, Rarity.UNCOMMON, mage.cards.s.SeizeTheStorm.class)); + cards.add(new SetCardInfo("Selhoff Entomber", 343, Rarity.COMMON, mage.cards.s.SelhoffEntomber.class)); + cards.add(new SetCardInfo("Serpentine Ambush", 344, Rarity.COMMON, mage.cards.s.SerpentineAmbush.class)); + cards.add(new SetCardInfo("Shadowbeast Sighting", 198, Rarity.COMMON, mage.cards.s.ShadowbeastSighting.class)); + cards.add(new SetCardInfo("Shady Traveler", 120, Rarity.COMMON, mage.cards.s.ShadyTraveler.class)); + cards.add(new SetCardInfo("Shattered Sanctum", 531, Rarity.RARE, mage.cards.s.ShatteredSanctum.class)); + cards.add(new SetCardInfo("Sheltering Boughs", 485, Rarity.COMMON, mage.cards.s.ShelteringBoughs.class)); + cards.add(new SetCardInfo("Shipwreck Marsh", 267, Rarity.RARE, mage.cards.s.ShipwreckMarsh.class)); + cards.add(new SetCardInfo("Shipwreck Sifters", 74, Rarity.COMMON, mage.cards.s.ShipwreckSifters.class)); + cards.add(new SetCardInfo("Siege Zombie", 121, Rarity.COMMON, mage.cards.s.SiegeZombie.class)); + cards.add(new SetCardInfo("Sigarda's Imprisonment", 302, Rarity.COMMON, mage.cards.s.SigardasImprisonment.class)); + cards.add(new SetCardInfo("Sigarda's Splendor", 33, Rarity.RARE, mage.cards.s.SigardasSplendor.class)); + cards.add(new SetCardInfo("Sigarda's Summons", 303, Rarity.RARE, mage.cards.s.SigardasSummons.class)); + cards.add(new SetCardInfo("Sigarda, Champion of Light", 240, Rarity.MYTHIC, mage.cards.s.SigardaChampionOfLight.class)); + cards.add(new SetCardInfo("Sigardian Paladin", 514, Rarity.UNCOMMON, mage.cards.s.SigardianPaladin.class)); + cards.add(new SetCardInfo("Sigardian Savior", 34, Rarity.MYTHIC, mage.cards.s.SigardianSavior.class)); + cards.add(new SetCardInfo("Silver Bolt", 258, Rarity.COMMON, mage.cards.s.SilverBolt.class)); + cards.add(new SetCardInfo("Sinner's Judgment", 279, Rarity.MYTHIC, mage.cards.s.SinnersJudgment.class)); + cards.add(new SetCardInfo("Siphon Insight", 241, Rarity.RARE, mage.cards.s.SiphonInsight.class)); + cards.add(new SetCardInfo("Skaab Wrangler", 75, Rarity.UNCOMMON, mage.cards.s.SkaabWrangler.class)); + cards.add(new SetCardInfo("Skulking Killer", 397, Rarity.UNCOMMON, mage.cards.s.SkulkingKiller.class)); + cards.add(new SetCardInfo("Skull Skaab", 515, Rarity.UNCOMMON, mage.cards.s.SkullSkaab.class)); + cards.add(new SetCardInfo("Skywarp Skaab", 345, Rarity.COMMON, mage.cards.s.SkywarpSkaab.class)); + cards.add(new SetCardInfo("Slaughter Specialist", 122, Rarity.RARE, mage.cards.s.SlaughterSpecialist.class)); + cards.add(new SetCardInfo("Slogurk, the Overslime", 242, Rarity.RARE, mage.cards.s.SlogurkTheOverslime.class)); + cards.add(new SetCardInfo("Sludge Monster", 76, Rarity.RARE, mage.cards.s.SludgeMonster.class)); + cards.add(new SetCardInfo("Smoldering Egg", 159, Rarity.RARE, mage.cards.s.SmolderingEgg.class)); + cards.add(new SetCardInfo("Snarling Wolf", 199, Rarity.COMMON, mage.cards.s.SnarlingWolf.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Snarling Wolf", 486, Rarity.COMMON, mage.cards.s.SnarlingWolf.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sorin the Mirthless", 398, Rarity.MYTHIC, mage.cards.s.SorinTheMirthless.class)); + cards.add(new SetCardInfo("Soul-Guide Gryff", 35, Rarity.COMMON, mage.cards.s.SoulGuideGryff.class)); + cards.add(new SetCardInfo("Soulcipher Board", 346, Rarity.UNCOMMON, mage.cards.s.SoulcipherBoard.class)); + cards.add(new SetCardInfo("Spectral Adversary", 77, Rarity.MYTHIC, mage.cards.s.SpectralAdversary.class)); + cards.add(new SetCardInfo("Spectral Binding", 315, Rarity.COMMON, mage.cards.s.SpectralBinding.class)); + cards.add(new SetCardInfo("Spellrune Howler", 160, Rarity.UNCOMMON, mage.cards.s.SpellruneHowler.class)); + cards.add(new SetCardInfo("Spellrune Painter", 160, Rarity.UNCOMMON, mage.cards.s.SpellrunePainter.class)); + cards.add(new SetCardInfo("Spiked Ripsaw", 487, Rarity.UNCOMMON, mage.cards.s.SpikedRipsaw.class)); + cards.add(new SetCardInfo("Splendid Reclamation", 488, Rarity.RARE, mage.cards.s.SplendidReclamation.class)); + cards.add(new SetCardInfo("Spore Crawler", 489, Rarity.COMMON, mage.cards.s.SporeCrawler.class)); + cards.add(new SetCardInfo("Sporeback Wolf", 490, Rarity.COMMON, mage.cards.s.SporebackWolf.class)); + cards.add(new SetCardInfo("Stalking Predator", 120, Rarity.COMMON, mage.cards.s.StalkingPredator.class)); + cards.add(new SetCardInfo("Startle", 78, Rarity.COMMON, mage.cards.s.Startle.class)); + cards.add(new SetCardInfo("Steelclad Spirit", 347, Rarity.COMMON, mage.cards.s.SteelcladSpirit.class)); + cards.add(new SetCardInfo("Stensia Uprising", 445, Rarity.RARE, mage.cards.s.StensiaUprising.class)); + cards.add(new SetCardInfo("Stitched Assistant", 348, Rarity.COMMON, mage.cards.s.StitchedAssistant.class)); + cards.add(new SetCardInfo("Stolen Vitality", 161, Rarity.COMMON, mage.cards.s.StolenVitality.class)); + cards.add(new SetCardInfo("Storm Skreelix", 243, Rarity.UNCOMMON, mage.cards.s.StormSkreelix.class)); + cards.add(new SetCardInfo("Storm the Festival", 200, Rarity.RARE, mage.cards.s.StormTheFestival.class)); + cards.add(new SetCardInfo("Storm-Charged Slasher", 157, Rarity.RARE, mage.cards.s.StormChargedSlasher.class)); + cards.add(new SetCardInfo("Stormcarved Coast", 532, Rarity.RARE, mage.cards.s.StormcarvedCoast.class)); + cards.add(new SetCardInfo("Stormchaser Drake", 349, Rarity.UNCOMMON, mage.cards.s.StormchaserDrake.class)); + cards.add(new SetCardInfo("Stormrider Spirit", 79, Rarity.COMMON, mage.cards.s.StormriderSpirit.class)); + cards.add(new SetCardInfo("Strangling Grasp", 126, Rarity.UNCOMMON, mage.cards.s.StranglingGrasp.class)); + cards.add(new SetCardInfo("Stromkirk Bloodthief", 123, Rarity.UNCOMMON, mage.cards.s.StromkirkBloodthief.class)); + cards.add(new SetCardInfo("Stuffed Bear", 259, Rarity.COMMON, mage.cards.s.StuffedBear.class)); + cards.add(new SetCardInfo("Sundown Pass", 533, Rarity.RARE, mage.cards.s.SundownPass.class)); + cards.add(new SetCardInfo("Sungold Barrage", 36, Rarity.COMMON, mage.cards.s.SungoldBarrage.class)); + cards.add(new SetCardInfo("Sungold Sentinel", 37, Rarity.RARE, mage.cards.s.SungoldSentinel.class)); + cards.add(new SetCardInfo("Sunrise Cavalier", 244, Rarity.UNCOMMON, mage.cards.s.SunriseCavalier.class)); + cards.add(new SetCardInfo("Sunset Revelry", 38, Rarity.UNCOMMON, mage.cards.s.SunsetRevelry.class)); + cards.add(new SetCardInfo("Sunstreak Phoenix", 162, Rarity.MYTHIC, mage.cards.s.SunstreakPhoenix.class)); + cards.add(new SetCardInfo("Supernatural Rescue", 304, Rarity.COMMON, mage.cards.s.SupernaturalRescue.class)); + cards.add(new SetCardInfo("Sure Strike", 446, Rarity.COMMON, mage.cards.s.SureStrike.class)); + cards.add(new SetCardInfo("Suspicious Stowaway", 80, Rarity.RARE, mage.cards.s.SuspiciousStowaway.class)); + cards.add(new SetCardInfo("Syncopate", 350, Rarity.COMMON, mage.cards.s.Syncopate.class)); + cards.add(new SetCardInfo("Syphon Essence", 351, Rarity.COMMON, mage.cards.s.SyphonEssence.class)); + cards.add(new SetCardInfo("Tainted Adversary", 124, Rarity.MYTHIC, mage.cards.t.TaintedAdversary.class)); + cards.add(new SetCardInfo("Tapping at the Window", 201, Rarity.COMMON, mage.cards.t.TappingAtTheWindow.class)); + cards.add(new SetCardInfo("Tavern Ruffian", 163, Rarity.COMMON, mage.cards.t.TavernRuffian.class)); + cards.add(new SetCardInfo("Tavern Smasher", 163, Rarity.COMMON, mage.cards.t.TavernSmasher.class)); + cards.add(new SetCardInfo("Teferi, Who Slows the Sunset", 245, Rarity.MYTHIC, mage.cards.t.TeferiWhoSlowsTheSunset.class)); + cards.add(new SetCardInfo("Thalia, Guardian of Thraben", 305, Rarity.RARE, mage.cards.t.ThaliaGuardianOfThraben.class)); + cards.add(new SetCardInfo("The Celestus", 252, Rarity.RARE, mage.cards.t.TheCelestus.class)); + cards.add(new SetCardInfo("The Meathook Massacre", 112, Rarity.MYTHIC, mage.cards.t.TheMeathookMassacre.class)); + cards.add(new SetCardInfo("Thermo-Alchemist", 164, Rarity.UNCOMMON, mage.cards.t.ThermoAlchemist.class)); + cards.add(new SetCardInfo("Thirst for Discovery", 352, Rarity.UNCOMMON, mage.cards.t.ThirstForDiscovery.class)); + cards.add(new SetCardInfo("Thraben Exorcism", 39, Rarity.COMMON, mage.cards.t.ThrabenExorcism.class)); + cards.add(new SetCardInfo("Timberland Guide", 202, Rarity.COMMON, mage.cards.t.TimberlandGuide.class)); + cards.add(new SetCardInfo("Tireless Hauler", 203, Rarity.COMMON, mage.cards.t.TirelessHauler.class)); + cards.add(new SetCardInfo("Torens, Fist of the Angels", 516, Rarity.RARE, mage.cards.t.TorensFistOfTheAngels.class)); + cards.add(new SetCardInfo("Tovolar's Huntmaster", 204, Rarity.RARE, mage.cards.t.TovolarsHuntmaster.class)); + cards.add(new SetCardInfo("Tovolar's Packleader", 204, Rarity.RARE, mage.cards.t.TovolarsPackleader.class)); + cards.add(new SetCardInfo("Tovolar, Dire Overlord", 246, Rarity.RARE, mage.cards.t.TovolarDireOverlord.class)); + cards.add(new SetCardInfo("Tovolar, the Midnight Scourge", 246, Rarity.RARE, mage.cards.t.TovolarTheMidnightScourge.class)); + cards.add(new SetCardInfo("Toxic Scorpion", 491, Rarity.COMMON, mage.cards.t.ToxicScorpion.class)); + cards.add(new SetCardInfo("Toxrill, the Corrosive", 399, Rarity.MYTHIC, mage.cards.t.ToxrillTheCorrosive.class)); + cards.add(new SetCardInfo("Traveling Minister", 306, Rarity.COMMON, mage.cards.t.TravelingMinister.class)); + cards.add(new SetCardInfo("Triskaidekaphile", 81, Rarity.RARE, mage.cards.t.Triskaidekaphile.class)); + cards.add(new SetCardInfo("Turn the Earth", 205, Rarity.UNCOMMON, mage.cards.t.TurnTheEarth.class)); + cards.add(new SetCardInfo("Twinblade Geist", 307, Rarity.UNCOMMON, mage.cards.t.TwinbladeGeist.class)); + cards.add(new SetCardInfo("Twinblade Invocation", 307, Rarity.UNCOMMON, mage.cards.t.TwinbladeInvocation.class)); + cards.add(new SetCardInfo("Ulvenwald Behemoth", 492, Rarity.RARE, mage.cards.u.UlvenwaldBehemoth.class)); + cards.add(new SetCardInfo("Ulvenwald Oddity", 492, Rarity.RARE, mage.cards.u.UlvenwaldOddity.class)); + cards.add(new SetCardInfo("Unblinking Observer", 82, Rarity.COMMON, mage.cards.u.UnblinkingObserver.class)); + cards.add(new SetCardInfo("Undead Butler", 400, Rarity.UNCOMMON, mage.cards.u.UndeadButler.class)); + cards.add(new SetCardInfo("Undying Malice", 401, Rarity.COMMON, mage.cards.u.UndyingMalice.class)); + cards.add(new SetCardInfo("Unhallowed Phalanx", 402, Rarity.COMMON, mage.cards.u.UnhallowedPhalanx.class)); + cards.add(new SetCardInfo("Unholy Officiant", 308, Rarity.COMMON, mage.cards.u.UnholyOfficiant.class)); + cards.add(new SetCardInfo("Unnatural Growth", 206, Rarity.RARE, mage.cards.u.UnnaturalGrowth.class)); + cards.add(new SetCardInfo("Unnatural Moonrise", 247, Rarity.UNCOMMON, mage.cards.u.UnnaturalMoonrise.class)); + cards.add(new SetCardInfo("Unruly Mob", 40, Rarity.COMMON, mage.cards.u.UnrulyMob.class)); + cards.add(new SetCardInfo("Untamed Pup", 187, Rarity.UNCOMMON, mage.cards.u.UntamedPup.class)); + cards.add(new SetCardInfo("Vadrik, Astral Archmage", 248, Rarity.RARE, mage.cards.v.VadrikAstralArchmage.class)); + cards.add(new SetCardInfo("Valorous Stance", 309, Rarity.UNCOMMON, mage.cards.v.ValorousStance.class)); + cards.add(new SetCardInfo("Vampire Interloper", 125, Rarity.COMMON, mage.cards.v.VampireInterloper.class)); + cards.add(new SetCardInfo("Vampire Slayer", 310, Rarity.COMMON, mage.cards.v.VampireSlayer.class)); + cards.add(new SetCardInfo("Vampire Socialite", 249, Rarity.UNCOMMON, mage.cards.v.VampireSocialite.class)); + cards.add(new SetCardInfo("Vampire's Kiss", 403, Rarity.COMMON, mage.cards.v.VampiresKiss.class)); + cards.add(new SetCardInfo("Vampires' Vengeance", 447, Rarity.UNCOMMON, mage.cards.v.VampiresVengeance.class)); + cards.add(new SetCardInfo("Vanquish the Horde", 41, Rarity.RARE, mage.cards.v.VanquishTheHorde.class)); + cards.add(new SetCardInfo("Vengeful Strangler", 126, Rarity.UNCOMMON, mage.cards.v.VengefulStrangler.class)); + cards.add(new SetCardInfo("Vilespawn Spider", 517, Rarity.UNCOMMON, mage.cards.v.VilespawnSpider.class)); + cards.add(new SetCardInfo("Village Reavers", 165, Rarity.UNCOMMON, mage.cards.v.VillageReavers.class)); + cards.add(new SetCardInfo("Village Watch", 165, Rarity.UNCOMMON, mage.cards.v.VillageWatch.class)); + cards.add(new SetCardInfo("Vivisection", 83, Rarity.UNCOMMON, mage.cards.v.Vivisection.class)); + cards.add(new SetCardInfo("Voice of the Blessed", 311, Rarity.RARE, mage.cards.v.VoiceOfTheBlessed.class)); + cards.add(new SetCardInfo("Volatile Arsonist", 448, Rarity.MYTHIC, mage.cards.v.VolatileArsonist.class)); + cards.add(new SetCardInfo("Voldaren Ambusher", 166, Rarity.UNCOMMON, mage.cards.v.VoldarenAmbusher.class)); + cards.add(new SetCardInfo("Voldaren Bloodcaster", 404, Rarity.RARE, mage.cards.v.VoldarenBloodcaster.class)); + cards.add(new SetCardInfo("Voldaren Epicure", 449, Rarity.COMMON, mage.cards.v.VoldarenEpicure.class)); + cards.add(new SetCardInfo("Voldaren Estate", 534, Rarity.RARE, mage.cards.v.VoldarenEstate.class)); + cards.add(new SetCardInfo("Voldaren Stinger", 167, Rarity.COMMON, mage.cards.v.VoldarenStinger.class)); + cards.add(new SetCardInfo("Volt-Charged Berserker", 450, Rarity.UNCOMMON, mage.cards.v.VoltChargedBerserker.class)); + cards.add(new SetCardInfo("Voltaic Visionary", 450, Rarity.UNCOMMON, mage.cards.v.VoltaicVisionary.class)); + cards.add(new SetCardInfo("Waildrifter", 55, Rarity.COMMON, mage.cards.w.Waildrifter.class)); + cards.add(new SetCardInfo("Wake to Slaughter", 250, Rarity.RARE, mage.cards.w.WakeToSlaughter.class)); + cards.add(new SetCardInfo("Wandering Mind", 518, Rarity.UNCOMMON, mage.cards.w.WanderingMind.class)); + cards.add(new SetCardInfo("Wanderlight Spirit", 353, Rarity.COMMON, mage.cards.w.WanderlightSpirit.class)); + cards.add(new SetCardInfo("Wash Away", 354, Rarity.UNCOMMON, mage.cards.w.WashAway.class)); + cards.add(new SetCardInfo("Weary Prisoner", 451, Rarity.COMMON, mage.cards.w.WearyPrisoner.class)); + cards.add(new SetCardInfo("Weaver of Blossoms", 493, Rarity.COMMON, mage.cards.w.WeaverOfBlossoms.class)); + cards.add(new SetCardInfo("Wedding Announcement", 312, Rarity.RARE, mage.cards.w.WeddingAnnouncement.class)); + cards.add(new SetCardInfo("Wedding Crasher", 496, Rarity.UNCOMMON, mage.cards.w.WeddingCrasher.class)); + cards.add(new SetCardInfo("Wedding Festivity", 312, Rarity.RARE, mage.cards.w.WeddingFestivity.class)); + cards.add(new SetCardInfo("Wedding Invitation", 527, Rarity.COMMON, mage.cards.w.WeddingInvitation.class)); + cards.add(new SetCardInfo("Wedding Security", 405, Rarity.UNCOMMON, mage.cards.w.WeddingSecurity.class)); + cards.add(new SetCardInfo("Welcoming Vampire", 313, Rarity.RARE, mage.cards.w.WelcomingVampire.class)); + cards.add(new SetCardInfo("Whispering Wizard", 355, Rarity.UNCOMMON, mage.cards.w.WhisperingWizard.class)); + cards.add(new SetCardInfo("Wildsong Howler", 472, Rarity.RARE, mage.cards.w.WildsongHowler.class)); + cards.add(new SetCardInfo("Willow Geist", 207, Rarity.RARE, mage.cards.w.WillowGeist.class)); + cards.add(new SetCardInfo("Wing Shredder", 169, Rarity.COMMON, mage.cards.w.WingShredder.class)); + cards.add(new SetCardInfo("Winged Portent", 356, Rarity.RARE, mage.cards.w.WingedPortent.class)); + cards.add(new SetCardInfo("Winterthorn Blessing", 251, Rarity.UNCOMMON, mage.cards.w.WinterthornBlessing.class)); + cards.add(new SetCardInfo("Witch's Web", 494, Rarity.COMMON, mage.cards.w.WitchsWeb.class)); + cards.add(new SetCardInfo("Witness the Future", 357, Rarity.UNCOMMON, mage.cards.w.WitnessTheFuture.class)); + cards.add(new SetCardInfo("Wolf Strike", 495, Rarity.COMMON, mage.cards.w.WolfStrike.class)); + cards.add(new SetCardInfo("Wolfkin Outcast", 496, Rarity.UNCOMMON, mage.cards.w.WolfkinOutcast.class)); + cards.add(new SetCardInfo("Wrathful Jailbreaker", 451, Rarity.COMMON, mage.cards.w.WrathfulJailbreaker.class)); + cards.add(new SetCardInfo("Wrenn and Seven", 208, Rarity.MYTHIC, mage.cards.w.WrennAndSeven.class)); + cards.add(new SetCardInfo("Wretched Throng", 358, Rarity.COMMON, mage.cards.w.WretchedThrong.class)); + } +} diff --git a/Mage.Sets/src/mage/sets/InnistradMidnightHunt.java b/Mage.Sets/src/mage/sets/InnistradMidnightHunt.java index a4201ba0e33..3f994791efc 100644 --- a/Mage.Sets/src/mage/sets/InnistradMidnightHunt.java +++ b/Mage.Sets/src/mage/sets/InnistradMidnightHunt.java @@ -1,5 +1,6 @@ package mage.sets; +import mage.cards.Card; import mage.cards.ExpansionSet; import mage.collation.BoosterCollator; import mage.collation.BoosterStructure; @@ -9,7 +10,6 @@ import mage.constants.Rarity; import mage.constants.SetType; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; /** @@ -17,7 +17,6 @@ import java.util.List; */ public final class InnistradMidnightHunt extends ExpansionSet { - private static final List unfinished = Arrays.asList("Arlinn, the Pack's Hope", "Arlinn, the Moon's Fury", "Baithook Angler", "Hook-Haunt Drifter", "Baneblade Scoundrel", "Baneclaw Marauder", "Beloved Beggar", "Generous Soul", "Bird Admirer", "Wing Shredder", "Brimstone Vandal", "Brutal Cathar", "Moonrage Brute", "Burly Breaker", "Dire-Strain Demolisher", "Celestus Sanctifier", "Chaplain of Alms", "Chapel Shieldgeist", "Component Collector", "Covert Cutpurse", "Covetous Geist", "Covetous Castaway", "Ghostly Castigator", "Curse of Leeches", "Leeching Lurker", "Dennick, Pious Apprentice", "Dennick, Pious Apparition", "Devoted Grafkeeper", "Departed Soulkeeper", "Fangblade Brigand", "Fangblade Eviscerator", "Firmament Sage", "Galedrifter", "Waildrifter", "Gavony Dawnguard", "Graveyard Trespasser", "Graveyard Glutton", "Harvesttide Infiltrator", "Harvesttide Assailant", "Hound Tamer", "Untamed Pup", "Kessig Naturalist", "Lord of the Ulvenwald", "Lunarch Veteran", "Luminous Phantom", "Malevolent Hermit", "Benevolent Geist", "Mourning Patrol", "Morning Apparition", "Obsessive Astronomer", "Outland Liberator", "Frenzied Trapbreaker", "Overwhelmed Archivist", "Archive Haunt", "Phantom Carriage", "Reckless Stormseeker", "Storm-Charged Slasher", "Shady Traveler", "Stalking Predator", "Shipwreck Sifters", "Spellrune Painter", "Spellrune Howler", "Sunrise Cavalier", "Sunstreak Phoenix", "Suspicious Stowaway", "Seafaring Werewolf", "Tavern Ruffian", "Tavern Smasher", "The Celestus", "Thraben Exorcism", "Tireless Hauler", "Dire-Strain Brawler", "Tovolar, Dire Overlord", "Tovolar, the Midnight Scourge", "Tovolar's Huntmaster", "Tovolar's Packleader", "Unblinking Observer", "Vadrik, Astral Archmage", "Village Watch", "Village Reavers"); private static final InnistradMidnightHunt instance = new InnistradMidnightHunt(); public static InnistradMidnightHunt getInstance() { @@ -34,90 +33,138 @@ public final class InnistradMidnightHunt extends ExpansionSet { this.numBoosterUncommon = 3; this.numBoosterRare = 1; this.ratioBoosterMythic = 8; + this.ratioBoosterSpecialRare = 5.5; + this.ratioBoosterSpecialMythic = 5.4; // 5 mythic DFCs, 11 rare DFCs this.numBoosterDoubleFaced = 1; + this.maxCardNumberInBooster = 277; cards.add(new SetCardInfo("Abandon the Post", 127, Rarity.COMMON, mage.cards.a.AbandonThePost.class)); - cards.add(new SetCardInfo("Adeline, Resplendent Cathar", 1, Rarity.RARE, mage.cards.a.AdelineResplendentCathar.class)); + cards.add(new SetCardInfo("Adeline, Resplendent Cathar", 1, Rarity.RARE, mage.cards.a.AdelineResplendentCathar.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Adeline, Resplendent Cathar", 312, Rarity.RARE, mage.cards.a.AdelineResplendentCathar.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ambitious Farmhand", 2, Rarity.UNCOMMON, mage.cards.a.AmbitiousFarmhand.class)); - cards.add(new SetCardInfo("Angelfire Ignition", 209, Rarity.RARE, mage.cards.a.AngelfireIgnition.class)); + cards.add(new SetCardInfo("Angelfire Ignition", 209, Rarity.RARE, mage.cards.a.AngelfireIgnition.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Angelfire Ignition", 367, Rarity.RARE, mage.cards.a.AngelfireIgnition.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Angelic Enforcer", 17, Rarity.MYTHIC, mage.cards.a.AngelicEnforcer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Angelic Enforcer", 327, Rarity.MYTHIC, mage.cards.a.AngelicEnforcer.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Arcane Infusion", 210, Rarity.UNCOMMON, mage.cards.a.ArcaneInfusion.class)); cards.add(new SetCardInfo("Archive Haunt", 68, Rarity.UNCOMMON, mage.cards.a.ArchiveHaunt.class)); cards.add(new SetCardInfo("Ardent Elementalist", 128, Rarity.COMMON, mage.cards.a.ArdentElementalist.class)); - cards.add(new SetCardInfo("Arlinn, the Moon's Fury", 211, Rarity.MYTHIC, mage.cards.a.ArlinnTheMoonsFury.class)); - cards.add(new SetCardInfo("Arlinn, the Pack's Hope", 211, Rarity.MYTHIC, mage.cards.a.ArlinnThePacksHope.class)); + cards.add(new SetCardInfo("Arlinn, the Moon's Fury", 211, Rarity.MYTHIC, mage.cards.a.ArlinnTheMoonsFury.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Arlinn, the Moon's Fury", 279, Rarity.MYTHIC, mage.cards.a.ArlinnTheMoonsFury.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Arlinn, the Moon's Fury", 307, Rarity.MYTHIC, mage.cards.a.ArlinnTheMoonsFury.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Arlinn, the Pack's Hope", 211, Rarity.MYTHIC, mage.cards.a.ArlinnThePacksHope.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Arlinn, the Pack's Hope", 279, Rarity.MYTHIC, mage.cards.a.ArlinnThePacksHope.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Arlinn, the Pack's Hope", 307, Rarity.MYTHIC, mage.cards.a.ArlinnThePacksHope.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Arrogant Outlaw", 84, Rarity.COMMON, mage.cards.a.ArrogantOutlaw.class)); - cards.add(new SetCardInfo("Ashmouth Dragon", 159, Rarity.RARE, mage.cards.a.AshmouthDragon.class)); - cards.add(new SetCardInfo("Augur of Autumn", 168, Rarity.RARE, mage.cards.a.AugurOfAutumn.class)); + cards.add(new SetCardInfo("Ashmouth Dragon", 159, Rarity.RARE, mage.cards.a.AshmouthDragon.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ashmouth Dragon", 358, Rarity.RARE, mage.cards.a.AshmouthDragon.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Augur of Autumn", 168, Rarity.RARE, mage.cards.a.AugurOfAutumn.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Augur of Autumn", 360, Rarity.RARE, mage.cards.a.AugurOfAutumn.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Awoken Demon", 100, Rarity.COMMON, mage.cards.a.AwokenDemon.class)); cards.add(new SetCardInfo("Baithook Angler", 42, Rarity.COMMON, mage.cards.b.BaithookAngler.class)); + cards.add(new SetCardInfo("Baneblade Scoundrel", 289, Rarity.UNCOMMON, mage.cards.b.BanebladeScoundrel.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Baneblade Scoundrel", 85, Rarity.UNCOMMON, mage.cards.b.BanebladeScoundrel.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Baneclaw Marauder", 289, Rarity.UNCOMMON, mage.cards.b.BaneclawMarauder.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Baneclaw Marauder", 85, Rarity.UNCOMMON, mage.cards.b.BaneclawMarauder.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Bat Whisperer", 86, Rarity.COMMON, mage.cards.b.BatWhisperer.class)); - cards.add(new SetCardInfo("Benevolent Geist", 61, Rarity.RARE, mage.cards.b.BenevolentGeist.class)); + cards.add(new SetCardInfo("Beloved Beggar", 3, Rarity.UNCOMMON, mage.cards.b.BelovedBeggar.class)); + cards.add(new SetCardInfo("Benevolent Geist", 336, Rarity.RARE, mage.cards.b.BenevolentGeist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Benevolent Geist", 61, Rarity.RARE, mage.cards.b.BenevolentGeist.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Bereaved Survivor", 4, Rarity.UNCOMMON, mage.cards.b.BereavedSurvivor.class)); - cards.add(new SetCardInfo("Bird Admirer", 169, Rarity.COMMON, mage.cards.b.BirdAdmirer.class)); + cards.add(new SetCardInfo("Bird Admirer", 169, Rarity.COMMON, mage.cards.b.BirdAdmirer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bird Admirer", 298, Rarity.COMMON, mage.cards.b.BirdAdmirer.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Bladebrand", 87, Rarity.COMMON, mage.cards.b.Bladebrand.class)); cards.add(new SetCardInfo("Bladestitched Skaab", 212, Rarity.UNCOMMON, mage.cards.b.BladestitchedSkaab.class)); cards.add(new SetCardInfo("Blessed Defiance", 5, Rarity.COMMON, mage.cards.b.BlessedDefiance.class)); cards.add(new SetCardInfo("Blood Pact", 88, Rarity.COMMON, mage.cards.b.BloodPact.class)); - cards.add(new SetCardInfo("Bloodline Culling", 89, Rarity.RARE, mage.cards.b.BloodlineCulling.class)); - cards.add(new SetCardInfo("Bloodthirsty Adversary", 129, Rarity.MYTHIC, mage.cards.b.BloodthirstyAdversary.class)); + cards.add(new SetCardInfo("Bloodline Culling", 343, Rarity.RARE, mage.cards.b.BloodlineCulling.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bloodline Culling", 89, Rarity.RARE, mage.cards.b.BloodlineCulling.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bloodthirsty Adversary", 129, Rarity.MYTHIC, mage.cards.b.BloodthirstyAdversary.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bloodthirsty Adversary", 351, Rarity.MYTHIC, mage.cards.b.BloodthirstyAdversary.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Bloodtithe Collector", 90, Rarity.UNCOMMON, mage.cards.b.BloodtitheCollector.class)); cards.add(new SetCardInfo("Borrowed Time", 6, Rarity.UNCOMMON, mage.cards.b.BorrowedTime.class)); cards.add(new SetCardInfo("Bounding Wolf", 170, Rarity.COMMON, mage.cards.b.BoundingWolf.class)); cards.add(new SetCardInfo("Bramble Armor", 171, Rarity.COMMON, mage.cards.b.BrambleArmor.class)); - cards.add(new SetCardInfo("Briarbridge Tracker", 172, Rarity.RARE, mage.cards.b.BriarbridgeTracker.class)); + cards.add(new SetCardInfo("Briarbridge Tracker", 172, Rarity.RARE, mage.cards.b.BriarbridgeTracker.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Briarbridge Tracker", 361, Rarity.RARE, mage.cards.b.BriarbridgeTracker.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Brimstone Vandal", 130, Rarity.COMMON, mage.cards.b.BrimstoneVandal.class)); cards.add(new SetCardInfo("Brood Weaver", 173, Rarity.UNCOMMON, mage.cards.b.BroodWeaver.class)); - cards.add(new SetCardInfo("Brutal Cathar", 7, Rarity.RARE, mage.cards.b.BrutalCathar.class)); - cards.add(new SetCardInfo("Burly Breaker", 174, Rarity.UNCOMMON, mage.cards.b.BurlyBreaker.class)); - cards.add(new SetCardInfo("Burn Down the House", 131, Rarity.RARE, mage.cards.b.BurnDownTheHouse.class)); + cards.add(new SetCardInfo("Brutal Cathar", 286, Rarity.RARE, mage.cards.b.BrutalCathar.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Brutal Cathar", 7, Rarity.RARE, mage.cards.b.BrutalCathar.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Burly Breaker", 174, Rarity.UNCOMMON, mage.cards.b.BurlyBreaker.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Burly Breaker", 299, Rarity.UNCOMMON, mage.cards.b.BurlyBreaker.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Burn Down the House", 131, Rarity.RARE, mage.cards.b.BurnDownTheHouse.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Burn Down the House", 352, Rarity.RARE, mage.cards.b.BurnDownTheHouse.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Burn the Accursed", 132, Rarity.COMMON, mage.cards.b.BurnTheAccursed.class)); - cards.add(new SetCardInfo("Can't Stay Away", 213, Rarity.RARE, mage.cards.c.CantStayAway.class)); - cards.add(new SetCardInfo("Candlegrove Witch", 8, Rarity.COMMON, mage.cards.c.CandlegroveWitch.class)); + cards.add(new SetCardInfo("Can't Stay Away", 213, Rarity.RARE, mage.cards.c.CantStayAway.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Can't Stay Away", 368, Rarity.RARE, mage.cards.c.CantStayAway.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Candlegrove Witch", 287, Rarity.COMMON, mage.cards.c.CandlegroveWitch.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Candlegrove Witch", 8, Rarity.COMMON, mage.cards.c.CandlegroveWitch.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Candlelit Cavalry", 175, Rarity.COMMON, mage.cards.c.CandlelitCavalry.class)); cards.add(new SetCardInfo("Candletrap", 9, Rarity.COMMON, mage.cards.c.Candletrap.class)); cards.add(new SetCardInfo("Cathar Commando", 10, Rarity.COMMON, mage.cards.c.CatharCommando.class)); cards.add(new SetCardInfo("Cathar's Call", 11, Rarity.UNCOMMON, mage.cards.c.CatharsCall.class)); cards.add(new SetCardInfo("Cathartic Pyre", 133, Rarity.UNCOMMON, mage.cards.c.CatharticPyre.class)); cards.add(new SetCardInfo("Celestus Sanctifier", 12, Rarity.COMMON, mage.cards.c.CelestusSanctifier.class)); - cards.add(new SetCardInfo("Champion of the Perished", 91, Rarity.RARE, mage.cards.c.ChampionOfThePerished.class)); + cards.add(new SetCardInfo("Champion of the Perished", 344, Rarity.RARE, mage.cards.c.ChampionOfThePerished.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Champion of the Perished", 385, Rarity.RARE, mage.cards.c.ChampionOfThePerished.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Champion of the Perished", 91, Rarity.RARE, mage.cards.c.ChampionOfThePerished.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Chapel Shieldgeist", 13, Rarity.UNCOMMON, mage.cards.c.ChapelShieldgeist.class)); cards.add(new SetCardInfo("Chaplain of Alms", 13, Rarity.UNCOMMON, mage.cards.c.ChaplainOfAlms.class)); cards.add(new SetCardInfo("Chilling Chronicle", 63, Rarity.UNCOMMON, mage.cards.c.ChillingChronicle.class)); cards.add(new SetCardInfo("Clarion Cathars", 14, Rarity.COMMON, mage.cards.c.ClarionCathars.class)); cards.add(new SetCardInfo("Clear Shot", 176, Rarity.UNCOMMON, mage.cards.c.ClearShot.class)); cards.add(new SetCardInfo("Component Collector", 43, Rarity.COMMON, mage.cards.c.ComponentCollector.class)); - cards.add(new SetCardInfo("Consider", 44, Rarity.COMMON, mage.cards.c.Consider.class)); - cards.add(new SetCardInfo("Consuming Blob", 177, Rarity.MYTHIC, mage.cards.c.ConsumingBlob.class)); + cards.add(new SetCardInfo("Consider", 388, Rarity.COMMON, mage.cards.c.Consider.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Consider", 44, Rarity.COMMON, mage.cards.c.Consider.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Consuming Blob", 177, Rarity.MYTHIC, mage.cards.c.ConsumingBlob.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Consuming Blob", 362, Rarity.MYTHIC, mage.cards.c.ConsumingBlob.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Contortionist Troupe", 178, Rarity.UNCOMMON, mage.cards.c.ContortionistTroupe.class)); cards.add(new SetCardInfo("Corpse Cobble", 214, Rarity.UNCOMMON, mage.cards.c.CorpseCobble.class)); cards.add(new SetCardInfo("Covert Cutpurse", 92, Rarity.UNCOMMON, mage.cards.c.CovertCutpurse.class)); cards.add(new SetCardInfo("Covetous Castaway", 45, Rarity.UNCOMMON, mage.cards.c.CovetousCastaway.class)); cards.add(new SetCardInfo("Covetous Geist", 92, Rarity.UNCOMMON, mage.cards.c.CovetousGeist.class)); cards.add(new SetCardInfo("Crawl from the Cellar", 93, Rarity.COMMON, mage.cards.c.CrawlFromTheCellar.class)); - cards.add(new SetCardInfo("Creeping Inn", 264, Rarity.MYTHIC, mage.cards.c.CreepingInn.class)); - cards.add(new SetCardInfo("Croaking Counterpart", 215, Rarity.RARE, mage.cards.c.CroakingCounterpart.class)); + cards.add(new SetCardInfo("Creeping Inn", 264, Rarity.MYTHIC, mage.cards.c.CreepingInn.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Creeping Inn", 379, Rarity.MYTHIC, mage.cards.c.CreepingInn.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Croaking Counterpart", 215, Rarity.RARE, mage.cards.c.CroakingCounterpart.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Croaking Counterpart", 369, Rarity.RARE, mage.cards.c.CroakingCounterpart.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Crossroads Candleguide", 253, Rarity.COMMON, mage.cards.c.CrossroadsCandleguide.class)); - cards.add(new SetCardInfo("Curse of Shaken Faith", 134, Rarity.RARE, mage.cards.c.CurseOfShakenFaith.class)); - cards.add(new SetCardInfo("Curse of Silence", 15, Rarity.RARE, mage.cards.c.CurseOfSilence.class)); - cards.add(new SetCardInfo("Curse of Surveillance", 46, Rarity.RARE, mage.cards.c.CurseOfSurveillance.class)); + cards.add(new SetCardInfo("Curse of Leeches", 345, Rarity.RARE, mage.cards.c.CurseOfLeeches.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Curse of Leeches", 94, Rarity.RARE, mage.cards.c.CurseOfLeeches.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Curse of Shaken Faith", 134, Rarity.RARE, mage.cards.c.CurseOfShakenFaith.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Curse of Shaken Faith", 353, Rarity.RARE, mage.cards.c.CurseOfShakenFaith.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Curse of Silence", 15, Rarity.RARE, mage.cards.c.CurseOfSilence.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Curse of Silence", 326, Rarity.RARE, mage.cards.c.CurseOfSilence.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Curse of Surveillance", 334, Rarity.RARE, mage.cards.c.CurseOfSurveillance.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Curse of Surveillance", 46, Rarity.RARE, mage.cards.c.CurseOfSurveillance.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Dauntless Avenger", 4, Rarity.UNCOMMON, mage.cards.d.DauntlessAvenger.class)); - cards.add(new SetCardInfo("Dawnhart Mentor", 179, Rarity.UNCOMMON, mage.cards.d.DawnhartMentor.class)); - cards.add(new SetCardInfo("Dawnhart Rejuvenator", 180, Rarity.COMMON, mage.cards.d.DawnhartRejuvenator.class)); - cards.add(new SetCardInfo("Dawnhart Wardens", 216, Rarity.UNCOMMON, mage.cards.d.DawnhartWardens.class)); + cards.add(new SetCardInfo("Dawnhart Mentor", 179, Rarity.UNCOMMON, mage.cards.d.DawnhartMentor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dawnhart Mentor", 300, Rarity.UNCOMMON, mage.cards.d.DawnhartMentor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dawnhart Rejuvenator", 180, Rarity.COMMON, mage.cards.d.DawnhartRejuvenator.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dawnhart Rejuvenator", 301, Rarity.COMMON, mage.cards.d.DawnhartRejuvenator.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dawnhart Wardens", 216, Rarity.UNCOMMON, mage.cards.d.DawnhartWardens.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dawnhart Wardens", 308, Rarity.UNCOMMON, mage.cards.d.DawnhartWardens.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Deathbonnet Hulk", 181, Rarity.UNCOMMON, mage.cards.d.DeathbonnetHulk.class)); cards.add(new SetCardInfo("Deathbonnet Sprout", 181, Rarity.UNCOMMON, mage.cards.d.DeathbonnetSprout.class)); cards.add(new SetCardInfo("Defend the Celestus", 182, Rarity.UNCOMMON, mage.cards.d.DefendTheCelestus.class)); cards.add(new SetCardInfo("Defenestrate", 95, Rarity.COMMON, mage.cards.d.Defenestrate.class)); cards.add(new SetCardInfo("Delver of Secrets", 47, Rarity.UNCOMMON, mage.cards.d.DelverOfSecrets.class)); - cards.add(new SetCardInfo("Dennick, Pious Apparition", 217, Rarity.RARE, mage.cards.d.DennickPiousApparition.class)); - cards.add(new SetCardInfo("Dennick, Pious Apprentice", 217, Rarity.RARE, mage.cards.d.DennickPiousApprentice.class)); + cards.add(new SetCardInfo("Dennick, Pious Apparition", 217, Rarity.RARE, mage.cards.d.DennickPiousApparition.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dennick, Pious Apparition", 317, Rarity.RARE, mage.cards.d.DennickPiousApparition.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dennick, Pious Apprentice", 217, Rarity.RARE, mage.cards.d.DennickPiousApprentice.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dennick, Pious Apprentice", 317, Rarity.RARE, mage.cards.d.DennickPiousApprentice.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Departed Soulkeeper", 218, Rarity.UNCOMMON, mage.cards.d.DepartedSoulkeeper.class)); - cards.add(new SetCardInfo("Deserted Beach", 260, Rarity.RARE, mage.cards.d.DesertedBeach.class)); + cards.add(new SetCardInfo("Deserted Beach", 260, Rarity.RARE, mage.cards.d.DesertedBeach.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Deserted Beach", 281, Rarity.RARE, mage.cards.d.DesertedBeach.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Devious Cover-Up", 48, Rarity.COMMON, mage.cards.d.DeviousCoverUp.class)); cards.add(new SetCardInfo("Devoted Grafkeeper", 218, Rarity.UNCOMMON, mage.cards.d.DevotedGrafkeeper.class)); - cards.add(new SetCardInfo("Dire-Strain Brawler", 203, Rarity.COMMON, mage.cards.d.DireStrainBrawler.class)); - cards.add(new SetCardInfo("Dire-Strain Demolisher", 174, Rarity.UNCOMMON, mage.cards.d.DireStrainDemolisher.class)); - cards.add(new SetCardInfo("Dire-Strain Rampage", 219, Rarity.RARE, mage.cards.d.DireStrainRampage.class)); + cards.add(new SetCardInfo("Dire-Strain Brawler", 203, Rarity.COMMON, mage.cards.d.DireStrainBrawler.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dire-Strain Brawler", 305, Rarity.COMMON, mage.cards.d.DireStrainBrawler.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dire-Strain Demolisher", 174, Rarity.UNCOMMON, mage.cards.d.DireStrainDemolisher.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dire-Strain Demolisher", 299, Rarity.UNCOMMON, mage.cards.d.DireStrainDemolisher.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dire-Strain Rampage", 219, Rarity.RARE, mage.cards.d.DireStrainRampage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dire-Strain Rampage", 370, Rarity.RARE, mage.cards.d.DireStrainRampage.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Diregraf Horde", 96, Rarity.COMMON, mage.cards.d.DiregrafHorde.class)); cards.add(new SetCardInfo("Diregraf Rebirth", 220, Rarity.UNCOMMON, mage.cards.d.DiregrafRebirth.class)); cards.add(new SetCardInfo("Dissipate", 49, Rarity.UNCOMMON, mage.cards.d.Dissipate.class)); @@ -132,16 +179,22 @@ public final class InnistradMidnightHunt extends ExpansionSet { cards.add(new SetCardInfo("Ecstatic Awakener", 100, Rarity.COMMON, mage.cards.e.EcstaticAwakener.class)); cards.add(new SetCardInfo("Electric Revelation", 135, Rarity.COMMON, mage.cards.e.ElectricRevelation.class)); cards.add(new SetCardInfo("Embodiment of Flame", 141, Rarity.UNCOMMON, mage.cards.e.EmbodimentOfFlame.class)); + cards.add(new SetCardInfo("Enduring Angel", 17, Rarity.MYTHIC, mage.cards.e.EnduringAngel.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Enduring Angel", 327, Rarity.MYTHIC, mage.cards.e.EnduringAngel.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Evolving Wilds", 261, Rarity.COMMON, mage.cards.e.EvolvingWilds.class)); cards.add(new SetCardInfo("Fading Hope", 51, Rarity.UNCOMMON, mage.cards.f.FadingHope.class)); cards.add(new SetCardInfo("Faithful Mending", 221, Rarity.UNCOMMON, mage.cards.f.FaithfulMending.class)); cards.add(new SetCardInfo("Falcon Abomination", 52, Rarity.COMMON, mage.cards.f.FalconAbomination.class)); cards.add(new SetCardInfo("Falkenrath Perforator", 136, Rarity.COMMON, mage.cards.f.FalkenrathPerforator.class)); - cards.add(new SetCardInfo("Falkenrath Pit Fighter", 137, Rarity.RARE, mage.cards.f.FalkenrathPitFighter.class)); + cards.add(new SetCardInfo("Falkenrath Pit Fighter", 137, Rarity.RARE, mage.cards.f.FalkenrathPitFighter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Falkenrath Pit Fighter", 354, Rarity.RARE, mage.cards.f.FalkenrathPitFighter.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Famished Foragers", 138, Rarity.COMMON, mage.cards.f.FamishedForagers.class)); - cards.add(new SetCardInfo("Fangblade Brigand", 139, Rarity.UNCOMMON, mage.cards.f.FangbladeBrigand.class)); - cards.add(new SetCardInfo("Fangblade Eviscerator", 139, Rarity.UNCOMMON, mage.cards.f.FangbladeEviscerator.class)); - cards.add(new SetCardInfo("Fateful Absence", 18, Rarity.RARE, mage.cards.f.FatefulAbsence.class)); + cards.add(new SetCardInfo("Fangblade Brigand", 139, Rarity.UNCOMMON, mage.cards.f.FangbladeBrigand.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fangblade Brigand", 292, Rarity.UNCOMMON, mage.cards.f.FangbladeBrigand.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fangblade Eviscerator", 139, Rarity.UNCOMMON, mage.cards.f.FangbladeEviscerator.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fangblade Eviscerator", 292, Rarity.UNCOMMON, mage.cards.f.FangbladeEviscerator.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fateful Absence", 18, Rarity.RARE, mage.cards.f.FatefulAbsence.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fateful Absence", 328, Rarity.RARE, mage.cards.f.FatefulAbsence.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Festival Crasher", 140, Rarity.COMMON, mage.cards.f.FestivalCrasher.class)); cards.add(new SetCardInfo("Field of Ruin", 262, Rarity.UNCOMMON, mage.cards.f.FieldOfRuin.class)); cards.add(new SetCardInfo("Firmament Sage", 53, Rarity.UNCOMMON, mage.cards.f.FirmamentSage.class)); @@ -149,75 +202,119 @@ public final class InnistradMidnightHunt extends ExpansionSet { cards.add(new SetCardInfo("Flare of Faith", 19, Rarity.COMMON, mage.cards.f.FlareOfFaith.class)); cards.add(new SetCardInfo("Fleshtaker", 222, Rarity.UNCOMMON, mage.cards.f.Fleshtaker.class)); cards.add(new SetCardInfo("Flip the Switch", 54, Rarity.COMMON, mage.cards.f.FlipTheSwitch.class)); - cards.add(new SetCardInfo("Florian, Voldaren Scion", 223, Rarity.RARE, mage.cards.f.FlorianVoldarenScion.class)); + cards.add(new SetCardInfo("Florian, Voldaren Scion", 223, Rarity.RARE, mage.cards.f.FlorianVoldarenScion.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Florian, Voldaren Scion", 318, Rarity.RARE, mage.cards.f.FlorianVoldarenScion.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Forest", 276, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Forest", 277, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Forest", 384, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Foul Play", 101, Rarity.UNCOMMON, mage.cards.f.FoulPlay.class)); - cards.add(new SetCardInfo("Frenzied Trapbreaker", 190, Rarity.UNCOMMON, mage.cards.f.FrenziedTrapbreaker.class)); + cards.add(new SetCardInfo("Frenzied Trapbreaker", 190, Rarity.UNCOMMON, mage.cards.f.FrenziedTrapbreaker.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Frenzied Trapbreaker", 303, Rarity.UNCOMMON, mage.cards.f.FrenziedTrapbreaker.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Galedrifter", 55, Rarity.COMMON, mage.cards.g.Galedrifter.class)); - cards.add(new SetCardInfo("Galvanic Iteration", 224, Rarity.RARE, mage.cards.g.GalvanicIteration.class)); - cards.add(new SetCardInfo("Gavony Dawnguard", 20, Rarity.UNCOMMON, mage.cards.g.GavonyDawnguard.class)); + cards.add(new SetCardInfo("Galvanic Iteration", 224, Rarity.RARE, mage.cards.g.GalvanicIteration.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Galvanic Iteration", 371, Rarity.RARE, mage.cards.g.GalvanicIteration.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gavony Dawnguard", 20, Rarity.UNCOMMON, mage.cards.g.GavonyDawnguard.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gavony Dawnguard", 387, Rarity.UNCOMMON, mage.cards.g.GavonyDawnguard.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gavony Silversmith", 21, Rarity.COMMON, mage.cards.g.GavonySilversmith.class)); cards.add(new SetCardInfo("Gavony Trapper", 22, Rarity.COMMON, mage.cards.g.GavonyTrapper.class)); - cards.add(new SetCardInfo("Geistflame Reservoir", 142, Rarity.RARE, mage.cards.g.GeistflameReservoir.class)); + cards.add(new SetCardInfo("Geistflame Reservoir", 142, Rarity.RARE, mage.cards.g.GeistflameReservoir.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Geistflame Reservoir", 355, Rarity.RARE, mage.cards.g.GeistflameReservoir.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Geistwave", 56, Rarity.COMMON, mage.cards.g.Geistwave.class)); + cards.add(new SetCardInfo("Generous Soul", 3, Rarity.UNCOMMON, mage.cards.g.GenerousSoul.class)); cards.add(new SetCardInfo("Ghostly Castigator", 45, Rarity.UNCOMMON, mage.cards.g.GhostlyCastigator.class)); - cards.add(new SetCardInfo("Ghoulcaller's Harvest", 225, Rarity.RARE, mage.cards.g.GhoulcallersHarvest.class)); + cards.add(new SetCardInfo("Ghoulcaller's Harvest", 225, Rarity.RARE, mage.cards.g.GhoulcallersHarvest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ghoulcaller's Harvest", 372, Rarity.RARE, mage.cards.g.GhoulcallersHarvest.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ghoulish Procession", 102, Rarity.UNCOMMON, mage.cards.g.GhoulishProcession.class)); - cards.add(new SetCardInfo("Gisa, Glorious Resurrector", 103, Rarity.RARE, mage.cards.g.GisaGloriousResurrector.class)); - cards.add(new SetCardInfo("Grafted Identity", 57, Rarity.RARE, mage.cards.g.GraftedIdentity.class)); - cards.add(new SetCardInfo("Graveyard Glutton", 104, Rarity.RARE, mage.cards.g.GraveyardGlutton.class)); - cards.add(new SetCardInfo("Graveyard Trespasser", 104, Rarity.RARE, mage.cards.g.GraveyardTrespasser.class)); + cards.add(new SetCardInfo("Gisa, Glorious Resurrector", 103, Rarity.RARE, mage.cards.g.GisaGloriousResurrector.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gisa, Glorious Resurrector", 314, Rarity.RARE, mage.cards.g.GisaGloriousResurrector.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Grafted Identity", 335, Rarity.RARE, mage.cards.g.GraftedIdentity.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Grafted Identity", 57, Rarity.RARE, mage.cards.g.GraftedIdentity.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Graveyard Glutton", 104, Rarity.RARE, mage.cards.g.GraveyardGlutton.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Graveyard Glutton", 290, Rarity.RARE, mage.cards.g.GraveyardGlutton.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Graveyard Trespasser", 104, Rarity.RARE, mage.cards.g.GraveyardTrespasser.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Graveyard Trespasser", 290, Rarity.RARE, mage.cards.g.GraveyardTrespasser.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Grizzly Ghoul", 226, Rarity.UNCOMMON, mage.cards.g.GrizzlyGhoul.class)); - cards.add(new SetCardInfo("Hallowed Respite", 227, Rarity.RARE, mage.cards.h.HallowedRespite.class)); - cards.add(new SetCardInfo("Harvesttide Assailant", 143, Rarity.COMMON, mage.cards.h.HarvesttideAssailant.class)); - cards.add(new SetCardInfo("Harvesttide Infiltrator", 143, Rarity.COMMON, mage.cards.h.HarvesttideInfiltrator.class)); + cards.add(new SetCardInfo("Hallowed Respite", 227, Rarity.RARE, mage.cards.h.HallowedRespite.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hallowed Respite", 373, Rarity.RARE, mage.cards.h.HallowedRespite.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Harvesttide Assailant", 143, Rarity.COMMON, mage.cards.h.HarvesttideAssailant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Harvesttide Assailant", 293, Rarity.COMMON, mage.cards.h.HarvesttideAssailant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Harvesttide Infiltrator", 143, Rarity.COMMON, mage.cards.h.HarvesttideInfiltrator.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Harvesttide Infiltrator", 293, Rarity.COMMON, mage.cards.h.HarvesttideInfiltrator.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Harvesttide Sentry", 186, Rarity.COMMON, mage.cards.h.HarvesttideSentry.class)); - cards.add(new SetCardInfo("Haunted Ridge", 263, Rarity.RARE, mage.cards.h.HauntedRidge.class)); + cards.add(new SetCardInfo("Haunted Ridge", 263, Rarity.RARE, mage.cards.h.HauntedRidge.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Haunted Ridge", 282, Rarity.RARE, mage.cards.h.HauntedRidge.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Hedgewitch's Mask", 23, Rarity.COMMON, mage.cards.h.HedgewitchsMask.class)); cards.add(new SetCardInfo("Heirloom Mirror", 105, Rarity.UNCOMMON, mage.cards.h.HeirloomMirror.class)); cards.add(new SetCardInfo("Hobbling Zombie", 106, Rarity.COMMON, mage.cards.h.HobblingZombie.class)); cards.add(new SetCardInfo("Homestead Courage", 24, Rarity.COMMON, mage.cards.h.HomesteadCourage.class)); cards.add(new SetCardInfo("Hook-Haunt Drifter", 42, Rarity.COMMON, mage.cards.h.HookHauntDrifter.class)); - cards.add(new SetCardInfo("Hostile Hostel", 264, Rarity.MYTHIC, mage.cards.h.HostileHostel.class)); - cards.add(new SetCardInfo("Hound Tamer", 187, Rarity.UNCOMMON, mage.cards.h.HoundTamer.class)); + cards.add(new SetCardInfo("Hostile Hostel", 264, Rarity.MYTHIC, mage.cards.h.HostileHostel.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hostile Hostel", 379, Rarity.MYTHIC, mage.cards.h.HostileHostel.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hound Tamer", 187, Rarity.UNCOMMON, mage.cards.h.HoundTamer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hound Tamer", 302, Rarity.UNCOMMON, mage.cards.h.HoundTamer.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Howl of the Hunt", 188, Rarity.COMMON, mage.cards.h.HowlOfTheHunt.class)); cards.add(new SetCardInfo("Hungry for More", 228, Rarity.UNCOMMON, mage.cards.h.HungryForMore.class)); cards.add(new SetCardInfo("Immolation", 144, Rarity.COMMON, mage.cards.i.Immolation.class)); - cards.add(new SetCardInfo("Infernal Grasp", 107, Rarity.UNCOMMON, mage.cards.i.InfernalGrasp.class)); + cards.add(new SetCardInfo("Infernal Grasp", 107, Rarity.UNCOMMON, mage.cards.i.InfernalGrasp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Infernal Grasp", 389, Rarity.UNCOMMON, mage.cards.i.InfernalGrasp.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Inherited Fiend", 105, Rarity.UNCOMMON, mage.cards.i.InheritedFiend.class)); cards.add(new SetCardInfo("Insectile Aberration", 47, Rarity.UNCOMMON, mage.cards.i.InsectileAberration.class)); - cards.add(new SetCardInfo("Intrepid Adversary", 25, Rarity.MYTHIC, mage.cards.i.IntrepidAdversary.class)); + cards.add(new SetCardInfo("Intrepid Adversary", 25, Rarity.MYTHIC, mage.cards.i.IntrepidAdversary.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Intrepid Adversary", 329, Rarity.MYTHIC, mage.cards.i.IntrepidAdversary.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Island", 270, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Island", 271, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Island", 381, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Jack-o'-Lantern", 254, Rarity.COMMON, mage.cards.j.JackOLantern.class)); - cards.add(new SetCardInfo("Jadar, Ghoulcaller of Nephalia", 108, Rarity.RARE, mage.cards.j.JadarGhoulcallerOfNephalia.class)); - cards.add(new SetCardInfo("Jerren, Corrupted Bishop", 109, Rarity.MYTHIC, mage.cards.j.JerrenCorruptedBishop.class)); - cards.add(new SetCardInfo("Join the Dance", 229, Rarity.UNCOMMON, mage.cards.j.JoinTheDance.class)); - cards.add(new SetCardInfo("Katilda, Dawnhart Prime", 230, Rarity.RARE, mage.cards.k.KatildaDawnhartPrime.class)); - cards.add(new SetCardInfo("Kessig Naturalist", 231, Rarity.UNCOMMON, mage.cards.k.KessigNaturalist.class)); + cards.add(new SetCardInfo("Jadar, Ghoulcaller of Nephalia", 108, Rarity.RARE, mage.cards.j.JadarGhoulcallerOfNephalia.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Jadar, Ghoulcaller of Nephalia", 315, Rarity.RARE, mage.cards.j.JadarGhoulcallerOfNephalia.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Jerren, Corrupted Bishop", 109, Rarity.MYTHIC, mage.cards.j.JerrenCorruptedBishop.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Jerren, Corrupted Bishop", 316, Rarity.MYTHIC, mage.cards.j.JerrenCorruptedBishop.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Join the Dance", 229, Rarity.UNCOMMON, mage.cards.j.JoinTheDance.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Join the Dance", 391, Rarity.UNCOMMON, mage.cards.j.JoinTheDance.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Katilda, Dawnhart Prime", 230, Rarity.RARE, mage.cards.k.KatildaDawnhartPrime.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Katilda, Dawnhart Prime", 309, Rarity.RARE, mage.cards.k.KatildaDawnhartPrime.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kessig Naturalist", 231, Rarity.UNCOMMON, mage.cards.k.KessigNaturalist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kessig Naturalist", 310, Rarity.UNCOMMON, mage.cards.k.KessigNaturalist.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Lambholt Harrier", 145, Rarity.COMMON, mage.cards.l.LambholtHarrier.class)); cards.add(new SetCardInfo("Larder Zombie", 58, Rarity.COMMON, mage.cards.l.LarderZombie.class)); - cards.add(new SetCardInfo("Lier, Disciple of the Drowned", 59, Rarity.MYTHIC, mage.cards.l.LierDiscipleOfTheDrowned.class)); - cards.add(new SetCardInfo("Liesa, Forgotten Archangel", 232, Rarity.RARE, mage.cards.l.LiesaForgottenArchangel.class)); - cards.add(new SetCardInfo("Light Up the Night", 146, Rarity.RARE, mage.cards.l.LightUpTheNight.class)); + cards.add(new SetCardInfo("Leeching Lurker", 345, Rarity.RARE, mage.cards.l.LeechingLurker.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Leeching Lurker", 94, Rarity.RARE, mage.cards.l.LeechingLurker.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lier, Disciple of the Drowned", 313, Rarity.MYTHIC, mage.cards.l.LierDiscipleOfTheDrowned.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lier, Disciple of the Drowned", 59, Rarity.MYTHIC, mage.cards.l.LierDiscipleOfTheDrowned.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Liesa, Forgotten Archangel", 232, Rarity.RARE, mage.cards.l.LiesaForgottenArchangel.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Liesa, Forgotten Archangel", 319, Rarity.RARE, mage.cards.l.LiesaForgottenArchangel.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Light Up the Night", 146, Rarity.RARE, mage.cards.l.LightUpTheNight.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Light Up the Night", 356, Rarity.RARE, mage.cards.l.LightUpTheNight.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Locked in the Cemetery", 60, Rarity.COMMON, mage.cards.l.LockedInTheCemetery.class)); - cards.add(new SetCardInfo("Lord of the Forsaken", 110, Rarity.MYTHIC, mage.cards.l.LordOfTheForsaken.class)); - cards.add(new SetCardInfo("Lord of the Ulvenwald", 231, Rarity.UNCOMMON, mage.cards.l.LordOfTheUlvenwald.class)); + cards.add(new SetCardInfo("Lord of the Forsaken", 110, Rarity.MYTHIC, mage.cards.l.LordOfTheForsaken.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lord of the Forsaken", 346, Rarity.MYTHIC, mage.cards.l.LordOfTheForsaken.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lord of the Ulvenwald", 231, Rarity.UNCOMMON, mage.cards.l.LordOfTheUlvenwald.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lord of the Ulvenwald", 310, Rarity.UNCOMMON, mage.cards.l.LordOfTheUlvenwald.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Loyal Gryff", 26, Rarity.UNCOMMON, mage.cards.l.LoyalGryff.class)); + cards.add(new SetCardInfo("Ludevic, Necrogenius", 233, Rarity.RARE, mage.cards.l.LudevicNecrogenius.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ludevic, Necrogenius", 320, Rarity.RARE, mage.cards.l.LudevicNecrogenius.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Luminous Phantom", 27, Rarity.COMMON, mage.cards.l.LuminousPhantom.class)); cards.add(new SetCardInfo("Lunar Frenzy", 147, Rarity.UNCOMMON, mage.cards.l.LunarFrenzy.class)); cards.add(new SetCardInfo("Lunarch Veteran", 27, Rarity.COMMON, mage.cards.l.LunarchVeteran.class)); - cards.add(new SetCardInfo("Malevolent Hermit", 61, Rarity.RARE, mage.cards.m.MalevolentHermit.class)); - cards.add(new SetCardInfo("Mask of Griselbrand", 111, Rarity.RARE, mage.cards.m.MaskOfGriselbrand.class)); - cards.add(new SetCardInfo("Memory Deluge", 62, Rarity.RARE, mage.cards.m.MemoryDeluge.class)); + cards.add(new SetCardInfo("Malevolent Hermit", 336, Rarity.RARE, mage.cards.m.MalevolentHermit.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Malevolent Hermit", 61, Rarity.RARE, mage.cards.m.MalevolentHermit.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mask of Griselbrand", 111, Rarity.RARE, mage.cards.m.MaskOfGriselbrand.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mask of Griselbrand", 347, Rarity.RARE, mage.cards.m.MaskOfGriselbrand.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Memory Deluge", 337, Rarity.RARE, mage.cards.m.MemoryDeluge.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Memory Deluge", 62, Rarity.RARE, mage.cards.m.MemoryDeluge.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Might of the Old Ways", 189, Rarity.COMMON, mage.cards.m.MightOfTheOldWays.class)); - cards.add(new SetCardInfo("Moonrage Brute", 7, Rarity.RARE, mage.cards.m.MoonrageBrute.class)); + cards.add(new SetCardInfo("Moonrage Brute", 286, Rarity.RARE, mage.cards.m.MoonrageBrute.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Moonrage Brute", 7, Rarity.RARE, mage.cards.m.MoonrageBrute.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Moonrager's Slash", 148, Rarity.COMMON, mage.cards.m.MoonragersSlash.class)); cards.add(new SetCardInfo("Moonsilver Key", 255, Rarity.UNCOMMON, mage.cards.m.MoonsilverKey.class)); - cards.add(new SetCardInfo("Moonveil Regent", 149, Rarity.MYTHIC, mage.cards.m.MoonveilRegent.class)); + cards.add(new SetCardInfo("Moonveil Regent", 149, Rarity.MYTHIC, mage.cards.m.MoonveilRegent.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Moonveil Regent", 357, Rarity.MYTHIC, mage.cards.m.MoonveilRegent.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Morbid Opportunist", 113, Rarity.UNCOMMON, mage.cards.m.MorbidOpportunist.class)); cards.add(new SetCardInfo("Morkrut Behemoth", 114, Rarity.COMMON, mage.cards.m.MorkrutBehemoth.class)); cards.add(new SetCardInfo("Morning Apparition", 28, Rarity.COMMON, mage.cards.m.MorningApparition.class)); cards.add(new SetCardInfo("Mountain", 274, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 275, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 383, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mounted Dreadknight", 150, Rarity.COMMON, mage.cards.m.MountedDreadknight.class)); cards.add(new SetCardInfo("Mourning Patrol", 28, Rarity.COMMON, mage.cards.m.MourningPatrol.class)); cards.add(new SetCardInfo("Mysterious Tome", 63, Rarity.UNCOMMON, mage.cards.m.MysteriousTome.class)); @@ -230,128 +327,199 @@ public final class InnistradMidnightHunt extends ExpansionSet { cards.add(new SetCardInfo("Novice Occultist", 117, Rarity.COMMON, mage.cards.n.NoviceOccultist.class)); cards.add(new SetCardInfo("Obsessive Astronomer", 152, Rarity.UNCOMMON, mage.cards.o.ObsessiveAstronomer.class)); cards.add(new SetCardInfo("Odric's Outrider", 29, Rarity.UNCOMMON, mage.cards.o.OdricsOutrider.class)); - cards.add(new SetCardInfo("Old Stickfingers", 234, Rarity.RARE, mage.cards.o.OldStickfingers.class)); + cards.add(new SetCardInfo("Olag, Ludevic's Hubris", 233, Rarity.RARE, mage.cards.o.OlagLudevicsHubris.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Olag, Ludevic's Hubris", 320, Rarity.RARE, mage.cards.o.OlagLudevicsHubris.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Old Stickfingers", 234, Rarity.RARE, mage.cards.o.OldStickfingers.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Old Stickfingers", 321, Rarity.RARE, mage.cards.o.OldStickfingers.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Olivia's Midnight Ambush", 118, Rarity.COMMON, mage.cards.o.OliviasMidnightAmbush.class)); cards.add(new SetCardInfo("Ominous Roost", 65, Rarity.UNCOMMON, mage.cards.o.OminousRoost.class)); cards.add(new SetCardInfo("Organ Hoarder", 66, Rarity.COMMON, mage.cards.o.OrganHoarder.class)); - cards.add(new SetCardInfo("Ormendahl, the Corrupter", 109, Rarity.MYTHIC, mage.cards.o.OrmendahlTheCorrupter.class)); + cards.add(new SetCardInfo("Ormendahl, the Corrupter", 109, Rarity.MYTHIC, mage.cards.o.OrmendahlTheCorrupter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ormendahl, the Corrupter", 316, Rarity.MYTHIC, mage.cards.o.OrmendahlTheCorrupter.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Otherworldly Gaze", 67, Rarity.COMMON, mage.cards.o.OtherworldlyGaze.class)); - cards.add(new SetCardInfo("Outland Liberator", 190, Rarity.UNCOMMON, mage.cards.o.OutlandLiberator.class)); - cards.add(new SetCardInfo("Overgrown Farmland", 265, Rarity.RARE, mage.cards.o.OvergrownFarmland.class)); + cards.add(new SetCardInfo("Outland Liberator", 190, Rarity.UNCOMMON, mage.cards.o.OutlandLiberator.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Outland Liberator", 303, Rarity.UNCOMMON, mage.cards.o.OutlandLiberator.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Overgrown Farmland", 265, Rarity.RARE, mage.cards.o.OvergrownFarmland.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Overgrown Farmland", 283, Rarity.RARE, mage.cards.o.OvergrownFarmland.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Overwhelmed Archivist", 68, Rarity.UNCOMMON, mage.cards.o.OverwhelmedArchivist.class)); cards.add(new SetCardInfo("Pack's Betrayal", 153, Rarity.COMMON, mage.cards.p.PacksBetrayal.class)); cards.add(new SetCardInfo("Path to the Festival", 191, Rarity.COMMON, mage.cards.p.PathToTheFestival.class)); - cards.add(new SetCardInfo("Patrician Geist", 69, Rarity.RARE, mage.cards.p.PatricianGeist.class)); + cards.add(new SetCardInfo("Patrician Geist", 338, Rarity.RARE, mage.cards.p.PatricianGeist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Patrician Geist", 69, Rarity.RARE, mage.cards.p.PatricianGeist.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Pestilent Wolf", 192, Rarity.COMMON, mage.cards.p.PestilentWolf.class)); cards.add(new SetCardInfo("Phantom Carriage", 70, Rarity.UNCOMMON, mage.cards.p.PhantomCarriage.class)); - cards.add(new SetCardInfo("Pithing Needle", 257, Rarity.RARE, mage.cards.p.PithingNeedle.class)); + cards.add(new SetCardInfo("Pithing Needle", 257, Rarity.RARE, mage.cards.p.PithingNeedle.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Pithing Needle", 378, Rarity.RARE, mage.cards.p.PithingNeedle.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Plains", 268, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); - cards.add(new SetCardInfo("Play with Fire", 154, Rarity.UNCOMMON, mage.cards.p.PlayWithFire.class)); + cards.add(new SetCardInfo("Plains", 269, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Plains", 380, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Play with Fire", 154, Rarity.UNCOMMON, mage.cards.p.PlayWithFire.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Play with Fire", 390, Rarity.UNCOMMON, mage.cards.p.PlayWithFire.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Plummet", 193, Rarity.COMMON, mage.cards.p.Plummet.class)); - cards.add(new SetCardInfo("Poppet Factory", 71, Rarity.MYTHIC, mage.cards.p.PoppetFactory.class)); - cards.add(new SetCardInfo("Poppet Stitcher", 71, Rarity.MYTHIC, mage.cards.p.PoppetStitcher.class)); - cards.add(new SetCardInfo("Primal Adversary", 194, Rarity.MYTHIC, mage.cards.p.PrimalAdversary.class)); + cards.add(new SetCardInfo("Poppet Factory", 339, Rarity.MYTHIC, mage.cards.p.PoppetFactory.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Poppet Factory", 71, Rarity.MYTHIC, mage.cards.p.PoppetFactory.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Poppet Stitcher", 339, Rarity.MYTHIC, mage.cards.p.PoppetStitcher.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Poppet Stitcher", 71, Rarity.MYTHIC, mage.cards.p.PoppetStitcher.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Primal Adversary", 194, Rarity.MYTHIC, mage.cards.p.PrimalAdversary.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Primal Adversary", 363, Rarity.MYTHIC, mage.cards.p.PrimalAdversary.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Purifying Dragon", 155, Rarity.UNCOMMON, mage.cards.p.PurifyingDragon.class)); cards.add(new SetCardInfo("Raze the Effigy", 156, Rarity.COMMON, mage.cards.r.RazeTheEffigy.class)); - cards.add(new SetCardInfo("Reckless Stormseeker", 157, Rarity.RARE, mage.cards.r.RecklessStormseeker.class)); - cards.add(new SetCardInfo("Rem Karolus, Stalwart Slayer", 235, Rarity.RARE, mage.cards.r.RemKarolusStalwartSlayer.class)); + cards.add(new SetCardInfo("Reckless Stormseeker", 157, Rarity.RARE, mage.cards.r.RecklessStormseeker.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Reckless Stormseeker", 294, Rarity.RARE, mage.cards.r.RecklessStormseeker.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rem Karolus, Stalwart Slayer", 235, Rarity.RARE, mage.cards.r.RemKarolusStalwartSlayer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rem Karolus, Stalwart Slayer", 322, Rarity.RARE, mage.cards.r.RemKarolusStalwartSlayer.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Return to Nature", 195, Rarity.COMMON, mage.cards.r.ReturnToNature.class)); cards.add(new SetCardInfo("Revenge of the Drowned", 72, Rarity.COMMON, mage.cards.r.RevengeOfTheDrowned.class)); cards.add(new SetCardInfo("Rise of the Ants", 196, Rarity.UNCOMMON, mage.cards.r.RiseOfTheAnts.class)); - cards.add(new SetCardInfo("Rite of Harmony", 236, Rarity.RARE, mage.cards.r.RiteOfHarmony.class)); + cards.add(new SetCardInfo("Rite of Harmony", 236, Rarity.RARE, mage.cards.r.RiteOfHarmony.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rite of Harmony", 374, Rarity.RARE, mage.cards.r.RiteOfHarmony.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Rite of Oblivion", 237, Rarity.UNCOMMON, mage.cards.r.RiteOfOblivion.class)); cards.add(new SetCardInfo("Ritual Guardian", 30, Rarity.COMMON, mage.cards.r.RitualGuardian.class)); cards.add(new SetCardInfo("Ritual of Hope", 31, Rarity.UNCOMMON, mage.cards.r.RitualOfHope.class)); - cards.add(new SetCardInfo("Rockfall Vale", 266, Rarity.RARE, mage.cards.r.RockfallVale.class)); + cards.add(new SetCardInfo("Rockfall Vale", 266, Rarity.RARE, mage.cards.r.RockfallVale.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rockfall Vale", 284, Rarity.RARE, mage.cards.r.RockfallVale.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Rootcoil Creeper", 238, Rarity.UNCOMMON, mage.cards.r.RootcoilCreeper.class)); cards.add(new SetCardInfo("Rotten Reunion", 119, Rarity.COMMON, mage.cards.r.RottenReunion.class)); cards.add(new SetCardInfo("Sacred Fire", 239, Rarity.UNCOMMON, mage.cards.s.SacredFire.class)); - cards.add(new SetCardInfo("Saryth, the Viper's Fang", 197, Rarity.RARE, mage.cards.s.SarythTheVipersFang.class)); - cards.add(new SetCardInfo("Seafaring Werewolf", 80, Rarity.RARE, mage.cards.s.SeafaringWerewolf.class)); + cards.add(new SetCardInfo("Saryth, the Viper's Fang", 197, Rarity.RARE, mage.cards.s.SarythTheVipersFang.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Saryth, the Viper's Fang", 304, Rarity.RARE, mage.cards.s.SarythTheVipersFang.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Seafaring Werewolf", 288, Rarity.RARE, mage.cards.s.SeafaringWerewolf.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Seafaring Werewolf", 80, Rarity.RARE, mage.cards.s.SeafaringWerewolf.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Search Party Captain", 32, Rarity.COMMON, mage.cards.s.SearchPartyCaptain.class)); cards.add(new SetCardInfo("Seasoned Cathar", 2, Rarity.UNCOMMON, mage.cards.s.SeasonedCathar.class)); cards.add(new SetCardInfo("Secrets of the Key", 73, Rarity.COMMON, mage.cards.s.SecretsOfTheKey.class)); cards.add(new SetCardInfo("Seize the Storm", 158, Rarity.UNCOMMON, mage.cards.s.SeizeTheStorm.class)); cards.add(new SetCardInfo("Shadowbeast Sighting", 198, Rarity.COMMON, mage.cards.s.ShadowbeastSighting.class)); - cards.add(new SetCardInfo("Shady Traveler", 120, Rarity.COMMON, mage.cards.s.ShadyTraveler.class)); - cards.add(new SetCardInfo("Shipwreck Marsh", 267, Rarity.RARE, mage.cards.s.ShipwreckMarsh.class)); + cards.add(new SetCardInfo("Shady Traveler", 120, Rarity.COMMON, mage.cards.s.ShadyTraveler.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shady Traveler", 291, Rarity.COMMON, mage.cards.s.ShadyTraveler.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shipwreck Marsh", 267, Rarity.RARE, mage.cards.s.ShipwreckMarsh.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shipwreck Marsh", 285, Rarity.RARE, mage.cards.s.ShipwreckMarsh.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Shipwreck Sifters", 74, Rarity.COMMON, mage.cards.s.ShipwreckSifters.class)); cards.add(new SetCardInfo("Siege Zombie", 121, Rarity.COMMON, mage.cards.s.SiegeZombie.class)); - cards.add(new SetCardInfo("Sigarda's Splendor", 33, Rarity.RARE, mage.cards.s.SigardasSplendor.class)); - cards.add(new SetCardInfo("Sigarda, Champion of Light", 240, Rarity.MYTHIC, mage.cards.s.SigardaChampionOfLight.class)); - cards.add(new SetCardInfo("Sigardian Savior", 34, Rarity.MYTHIC, mage.cards.s.SigardianSavior.class)); + cards.add(new SetCardInfo("Sigarda's Splendor", 33, Rarity.RARE, mage.cards.s.SigardasSplendor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sigarda's Splendor", 330, Rarity.RARE, mage.cards.s.SigardasSplendor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sigarda, Champion of Light", 240, Rarity.MYTHIC, mage.cards.s.SigardaChampionOfLight.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sigarda, Champion of Light", 323, Rarity.MYTHIC, mage.cards.s.SigardaChampionOfLight.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sigardian Savior", 331, Rarity.MYTHIC, mage.cards.s.SigardianSavior.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sigardian Savior", 34, Rarity.MYTHIC, mage.cards.s.SigardianSavior.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Silver Bolt", 258, Rarity.COMMON, mage.cards.s.SilverBolt.class)); - cards.add(new SetCardInfo("Siphon Insight", 241, Rarity.RARE, mage.cards.s.SiphonInsight.class)); + cards.add(new SetCardInfo("Siphon Insight", 241, Rarity.RARE, mage.cards.s.SiphonInsight.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Siphon Insight", 375, Rarity.RARE, mage.cards.s.SiphonInsight.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Skaab Wrangler", 75, Rarity.UNCOMMON, mage.cards.s.SkaabWrangler.class)); - cards.add(new SetCardInfo("Slaughter Specialist", 122, Rarity.RARE, mage.cards.s.SlaughterSpecialist.class)); - cards.add(new SetCardInfo("Slogurk, the Overslime", 242, Rarity.RARE, mage.cards.s.SlogurkTheOverslime.class)); - cards.add(new SetCardInfo("Sludge Monster", 76, Rarity.RARE, mage.cards.s.SludgeMonster.class)); - cards.add(new SetCardInfo("Smoldering Egg", 159, Rarity.RARE, mage.cards.s.SmolderingEgg.class)); + cards.add(new SetCardInfo("Slaughter Specialist", 122, Rarity.RARE, mage.cards.s.SlaughterSpecialist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Slaughter Specialist", 349, Rarity.RARE, mage.cards.s.SlaughterSpecialist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Slogurk, the Overslime", 242, Rarity.RARE, mage.cards.s.SlogurkTheOverslime.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Slogurk, the Overslime", 324, Rarity.RARE, mage.cards.s.SlogurkTheOverslime.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sludge Monster", 340, Rarity.RARE, mage.cards.s.SludgeMonster.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sludge Monster", 76, Rarity.RARE, mage.cards.s.SludgeMonster.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Smoldering Egg", 159, Rarity.RARE, mage.cards.s.SmolderingEgg.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Smoldering Egg", 358, Rarity.RARE, mage.cards.s.SmolderingEgg.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Snarling Wolf", 199, Rarity.COMMON, mage.cards.s.SnarlingWolf.class)); cards.add(new SetCardInfo("Soul-Guide Gryff", 35, Rarity.COMMON, mage.cards.s.SoulGuideGryff.class)); - cards.add(new SetCardInfo("Spectral Adversary", 77, Rarity.MYTHIC, mage.cards.s.SpectralAdversary.class)); - cards.add(new SetCardInfo("Spellrune Howler", 160, Rarity.UNCOMMON, mage.cards.s.SpellruneHowler.class)); - cards.add(new SetCardInfo("Spellrune Painter", 160, Rarity.UNCOMMON, mage.cards.s.SpellrunePainter.class)); - cards.add(new SetCardInfo("Stalking Predator", 120, Rarity.COMMON, mage.cards.s.StalkingPredator.class)); + cards.add(new SetCardInfo("Spectral Adversary", 341, Rarity.MYTHIC, mage.cards.s.SpectralAdversary.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Spectral Adversary", 77, Rarity.MYTHIC, mage.cards.s.SpectralAdversary.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Spellrune Howler", 160, Rarity.UNCOMMON, mage.cards.s.SpellruneHowler.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Spellrune Howler", 295, Rarity.UNCOMMON, mage.cards.s.SpellruneHowler.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Spellrune Painter", 160, Rarity.UNCOMMON, mage.cards.s.SpellrunePainter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Spellrune Painter", 295, Rarity.UNCOMMON, mage.cards.s.SpellrunePainter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Stalking Predator", 120, Rarity.COMMON, mage.cards.s.StalkingPredator.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Stalking Predator", 291, Rarity.COMMON, mage.cards.s.StalkingPredator.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Startle", 78, Rarity.COMMON, mage.cards.s.Startle.class)); cards.add(new SetCardInfo("Stolen Vitality", 161, Rarity.COMMON, mage.cards.s.StolenVitality.class)); cards.add(new SetCardInfo("Storm Skreelix", 243, Rarity.UNCOMMON, mage.cards.s.StormSkreelix.class)); - cards.add(new SetCardInfo("Storm the Festival", 200, Rarity.RARE, mage.cards.s.StormTheFestival.class)); - cards.add(new SetCardInfo("Storm-Charged Slasher", 157, Rarity.RARE, mage.cards.s.StormChargedSlasher.class)); + cards.add(new SetCardInfo("Storm the Festival", 200, Rarity.RARE, mage.cards.s.StormTheFestival.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Storm the Festival", 364, Rarity.RARE, mage.cards.s.StormTheFestival.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Storm-Charged Slasher", 157, Rarity.RARE, mage.cards.s.StormChargedSlasher.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Storm-Charged Slasher", 294, Rarity.RARE, mage.cards.s.StormChargedSlasher.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Stormrider Spirit", 79, Rarity.COMMON, mage.cards.s.StormriderSpirit.class)); cards.add(new SetCardInfo("Strangling Grasp", 126, Rarity.UNCOMMON, mage.cards.s.StranglingGrasp.class)); cards.add(new SetCardInfo("Stromkirk Bloodthief", 123, Rarity.UNCOMMON, mage.cards.s.StromkirkBloodthief.class)); cards.add(new SetCardInfo("Stuffed Bear", 259, Rarity.COMMON, mage.cards.s.StuffedBear.class)); cards.add(new SetCardInfo("Sungold Barrage", 36, Rarity.COMMON, mage.cards.s.SungoldBarrage.class)); - cards.add(new SetCardInfo("Sungold Sentinel", 37, Rarity.RARE, mage.cards.s.SungoldSentinel.class)); + cards.add(new SetCardInfo("Sungold Sentinel", 332, Rarity.RARE, mage.cards.s.SungoldSentinel.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sungold Sentinel", 37, Rarity.RARE, mage.cards.s.SungoldSentinel.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sunrise Cavalier", 244, Rarity.UNCOMMON, mage.cards.s.SunriseCavalier.class)); cards.add(new SetCardInfo("Sunset Revelry", 38, Rarity.UNCOMMON, mage.cards.s.SunsetRevelry.class)); - cards.add(new SetCardInfo("Sunstreak Phoenix", 162, Rarity.MYTHIC, mage.cards.s.SunstreakPhoenix.class)); - cards.add(new SetCardInfo("Suspicious Stowaway", 80, Rarity.RARE, mage.cards.s.SuspiciousStowaway.class)); + cards.add(new SetCardInfo("Sunstreak Phoenix", 162, Rarity.MYTHIC, mage.cards.s.SunstreakPhoenix.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sunstreak Phoenix", 359, Rarity.MYTHIC, mage.cards.s.SunstreakPhoenix.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Suspicious Stowaway", 288, Rarity.RARE, mage.cards.s.SuspiciousStowaway.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Suspicious Stowaway", 80, Rarity.RARE, mage.cards.s.SuspiciousStowaway.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Swamp", 272, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); - cards.add(new SetCardInfo("Tainted Adversary", 124, Rarity.MYTHIC, mage.cards.t.TaintedAdversary.class)); + cards.add(new SetCardInfo("Swamp", 273, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 382, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tainted Adversary", 124, Rarity.MYTHIC, mage.cards.t.TaintedAdversary.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tainted Adversary", 350, Rarity.MYTHIC, mage.cards.t.TaintedAdversary.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Tapping at the Window", 201, Rarity.COMMON, mage.cards.t.TappingAtTheWindow.class)); - cards.add(new SetCardInfo("Tavern Ruffian", 163, Rarity.COMMON, mage.cards.t.TavernRuffian.class)); - cards.add(new SetCardInfo("Tavern Smasher", 163, Rarity.COMMON, mage.cards.t.TavernSmasher.class)); - cards.add(new SetCardInfo("Teferi, Who Slows the Sunset", 245, Rarity.MYTHIC, mage.cards.t.TeferiWhoSlowsTheSunset.class)); - cards.add(new SetCardInfo("The Meathook Massacre", 112, Rarity.MYTHIC, mage.cards.t.TheMeathookMassacre.class)); + cards.add(new SetCardInfo("Tavern Ruffian", 163, Rarity.COMMON, mage.cards.t.TavernRuffian.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tavern Ruffian", 296, Rarity.COMMON, mage.cards.t.TavernRuffian.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tavern Smasher", 163, Rarity.COMMON, mage.cards.t.TavernSmasher.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tavern Smasher", 296, Rarity.COMMON, mage.cards.t.TavernSmasher.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Teferi, Who Slows the Sunset", 245, Rarity.MYTHIC, mage.cards.t.TeferiWhoSlowsTheSunset.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Teferi, Who Slows the Sunset", 280, Rarity.MYTHIC, mage.cards.t.TeferiWhoSlowsTheSunset.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Celestus", 252, Rarity.RARE, mage.cards.t.TheCelestus.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Celestus", 377, Rarity.RARE, mage.cards.t.TheCelestus.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Meathook Massacre", 112, Rarity.MYTHIC, mage.cards.t.TheMeathookMassacre.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Meathook Massacre", 348, Rarity.MYTHIC, mage.cards.t.TheMeathookMassacre.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Thermo-Alchemist", 164, Rarity.UNCOMMON, mage.cards.t.ThermoAlchemist.class)); cards.add(new SetCardInfo("Thraben Exorcism", 39, Rarity.COMMON, mage.cards.t.ThrabenExorcism.class)); cards.add(new SetCardInfo("Timberland Guide", 202, Rarity.COMMON, mage.cards.t.TimberlandGuide.class)); - cards.add(new SetCardInfo("Tireless Hauler", 203, Rarity.COMMON, mage.cards.t.TirelessHauler.class)); - cards.add(new SetCardInfo("Tovolar's Huntmaster", 204, Rarity.RARE, mage.cards.t.TovolarsHuntmaster.class)); - cards.add(new SetCardInfo("Tovolar's Packleader", 204, Rarity.RARE, mage.cards.t.TovolarsPackleader.class)); - cards.add(new SetCardInfo("Triskaidekaphile", 81, Rarity.RARE, mage.cards.t.Triskaidekaphile.class)); + cards.add(new SetCardInfo("Tireless Hauler", 203, Rarity.COMMON, mage.cards.t.TirelessHauler.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tireless Hauler", 305, Rarity.COMMON, mage.cards.t.TirelessHauler.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tovolar's Huntmaster", 204, Rarity.RARE, mage.cards.t.TovolarsHuntmaster.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tovolar's Huntmaster", 306, Rarity.RARE, mage.cards.t.TovolarsHuntmaster.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tovolar's Packleader", 204, Rarity.RARE, mage.cards.t.TovolarsPackleader.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tovolar's Packleader", 306, Rarity.RARE, mage.cards.t.TovolarsPackleader.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tovolar, Dire Overlord", 246, Rarity.RARE, mage.cards.t.TovolarDireOverlord.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tovolar, Dire Overlord", 311, Rarity.RARE, mage.cards.t.TovolarDireOverlord.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tovolar, the Midnight Scourge", 246, Rarity.RARE, mage.cards.t.TovolarTheMidnightScourge.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tovolar, the Midnight Scourge", 311, Rarity.RARE, mage.cards.t.TovolarTheMidnightScourge.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Triskaidekaphile", 342, Rarity.RARE, mage.cards.t.Triskaidekaphile.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Triskaidekaphile", 386, Rarity.RARE, mage.cards.t.Triskaidekaphile.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Triskaidekaphile", 81, Rarity.RARE, mage.cards.t.Triskaidekaphile.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Turn the Earth", 205, Rarity.UNCOMMON, mage.cards.t.TurnTheEarth.class)); cards.add(new SetCardInfo("Unblinking Observer", 82, Rarity.COMMON, mage.cards.u.UnblinkingObserver.class)); - cards.add(new SetCardInfo("Unnatural Growth", 206, Rarity.RARE, mage.cards.u.UnnaturalGrowth.class)); + cards.add(new SetCardInfo("Unnatural Growth", 206, Rarity.RARE, mage.cards.u.UnnaturalGrowth.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Unnatural Growth", 365, Rarity.RARE, mage.cards.u.UnnaturalGrowth.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Unnatural Moonrise", 247, Rarity.UNCOMMON, mage.cards.u.UnnaturalMoonrise.class)); cards.add(new SetCardInfo("Unruly Mob", 40, Rarity.COMMON, mage.cards.u.UnrulyMob.class)); - cards.add(new SetCardInfo("Untamed Pup", 187, Rarity.UNCOMMON, mage.cards.u.UntamedPup.class)); - cards.add(new SetCardInfo("Vadrik, Astral Archmage", 248, Rarity.RARE, mage.cards.v.VadrikAstralArchmage.class)); + cards.add(new SetCardInfo("Untamed Pup", 187, Rarity.UNCOMMON, mage.cards.u.UntamedPup.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Untamed Pup", 302, Rarity.UNCOMMON, mage.cards.u.UntamedPup.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Vadrik, Astral Archmage", 248, Rarity.RARE, mage.cards.v.VadrikAstralArchmage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Vadrik, Astral Archmage", 325, Rarity.RARE, mage.cards.v.VadrikAstralArchmage.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Vampire Interloper", 125, Rarity.COMMON, mage.cards.v.VampireInterloper.class)); cards.add(new SetCardInfo("Vampire Socialite", 249, Rarity.UNCOMMON, mage.cards.v.VampireSocialite.class)); - cards.add(new SetCardInfo("Vanquish the Horde", 41, Rarity.RARE, mage.cards.v.VanquishTheHorde.class)); + cards.add(new SetCardInfo("Vanquish the Horde", 333, Rarity.RARE, mage.cards.v.VanquishTheHorde.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Vanquish the Horde", 41, Rarity.RARE, mage.cards.v.VanquishTheHorde.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Vengeful Strangler", 126, Rarity.UNCOMMON, mage.cards.v.VengefulStrangler.class)); - cards.add(new SetCardInfo("Village Reavers", 165, Rarity.UNCOMMON, mage.cards.v.VillageReavers.class)); - cards.add(new SetCardInfo("Village Watch", 165, Rarity.UNCOMMON, mage.cards.v.VillageWatch.class)); + cards.add(new SetCardInfo("Village Reavers", 165, Rarity.UNCOMMON, mage.cards.v.VillageReavers.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Village Reavers", 297, Rarity.UNCOMMON, mage.cards.v.VillageReavers.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Village Watch", 165, Rarity.UNCOMMON, mage.cards.v.VillageWatch.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Village Watch", 297, Rarity.UNCOMMON, mage.cards.v.VillageWatch.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Vivisection", 83, Rarity.UNCOMMON, mage.cards.v.Vivisection.class)); cards.add(new SetCardInfo("Voldaren Ambusher", 166, Rarity.UNCOMMON, mage.cards.v.VoldarenAmbusher.class)); cards.add(new SetCardInfo("Voldaren Stinger", 167, Rarity.COMMON, mage.cards.v.VoldarenStinger.class)); cards.add(new SetCardInfo("Waildrifter", 55, Rarity.COMMON, mage.cards.w.Waildrifter.class)); - cards.add(new SetCardInfo("Wake to Slaughter", 250, Rarity.RARE, mage.cards.w.WakeToSlaughter.class)); - cards.add(new SetCardInfo("Willow Geist", 207, Rarity.RARE, mage.cards.w.WillowGeist.class)); - cards.add(new SetCardInfo("Wing Shredder", 169, Rarity.COMMON, mage.cards.w.WingShredder.class)); + cards.add(new SetCardInfo("Wake to Slaughter", 250, Rarity.RARE, mage.cards.w.WakeToSlaughter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Wake to Slaughter", 376, Rarity.RARE, mage.cards.w.WakeToSlaughter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Willow Geist", 207, Rarity.RARE, mage.cards.w.WillowGeist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Willow Geist", 366, Rarity.RARE, mage.cards.w.WillowGeist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Wing Shredder", 169, Rarity.COMMON, mage.cards.w.WingShredder.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Wing Shredder", 298, Rarity.COMMON, mage.cards.w.WingShredder.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Winterthorn Blessing", 251, Rarity.UNCOMMON, mage.cards.w.WinterthornBlessing.class)); - cards.add(new SetCardInfo("Wrenn and Seven", 208, Rarity.MYTHIC, mage.cards.w.WrennAndSeven.class)); - - cards.removeIf(setCardInfo -> unfinished.contains(setCardInfo.getName())); // remove when mechanic is fully implemented + cards.add(new SetCardInfo("Wrenn and Seven", 208, Rarity.MYTHIC, mage.cards.w.WrennAndSeven.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Wrenn and Seven", 278, Rarity.MYTHIC, mage.cards.w.WrennAndSeven.class, NON_FULL_USE_VARIOUS)); } - /* + + // add common double faced card to booster + @Override + protected void addDoubleFace(List booster) { + addToBooster(booster, getSpecialCardsByRarity(Rarity.COMMON)); + } + @Override public BoosterCollator createCollator() { return new InnistradMidnightHuntCollator(); } - */ } // Booster collation info from https://www.lethe.xyz/mtg/collation/mid.html diff --git a/Mage.Sets/src/mage/sets/Ixalan.java b/Mage.Sets/src/mage/sets/Ixalan.java index 6846697d7db..28001ce859f 100644 --- a/Mage.Sets/src/mage/sets/Ixalan.java +++ b/Mage.Sets/src/mage/sets/Ixalan.java @@ -1,9 +1,16 @@ package mage.sets; import mage.cards.ExpansionSet; +import mage.collation.BoosterCollator; +import mage.collation.BoosterStructure; +import mage.collation.CardRun; +import mage.collation.RarityConfiguration; import mage.constants.Rarity; import mage.constants.SetType; +import java.util.ArrayList; +import java.util.List; + /** * @author fireshoes */ @@ -24,7 +31,7 @@ public final class Ixalan extends ExpansionSet { this.numBoosterCommon = 10; this.numBoosterUncommon = 3; this.numBoosterRare = 1; - this.ratioBoosterMythic = 8; + this.ratioBoosterMythic = 9.4; this.numBoosterDoubleFaced = -1; this.maxCardNumberInBooster = 279; @@ -328,4 +335,96 @@ public final class Ixalan extends ExpansionSet { cards.add(new SetCardInfo("Wind Strider", 88, Rarity.COMMON, mage.cards.w.WindStrider.class)); cards.add(new SetCardInfo("Woodland Stream", 284, Rarity.COMMON, mage.cards.w.WoodlandStream.class)); } -} \ No newline at end of file + + @Override + public BoosterCollator createCollator() { + return new IxalanCollator(); + } +} + +// Booster collation info from https://www.lethe.xyz/mtg/collation/xln.html +// Using USA collation for all rarities +// Foil rare sheet used for regular rares as regular rare sheet is not known +class IxalanCollator implements BoosterCollator { + private final CardRun commonA = new CardRun(true, "142", "20", "102", "169", "23", "114", "134", "21", "123", "146", "28", "115", "160", "6", "112", "158", "36", "107", "152", "40", "101", "164", "43", "98", "166", "21", "122", "142", "37", "102", "141", "20", "113", "146", "38", "114", "134", "23", "115", "144", "28", "92", "169", "29", "123", "158", "6", "112", "152", "36", "98", "160", "40", "107", "164", "43", "101", "144", "37", "122", "141", "38", "113", "166", "29", "92"); + private final CardRun commonB = new CardRun(true, "64", "182", "78", "194", "75", "198", "76", "193", "77", "202", "52", "181", "73", "209", "68", "192", "65", "178", "85", "204", "69", "186", "77", "194", "64", "193", "78", "202", "75", "198", "73", "182", "76", "181", "85", "178", "52", "204", "68", "209", "65", "192", "69", "186", "78", "202", "75", "198", "64", "182", "76", "181", "73", "193", "65", "194", "77", "204", "69", "178", "85", "209", "68", "192", "52", "186"); + private final CardRun commonC1 = new CardRun(true, "125", "96", "58", "240", "41", "103", "167", "80", "259", "72", "89", "242", "127", "195", "175", "211", "244", "147", "18", "163", "125", "31", "233", "81", "41", "88", "240", "96", "145", "103", "11", "72", "259", "167", "58", "127", "242", "89", "163", "80", "195", "31", "175", "200", "244", "147", "18", "211", "11", "233", "200", "81", "32", "88", "145"); + private final CardRun commonC2 = new CardRun(true, "139", "124", "8", "190", "239", "172", "47", "180", "8", "95", "148", "87", "177", "25", "239", "168", "183", "124", "53", "26", "139", "190", "105", "172", "32", "47", "180", "148", "95", "177", "25", "239", "168", "105", "26", "183", "172", "53", "190", "8", "124", "139", "25", "177", "95", "47", "87", "148", "26", "183", "87", "105", "168", "53", "180"); + private final CardRun uncommonA = new CardRun(true, "216", "83", "116", "247", "151", "196", "16", "79", "236", "162", "188", "35", "56", "110", "245", "174", "214", "39", "108", "237", "171", "212", "30", "67", "128", "218", "150", "216", "83", "131", "254", "140", "207", "9", "51", "119", "241", "162", "196", "63", "109", "247", "151", "214", "15", "62", "108", "237", "157", "212", "79", "116", "236", "174", "188", "39", "56", "110", "245", "150", "216", "67", "131", "254", "171", "196", "30", "83", "128", "218", "140", "16", "51", "119", "241", "162", "207", "9", "63", "109", "245", "150", "35", "56", "110", "247", "151", "214", "15", "62", "108", "237", "157", "39", "79", "109", "254", "174", "188", "16", "51", "131", "241", "140", "35", "63", "119", "236", "171", "212", "30", "67", "116", "218", "207", "9", "62", "128", "157", "15"); + private final CardRun uncommonB = new CardRun(true, "111", "7", "50", "187", "220", "1", "226", "91", "45", "208", "149", "121", "4", "210", "10", "228", "155", "229", "97", "86", "197", "153", "130", "12", "48", "176", "221", "133", "17", "100", "84", "201", "138", "111", "7", "70", "205", "225", "143", "14", "91", "50", "187", "149", "121", "4", "45", "208", "226", "155", "1", "219", "86", "210", "153", "97", "258", "84", "176", "220", "133", "17", "229", "48", "197", "138", "100", "10", "70", "201", "228", "143", "14", "111", "50", "205", "153", "130", "12", "84", "187", "219", "155", "1", "91", "45", "208", "226", "97", "4", "86", "197", "221", "149", "7", "121", "228", "210", "225", "258", "10", "48", "176", "220", "133", "17", "100", "229", "138", "221", "12", "258", "201", "219", "14", "225", "130", "70", "205", "143"); + private final CardRun rare = new CardRun(true, "137", "46", "213", "223", "3", "120", "57", "19", "248", "165", "93", "154", "49", "215", "227", "44", "132", "255", "24", "251", "224", "99", "184", "54", "2", "238", "60", "135", "256", "27", "252", "126", "106", "185", "55", "5", "246", "66", "136", "257", "203", "253", "231", "33", "189", "82", "13", "71", "94", "248", "46", "206", "170", "61", "34", "199", "59", "203", "93", "104", "251", "49", "129", "184", "57", "42", "117", "19", "206", "99", "159", "252", "54", "2", "185", "230", "55", "255", "24", "132", "106", "161", "253", "223", "5", "189", "118", "156", "256", "27", "135", "213", "179", "33", "227", "13", "199", "120", "137", "257", "165", "136", "215", "217", "34", "238", "82", "126", "232", "154", "71", "170", "129", "61", "222", "42", "246", "118", "59", "156", "117"); + private final CardRun rareDFC = new CardRun(false, "22", "74", "90", "173", "191", "234", "235", "243", "249", "250"); + private final CardRun land = new CardRun(false, "260", "261", "262", "263", "264", "265", "266", "267", "268", "269", "270", "271", "272", "273", "274", "275", "276", "277", "278", "279"); + + private final BoosterStructure AABBC1C1C1C1C1C1 = new BoosterStructure( + commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AAABBC1C1C1C1C1 = new BoosterStructure( + commonA, commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AAAABBC2C2C2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, + commonC2, commonC2, commonC2, commonC2 + ); + private final BoosterStructure AAAABBBBC2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, commonB, commonB, + commonC2, commonC2 + ); + private final BoosterStructure AAB = new BoosterStructure(uncommonA, uncommonA, uncommonB); + private final BoosterStructure ABB = new BoosterStructure(uncommonA, uncommonB, uncommonB); + private final BoosterStructure R1 = new BoosterStructure(rare); + private final BoosterStructure R2 = new BoosterStructure(rareDFC); + private final BoosterStructure L1 = new BoosterStructure(land); + + // In order for equal numbers of each common to exist, the average booster must contain: + // 3.27 A commons (36 / 11) + // 2.18 B commons (24 / 11) + // 2.73 C1 commons (30 / 11, or 60 / 11 in each C1 booster) + // 1.82 C2 commons (20 / 11, or 40 / 11 in each C2 booster) + // These numbers are the same for all sets with 101 commons in A/B/C1/C2 print runs + // and with 10 common slots per booster + private final RarityConfiguration commonRuns = new RarityConfiguration( + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBBBC2C2, + AAAABBBBC2C2 + ); + private final RarityConfiguration uncommonRuns = new RarityConfiguration(AAB, ABB); + private final RarityConfiguration rareRuns = new RarityConfiguration(R1, R1, R1, R1, R1, R1, R2); + private final RarityConfiguration landRuns = new RarityConfiguration(L1); + + @Override + public List makeBooster() { + List booster = new ArrayList<>(); + booster.addAll(commonRuns.getNext().makeRun()); + booster.addAll(uncommonRuns.getNext().makeRun()); + booster.addAll(rareRuns.getNext().makeRun()); + booster.addAll(landRuns.getNext().makeRun()); + return booster; + } +} diff --git a/Mage.Sets/src/mage/sets/JourneyIntoNyx.java b/Mage.Sets/src/mage/sets/JourneyIntoNyx.java index 7c41e6f6f1e..4a5de72bd4b 100644 --- a/Mage.Sets/src/mage/sets/JourneyIntoNyx.java +++ b/Mage.Sets/src/mage/sets/JourneyIntoNyx.java @@ -1,10 +1,16 @@ - package mage.sets; import mage.cards.ExpansionSet; +import mage.collation.BoosterCollator; +import mage.collation.BoosterStructure; +import mage.collation.CardRun; +import mage.collation.RarityConfiguration; import mage.constants.Rarity; import mage.constants.SetType; +import java.util.ArrayList; +import java.util.List; + /** * * @author LevelX2 @@ -195,4 +201,56 @@ public final class JourneyIntoNyx extends ExpansionSet { cards.add(new SetCardInfo("Worst Fears", 87, Rarity.MYTHIC, mage.cards.w.WorstFears.class)); } + @Override + public BoosterCollator createCollator() { + return new JourneyIntoNyxCollator(); + } +} + +// Booster collation info from https://www.lethe.xyz/mtg/collation/jou.html +// Using USA collation for common/uncommon, rare collation inferred from other sets +class JourneyIntoNyxCollator implements BoosterCollator { + private final CardRun commonA = new CardRun(true, "14", "133", "71", "38", "103", "13", "124", "79", "33", "105", "4", "123", "81", "39", "109", "11", "133", "69", "41", "110", "3", "127", "71", "30", "103", "9", "123", "78", "38", "111", "2", "124", "69", "32", "102", "14", "130", "81", "41", "105", "3", "131", "77", "39", "111", "11", "129", "78", "33", "102", "9", "130", "73", "32", "109", "4", "127", "77", "13", "79", "129", "2", "131", "73", "30", "110"); + private final CardRun commonB = new CardRun(true, "19", "45", "137", "96", "53", "99", "52", "114", "64", "27", "49", "134", "90", "67", "28", "53", "120", "95", "63", "20", "56", "136", "97", "60", "17", "45", "134", "99", "61", "19", "57", "139", "96", "67", "27", "56", "137", "90", "64", "17", "52", "120", "114", "63", "28", "49", "139", "97", "61", "20", "57", "136", "95", "60"); + private final CardRun uncommonA = new CardRun(true, "147", "84", "119", "159", "34", "107", "29", "116", "118", "22", "148", "40", "85", "23", "149", "108", "26", "86", "159", "44", "158", "58", "148", "76", "24", "35", "147", "125", "80", "26", "34", "104", "113", "132", "44", "118", "83", "29", "43", "21", "125", "23", "86", "104", "158", "107", "85", "149", "113", "58", "117", "84", "116", "119", "83", "40", "22", "35", "132", "24", "80", "43", "108", "21", "76", "117"); + private final CardRun uncommonB = new CardRun(true, "138", "72", "55", "161", "10", "54", "70", "48", "142", "88", "156", "59", "141", "98", "160", "62", "101", "138", "161", "54", "72", "47", "153", "18", "55", "59", "91", "144", "5", "62", "88", "16", "48", "98", "153", "141", "92", "144", "157", "101", "47", "143", "10", "160", "70", "16", "91", "156", "142", "5", "157", "92", "18", "143"); + private final CardRun rare = new CardRun(false, "1", "6", "7", "8", "15", "25", "31", "36", "37", "42", "46", "51", "65", "66", "68", "74", "75", "82", "89", "93", "94", "100", "112", "115", "121", "122", "126", "128", "135", "140", "155", "162", "163", "164", "165"); + private final CardRun mythic = new CardRun(false, "12", "50", "87", "106", "145", "146", "150", "151", "152", "154"); + private final CardRun land = new CardRun(false, "THS_230", "THS_231", "THS_232", "THS_233", "THS_234", "THS_235", "THS_236", "THS_237", "THS_238", "THS_239", "THS_240", "THS_241", "THS_242", "THS_243", "THS_244", "THS_245", "THS_246", "THS_247", "THS_248", "THS_249"); + + private final BoosterStructure AAAAABBBBB = new BoosterStructure( + commonA, commonA, commonA, commonA, commonA, + commonB, commonB, commonB, commonB, commonB + ); + private final BoosterStructure AAAAAABBBB = new BoosterStructure( + commonA, commonA, commonA, commonA, commonA, commonA, + commonB, commonB, commonB, commonB + ); + private final BoosterStructure AAB = new BoosterStructure(uncommonA, uncommonA, uncommonB); + private final BoosterStructure ABB = new BoosterStructure(uncommonA, uncommonB, uncommonB); + private final BoosterStructure R1 = new BoosterStructure(rare); + private final BoosterStructure M1 = new BoosterStructure(mythic); + private final BoosterStructure L1 = new BoosterStructure(land); + + private final RarityConfiguration commonRuns = new RarityConfiguration(AAAAABBBBB, AAAAAABBBB); + // In order for equal numbers of each uncommon to exist, the average booster must contain: + // 1.65 A uncommons (33 / 20) + // 1.35 B uncommons (27 / 20) + // These numbers are the same for all sets with 60 uncommons in asymmetrical A/B print runs + private final RarityConfiguration uncommonRuns = new RarityConfiguration( + AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, + ABB, ABB, ABB, ABB, ABB, ABB, ABB + ); + private final RarityConfiguration rareRuns = new RarityConfiguration(R1, R1, R1, R1, R1, R1, R1, M1); + private final RarityConfiguration landRuns = new RarityConfiguration(L1); + + @Override + public List makeBooster() { + List booster = new ArrayList<>(); + booster.addAll(commonRuns.getNext().makeRun()); + booster.addAll(uncommonRuns.getNext().makeRun()); + booster.addAll(rareRuns.getNext().makeRun()); + booster.addAll(landRuns.getNext().makeRun()); + return booster; + } } diff --git a/Mage.Sets/src/mage/sets/Judgment.java b/Mage.Sets/src/mage/sets/Judgment.java index c21637ed783..5ae5e9af369 100644 --- a/Mage.Sets/src/mage/sets/Judgment.java +++ b/Mage.Sets/src/mage/sets/Judgment.java @@ -28,6 +28,7 @@ public final class Judgment extends ExpansionSet { this.numBoosterUncommon = 3; this.numBoosterRare = 1; this.ratioBoosterMythic = 0; + this.hasUnbalancedColors = true; cards.add(new SetCardInfo("Ancestor's Chosen", 1, Rarity.UNCOMMON, mage.cards.a.AncestorsChosen.class)); cards.add(new SetCardInfo("Anger", 77, Rarity.UNCOMMON, mage.cards.a.Anger.class)); cards.add(new SetCardInfo("Anurid Barkripper", 104, Rarity.COMMON, mage.cards.a.AnuridBarkripper.class)); @@ -84,6 +85,7 @@ public final class Judgment extends ExpansionSet { cards.add(new SetCardInfo("Glory", 11, Rarity.RARE, mage.cards.g.Glory.class)); cards.add(new SetCardInfo("Golden Wish", 12, Rarity.RARE, mage.cards.g.GoldenWish.class)); cards.add(new SetCardInfo("Goretusk Firebeast", 91, Rarity.COMMON, mage.cards.g.GoretuskFirebeast.class)); + cards.add(new SetCardInfo("Grave Consequences", 67, Rarity.UNCOMMON, mage.cards.g.GraveConsequences.class)); cards.add(new SetCardInfo("Grip of Amnesia", 41, Rarity.COMMON, mage.cards.g.GripOfAmnesia.class)); cards.add(new SetCardInfo("Grizzly Fate", 119, Rarity.UNCOMMON, mage.cards.g.GrizzlyFate.class)); cards.add(new SetCardInfo("Guided Strike", 13, Rarity.COMMON, mage.cards.g.GuidedStrike.class)); @@ -129,6 +131,7 @@ public final class Judgment extends ExpansionSet { cards.add(new SetCardInfo("Scalpelexis", 50, Rarity.RARE, mage.cards.s.Scalpelexis.class)); cards.add(new SetCardInfo("Seedtime", 130, Rarity.RARE, mage.cards.s.Seedtime.class)); cards.add(new SetCardInfo("Selfless Exorcist", 21, Rarity.RARE, mage.cards.s.SelflessExorcist.class)); + cards.add(new SetCardInfo("Serene Sunset", 131, Rarity.UNCOMMON, mage.cards.s.SereneSunset.class)); cards.add(new SetCardInfo("Shieldmage Advocate", 22, Rarity.COMMON, mage.cards.s.ShieldmageAdvocate.class)); cards.add(new SetCardInfo("Silver Seraph", 23, Rarity.RARE, mage.cards.s.SilverSeraph.class)); cards.add(new SetCardInfo("Solitary Confinement", 24, Rarity.RARE, mage.cards.s.SolitaryConfinement.class)); @@ -159,6 +162,7 @@ public final class Judgment extends ExpansionSet { cards.add(new SetCardInfo("Web of Inertia", 53, Rarity.UNCOMMON, mage.cards.w.WebOfInertia.class)); cards.add(new SetCardInfo("Wonder", 54, Rarity.UNCOMMON, mage.cards.w.Wonder.class)); cards.add(new SetCardInfo("Worldgorger Dragon", 103, Rarity.RARE, mage.cards.w.WorldgorgerDragon.class)); + cards.add(new SetCardInfo("Wormfang Behemoth", 55, Rarity.RARE, mage.cards.w.WormfangBehemoth.class)); cards.add(new SetCardInfo("Wormfang Drake", 57, Rarity.COMMON, mage.cards.w.WormfangDrake.class)); cards.add(new SetCardInfo("Wormfang Manta", 58, Rarity.RARE, mage.cards.w.WormfangManta.class)); cards.add(new SetCardInfo("Wormfang Newt", 59, Rarity.COMMON, mage.cards.w.WormfangNewt.class)); diff --git a/Mage.Sets/src/mage/sets/JumpstartHistoricHorizons.java b/Mage.Sets/src/mage/sets/JumpstartHistoricHorizons.java index 41de9c18019..7f5f0ca8bd3 100644 --- a/Mage.Sets/src/mage/sets/JumpstartHistoricHorizons.java +++ b/Mage.Sets/src/mage/sets/JumpstartHistoricHorizons.java @@ -72,7 +72,7 @@ public final class JumpstartHistoricHorizons extends ExpansionSet { cards.add(new SetCardInfo("Bonescythe Sliver", 56, Rarity.RARE, mage.cards.b.BonescytheSliver.class)); cards.add(new SetCardInfo("Boneyard Aberration", 14, Rarity.UNCOMMON, mage.cards.b.BoneyardAberration.class)); cards.add(new SetCardInfo("Boros Elite", 57, Rarity.UNCOMMON, mage.cards.b.BorosElite.class)); - cards.add(new SetCardInfo("Bounty of the Deep", 8, Rarity.UNCOMMON, mage.cards.b.BountyOfTheDeep.class)); + cards.add(new SetCardInfo("Bounty of the Deep", 8, Rarity.COMMON, mage.cards.b.BountyOfTheDeep.class)); cards.add(new SetCardInfo("Breaching Hippocamp", 170, Rarity.COMMON, mage.cards.b.BreachingHippocamp.class)); cards.add(new SetCardInfo("Breathless Knight", 680, Rarity.COMMON, mage.cards.b.BreathlessKnight.class)); cards.add(new SetCardInfo("Bred for the Hunt", 681, Rarity.UNCOMMON, mage.cards.b.BredForTheHunt.class)); @@ -86,6 +86,7 @@ public final class JumpstartHistoricHorizons extends ExpansionSet { cards.add(new SetCardInfo("Changeling Outcast", 304, Rarity.COMMON, mage.cards.c.ChangelingOutcast.class)); cards.add(new SetCardInfo("Chatter of the Squirrel", 553, Rarity.COMMON, mage.cards.c.ChatterOfTheSquirrel.class)); cards.add(new SetCardInfo("Chatterfang, Squirrel General", 552, Rarity.MYTHIC, mage.cards.c.ChatterfangSquirrelGeneral.class)); + cards.add(new SetCardInfo("Chatterstorm", 554, Rarity.COMMON, mage.cards.c.Chatterstorm.class)); cards.add(new SetCardInfo("Chitterspitter", 555, Rarity.RARE, mage.cards.c.Chitterspitter.class)); cards.add(new SetCardInfo("Choking Tethers", 178, Rarity.COMMON, mage.cards.c.ChokingTethers.class)); cards.add(new SetCardInfo("Chrome Courier", 684, Rarity.COMMON, mage.cards.c.ChromeCourier.class)); diff --git a/Mage.Sets/src/mage/sets/Kaladesh.java b/Mage.Sets/src/mage/sets/Kaladesh.java index 19ea7be55a9..90c6de2841b 100644 --- a/Mage.Sets/src/mage/sets/Kaladesh.java +++ b/Mage.Sets/src/mage/sets/Kaladesh.java @@ -4,6 +4,10 @@ import mage.cards.ExpansionSet; import mage.cards.repository.CardCriteria; import mage.cards.repository.CardInfo; import mage.cards.repository.CardRepository; +import mage.collation.BoosterCollator; +import mage.collation.BoosterStructure; +import mage.collation.CardRun; +import mage.collation.RarityConfiguration; import mage.constants.Rarity; import mage.constants.SetType; @@ -21,8 +25,6 @@ public final class Kaladesh extends ExpansionSet { return instance; } - private final List savedSpecialLand = new ArrayList<>(); - private Kaladesh() { super("Kaladesh", "KLD", ExpansionSet.buildDate(2016, 9, 30), SetType.EXPANSION); this.blockName = "Kaladesh"; @@ -33,9 +35,8 @@ public final class Kaladesh extends ExpansionSet { this.numBoosterUncommon = 3; this.numBoosterRare = 1; this.ratioBoosterMythic = 8; - this.numBoosterSpecial = 0; + this.ratioBoosterSpecialCommon = 144; this.maxCardNumberInBooster = 264; - this.ratioBoosterSpecialLand = 144; cards.add(new SetCardInfo("Accomplished Automaton", 191, Rarity.COMMON, mage.cards.a.AccomplishedAutomaton.class)); cards.add(new SetCardInfo("Acrobatic Maneuver", 1, Rarity.COMMON, mage.cards.a.AcrobaticManeuver.class)); @@ -318,15 +319,127 @@ public final class Kaladesh extends ExpansionSet { } @Override - public List getSpecialLand() { - if (savedSpecialLand.isEmpty()) { - CardCriteria criteria = new CardCriteria(); - criteria.setCodes("MPS"); - criteria.minCardNumber(1); - criteria.maxCardNumber(30); - savedSpecialLand.addAll(CardRepository.instance.findCards(criteria)); + protected List findCardsByRarity(Rarity rarity) { + List cardInfos = super.findCardsByRarity(rarity); + if (rarity == Rarity.SPECIAL) { + cardInfos.addAll(CardRepository.instance.findCards(new CardCriteria().setCodes("MPS").maxCardNumber(30))); } + return cardInfos; + } - return new ArrayList<>(savedSpecialLand); + @Override + protected void generateBoosterMap() { + super.generateBoosterMap(); + CardRepository + .instance + .findCards(new CardCriteria().setCodes("MPS").maxCardNumber(30)) + .stream() + .forEach(cardInfo -> inBoosterMap.put("MPS_" + cardInfo.getCardNumber(), cardInfo)); + } + + @Override + public BoosterCollator createCollator() { + return new KaladeshCollator(); + } +} + +// Booster collation info from https://www.lethe.xyz/mtg/collation/kld.html +// Using USA collation for all rarities +// Foil rare sheet used for regular rares as regular rare sheet is not known +class KaladeshCollator implements BoosterCollator { + private final CardRun commonA = new CardRun(true, "14", "47", "113", "22", "51", "108", "1", "58", "118", "28", "44", "139", "7", "61", "133", "30", "37", "126", "20", "38", "137", "31", "66", "138", "12", "68", "128", "18", "51", "108", "14", "69", "113", "6", "47", "109", "1", "58", "134", "7", "63", "118", "22", "37", "133", "28", "44", "139", "30", "61", "138", "20", "38", "126", "18", "66", "128", "12", "69", "134", "6", "68", "137", "31", "63", "109"); + private final CardRun commonB = new CardRun(true, "93", "171", "86", "159", "98", "175", "100", "145", "77", "170", "102", "157", "72", "149", "91", "141", "95", "173", "81", "164", "87", "168", "93", "171", "86", "159", "72", "145", "102", "157", "95", "170", "98", "175", "91", "149", "87", "164", "77", "168", "81", "173", "86", "141", "93", "171", "100", "159", "102", "175", "98", "149", "95", "157", "72", "170", "91", "145", "100", "141", "77", "168", "87", "164", "81", "173"); + private final CardRun commonC1 = new CardRun(true, "23", "167", "229", "48", "202", "148", "19", "213", "42", "111", "201", "230", "99", "238", "140", "160", "197", "26", "127", "232", "103", "218", "116", "19", "202", "42", "167", "195", "209", "55", "217", "148", "111", "229", "76", "232", "48", "213", "127", "230", "23", "197", "140", "201", "103", "218", "160", "26", "217", "99", "55", "195", "116", "76", "238"); + private final CardRun commonC2 = new CardRun(true, "82", "240", "158", "65", "74", "224", "24", "241", "129", "233", "207", "237", "82", "228", "17", "191", "65", "221", "166", "224", "70", "233", "24", "240", "129", "241", "158", "237", "74", "17", "207", "228", "166", "240", "82", "191", "65", "221", "129", "224", "24", "237", "158", "233", "70", "74", "228", "166", "241", "17", "209", "207", "221", "70", "191"); + private final CardRun uncommonA = new CardRun(true, "35", "206", "64", "97", "176", "71", "142", "211", "29", "198", "117", "79", "187", "212", "13", "85", "162", "219", "181", "146", "80", "225", "161", "104", "190", "215", "2", "89", "189", "200", "115", "105", "54", "196", "180", "97", "106", "212", "182", "83", "64", "225", "35", "71", "176", "205", "142", "85", "13", "211", "117", "79", "187", "206", "162", "104", "29", "198", "181", "97", "189", "219", "80", "177", "54", "200", "190", "89", "2", "196", "161", "105", "180", "215", "115", "83", "106", "205", "182", "85", "35", "206", "64", "104", "187", "212", "29", "79", "176", "219", "13", "146", "181", "198", "142", "177", "80", "215", "189", "71", "117", "211", "162", "105", "190", "205", "2", "146", "54", "225", "182", "83", "161", "196", "115", "89", "180", "200", "106", "177"); + private final CardRun uncommonB = new CardRun(true, "185", "153", "227", "34", "36", "119", "25", "156", "40", "242", "10", "132", "155", "45", "16", "131", "236", "53", "169", "94", "33", "46", "107", "154", "239", "125", "11", "43", "150", "49", "27", "75", "120", "57", "144", "34", "248", "36", "119", "153", "227", "188", "50", "16", "135", "155", "107", "25", "46", "169", "131", "185", "40", "150", "10", "45", "156", "125", "43", "33", "236", "123", "154", "94", "53", "132", "242", "144", "120", "239", "49", "11", "153", "248", "57", "135", "188", "123", "75", "27", "50", "169", "131", "185", "45", "16", "227", "132", "154", "120", "25", "242", "236", "36", "119", "94", "34", "156", "40", "188", "43", "107", "155", "46", "10", "144", "53", "33", "239", "125", "150", "49", "11", "75", "135", "57", "27", "248", "123", "50"); + private final CardRun rare = new CardRun(true, "208", "152", "110", "246", "39", "192", "130", "73", "247", "5", "222", "60", "223", "21", "210", "151", "4", "231", "78", "32", "243", "121", "124", "226", "163", "220", "3", "245", "84", "183", "56", "235", "90", "174", "244", "41", "178", "136", "179", "222", "101", "214", "15", "247", "59", "203", "193", "152", "114", "62", "143", "5", "204", "88", "130", "92", "246", "8", "122", "249", "96", "147", "121", "32", "39", "199", "194", "60", "208", "9", "78", "216", "52", "165", "3", "223", "90", "124", "112", "231", "84", "183", "21", "192", "151", "172", "174", "245", "143", "101", "243", "67", "210", "178", "41", "226", "15", "204", "114", "147", "8", "234", "235", "136", "88", "203", "184", "220", "52", "249", "216", "92", "244", "59", "199", "186", "214", "122", "62", "165", "194"); + private final CardRun masterpiece = new CardRun(false, "MPS_1", "MPS_2", "MPS_3", "MPS_4", "MPS_5", "MPS_6", "MPS_7", "MPS_8", "MPS_9", "MPS_10", "MPS_11", "MPS_12", "MPS_13", "MPS_14", "MPS_15", "MPS_16", "MPS_17", "MPS_18", "MPS_19", "MPS_20", "MPS_21", "MPS_22", "MPS_23", "MPS_24", "MPS_25", "MPS_26", "MPS_27", "MPS_28", "MPS_29", "MPS_30"); + private final CardRun land = new CardRun(false, "250", "251", "252", "253", "254", "255", "256", "257", "258", "259", "260", "261", "262", "263", "264"); + + private final BoosterStructure AABBC1C1C1C1C1C1 = new BoosterStructure( + commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AAABBC1C1C1C1C1 = new BoosterStructure( + commonA, commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AABBC1C1C1C1C1M = new BoosterStructure( + commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1, commonC1, + masterpiece + ); + private final BoosterStructure AAAABBC2C2C2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, + commonC2, commonC2, commonC2, commonC2 + ); + private final BoosterStructure AAAABBBBC2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, commonB, commonB, + commonC2, commonC2 + ); + private final BoosterStructure AAB = new BoosterStructure(uncommonA, uncommonA, uncommonB); + private final BoosterStructure ABB = new BoosterStructure(uncommonA, uncommonB, uncommonB); + private final BoosterStructure R1 = new BoosterStructure(rare); + private final BoosterStructure L1 = new BoosterStructure(land); + + // In order for equal numbers of each common to exist, the average booster must contain: + // 3.27 A commons (36 / 11) (rounded to 471/144) + // 2.18 B commons (24 / 11) (rounded to 314/144) + // 2.73 C1 commons (30 / 11) (rounded to 392/144) + // 1.82 C2 commons (20 / 11) (rounded to 262/144) + // These numbers are the same for all sets with 101 commons in A/B/C1/C2 print runs + // and with 10 common slots per booster + private final RarityConfiguration commonRuns = new RarityConfiguration( + AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, AABBC1C1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AABBC1C1C1C1C1M, + + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, + + AAAABBBBC2C2, AAAABBBBC2C2, AAAABBBBC2C2, AAAABBBBC2C2, AAAABBBBC2C2, + AAAABBBBC2C2, AAAABBBBC2C2, AAAABBBBC2C2, AAAABBBBC2C2, AAAABBBBC2C2, + AAAABBBBC2C2, AAAABBBBC2C2, AAAABBBBC2C2 + ); + private final RarityConfiguration uncommonRuns = new RarityConfiguration(AAB, ABB); + private final RarityConfiguration rareRuns = new RarityConfiguration(R1); + private final RarityConfiguration landRuns = new RarityConfiguration(L1); + + @Override + public List makeBooster() { + List booster = new ArrayList<>(); + booster.addAll(commonRuns.getNext().makeRun()); + booster.addAll(uncommonRuns.getNext().makeRun()); + booster.addAll(rareRuns.getNext().makeRun()); + booster.addAll(landRuns.getNext().makeRun()); + return booster; } } diff --git a/Mage.Sets/src/mage/sets/KaladeshRemastered.java b/Mage.Sets/src/mage/sets/KaladeshRemastered.java index 89844ee7387..5ddb7b4a436 100644 --- a/Mage.Sets/src/mage/sets/KaladeshRemastered.java +++ b/Mage.Sets/src/mage/sets/KaladeshRemastered.java @@ -1,11 +1,9 @@ package mage.sets; import mage.cards.ExpansionSet; -import mage.cards.repository.CardInfo; import mage.constants.Rarity; import mage.constants.SetType; -import java.util.ArrayList; import java.util.List; /** diff --git a/Mage.Sets/src/mage/sets/Kaldheim.java b/Mage.Sets/src/mage/sets/Kaldheim.java index 6e9f0891920..dd96f68209c 100644 --- a/Mage.Sets/src/mage/sets/Kaldheim.java +++ b/Mage.Sets/src/mage/sets/Kaldheim.java @@ -1,14 +1,11 @@ package mage.sets; import mage.cards.ExpansionSet; -import mage.cards.repository.CardCriteria; import mage.cards.repository.CardInfo; -import mage.cards.repository.CardRepository; import mage.collation.BoosterCollator; import mage.collation.BoosterStructure; import mage.collation.CardRun; import mage.collation.RarityConfiguration; -import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.SetType; import mage.constants.SuperType; @@ -27,8 +24,6 @@ public final class Kaldheim extends ExpansionSet { return instance; } - private final List savedSpecialLand = new ArrayList<>(); - private Kaldheim() { super("Kaldheim", "KHM", ExpansionSet.buildDate(2021, 2, 5), SetType.EXPANSION); this.blockName = "Kaldheim"; @@ -451,46 +446,13 @@ public final class Kaldheim extends ExpansionSet { } @Override - public List getCardsByRarity(Rarity rarity) { - if (savedCards.containsKey(rarity)) { - return new ArrayList<>(savedCards.get(rarity)); + protected List findSpecialCardsByRarity(Rarity rarity) { + List cardInfos = super.findSpecialCardsByRarity(rarity); + if (rarity == Rarity.LAND) { + // Shimmerdrift Vale is a normal common + cardInfos.removeIf(cardInfo -> "Shimmerdrift Vale".equals(cardInfo.getName())); } - CardCriteria criteria = new CardCriteria(); - criteria.setCodes(this.code); - criteria.rarities(rarity); - List savedCardsInfos = CardRepository.instance.findCards(criteria); - switch (rarity) { - case LAND: - savedCardsInfos.removeIf(cardInfo -> !cardInfo.getSupertypes().contains(SuperType.SNOW)); - savedCardsInfos.removeIf(cardInfo -> !cardInfo.getSupertypes().contains(SuperType.BASIC)); - break; - case COMMON: - savedCardsInfos.removeIf(cardInfo -> - cardInfo.getCard().isSnow() - && cardInfo.getCard().isLand() - && !cardInfo.getCard().getName().equals("Shimmerdrift Vale") - ); - break; - } - savedCardsInfos.removeIf(next -> next.getCardNumberAsInt() > maxCardNumberInBooster); - savedCards.put(rarity, savedCardsInfos); - return new ArrayList<>(savedCardsInfos); - } - - @Override - public List getSpecialLand() { - if (savedSpecialLand.isEmpty()) { - CardCriteria criteria = new CardCriteria(); - criteria.setCodes(this.code); - criteria.types(CardType.LAND); - savedSpecialLand.addAll(CardRepository.instance.findCards(criteria)); - savedSpecialLand.removeIf(cardInfo -> cardInfo.getSupertypes().contains(SuperType.BASIC)); - savedSpecialLand.removeIf(cardInfo -> !cardInfo.getSupertypes().contains(SuperType.SNOW)); - savedSpecialLand.removeIf(cardInfo -> cardInfo.getName().equals("Shimmerdrift Vale")); - savedSpecialLand.removeIf(cardInfo -> cardInfo.getName().equals("Faceless Haven")); - savedSpecialLand.removeIf(cardInfo -> cardInfo.getCardNumberAsInt() > maxCardNumberInBooster); - } - return new ArrayList<>(savedSpecialLand); + return cardInfos; } @Override @@ -502,16 +464,20 @@ public final class Kaldheim extends ExpansionSet { // Booster collation info from https://www.lethe.xyz/mtg/collation/khm.html // Using USA collation for common/uncommon and JP for rare/mythic class KaldheimCollator implements BoosterCollator { - - private final CardRun commonA = new CardRun(true, "34","77","136","13","78","149","3","47","127","14","67","140","19","54","124","38","49","147","39","55","157","1","53","141","37","66","126","10","71","155","4","65","121","13","77","136","34","78","127","3","47","149","14","54","124","38","67","140","19","55","147","39","49","157","37","53","141","10","65","155","1","71","121","4","66","126"); - private final CardRun commonB = new CardRun(true, "102","176","87","183","93","184","104","178","117","174","111","171","96","194","84","176","119","180","83","164","89","172","87","175","102","183","104","178","93","174","117","184","111","171","84","194","119","164","96","180","89","176","83","172","102","175","87","178","104","174","93","183","117","171","119","184","84","164","111","194","89","180","96","172","83","175"); - private final CardRun commonC1 = new CardRun(true, "187","152","242","46","173","23","101","246","48","190","32","151","99","68","267","31","91","192","143","57","100","243","105","16","134","42","196","238","187","46","23","242","152","173","48","32","246","190","151","101","31","68","99","267","91","134","105","57","16","192","100","143","243","196","42"); - private final CardRun commonC2 = new CardRun(true, "11","193","95","158","17","239","44","159","129","7","118","85","138","74","165","11","129","193","150","72","5","95","159","74","158","17","85","239","118","138","44","7","238","193","150","165","5","72","158","95","11","44","159","239","129","17","85","74","7","118","5","150","165","138","72"); - private final CardRun uncommonA = new CardRun(true, "215","236","212","208","195","224","332","6","232","18","106","268","209","162","8","76","122","88","182","206","202","62","110","132","200","325","271","211","144","103","215","236","258","56","163","113","28","226","2","58","263","148","232","162","224","208","195","323","268","18","106","6","233","8","76","122","209","88","182","206","202","62","110","132","321","220","271","211","144","258","2","28","263","113","226","103","236","163","56","215","148","58","329","195","6","232","233","18","212","162","268","106","208","103","322","76","122","88","182","206","202","62","110","132","200","220","271","211","144","8","58","28","258","113","56","148","2","263","226","163"); - private final CardRun uncommonB = new CardRun(true, "30","166","75","201","265","222","45","135","256","191","231","235","36","250","316","128","25","247","264","35","97","186","223","59","60","130","216","80","244","259","217","133","64","245","108","189","331","137","116","253","30","166","75","201","265","327","45","128","256","247","235","36","191","25","170","250","135","231","186","35","60","324","97","130","59","264","244","80","328","259","133","217","64","245","108","189","230","137","116","253","30","166","75","201","265","222","45","256","191","235","170","135","36","128","25","247","250","231","35","223","60","130","97","264","216","186","59","244","80","259","217","133","304","245","108","189","230","137","116","253"); - private final CardRun rareA = new CardRun(false, "9","12","20","21","24","26","27","29","43","50","51","52","61","63","69","73","79","82","86","90","92","107","109","112","115","120","123","125","131","142","146","153","156","161","167","169","177","179","181","185","188","197","203","204","205","207","210","213","214","219","227","228","229","234","237","240","241","251","252","254","255","260","272","275","9","12","20","21","24","26","27","29","43","50","51","52","61","63","69","73","79","82","86","90","92","107","109","112","115","120","123","125","131","142","146","153","156","161","167","169","177","179","181","185","188","197","203","204","205","207","210","213","214","219","227","228","229","234","237","240","241","251","252","254","255","260","272","275","15","22","33","40","41","70","81","94","98","114","139","145","154","160","168","198","199","218","221","225"); - private final CardRun rareB = new CardRun(false, "9","12","20","300","24","26","27","301","43","303","51","52","61","63","69","73","79","82","86","90","306","107","109","307","309","310","311","125","131","312","146","153","156","161","167","315","177","317","318","185","188","319","203","204","205","207","210","213","214","219","227","330","229","234","237","240","241","290","291","292","255","293","272","275","9","12","20","300","24","26","27","301","43","303","51","52","61","63","69","73","79","82","86","90","306","107","109","307","309","310","311","125","131","312","146","153","156","161","167","315","177","317","318","185","188","319","203","204","205","207","210","213","214","219","227","330","229","234","237","240","241","290","291","292","255","293","272","275","299","22","294","302","295","305","81","94","296","308","139","297","313","298","314","287","320","288","326","289"); - private final CardRun land = new CardRun(true, "270","282","248","277","276","280","278","266","270","283","282","285","274","277","281","279","262","284","282","248","283","276","269","276","280","285","281","249","257","284","277","249","281","284","283","266","257","281","269","280","261","276","277","283","249","278","285","248","276","285","279","261","269","257","249","248","283","270","285","277","282","284","270","278","248","279","269","281","274","280","279","257","281","284","277","257","274","273","279","276","262","266","284","281","273","282","278","262","280","279","274","262","282","283","278","262","279","261","285","273","266","283","261","280","284","266","278","270","285","282","280","276","277","273","278","269","273","249","261","274"); + private final CardRun commonA = new CardRun(true, "34", "77", "136", "13", "78", "149", "3", "47", "127", "14", "67", "140", "19", "54", "124", "38", "49", "147", "39", "55", "157", "1", "53", "141", "37", "66", "126", "10", "71", "155", "4", "65", "121", "13", "77", "136", "34", "78", "127", "3", "47", "149", "14", "54", "124", "38", "67", "140", "19", "55", "147", "39", "49", "157", "37", "53", "141", "10", "65", "155", "1", "71", "121", "4", "66", "126"); + private final CardRun commonB = new CardRun(true, "102", "176", "87", "183", "93", "184", "104", "178", "117", "174", "111", "171", "96", "194", "84", "176", "119", "180", "83", "164", "89", "172", "87", "175", "102", "183", "104", "178", "93", "174", "117", "184", "111", "171", "84", "194", "119", "164", "96", "180", "89", "176", "83", "172", "102", "175", "87", "178", "104", "174", "93", "183", "117", "171", "119", "184", "84", "164", "111", "194", "89", "180", "96", "172", "83", "175"); + private final CardRun commonC1 = new CardRun(true, "187", "152", "242", "46", "173", "23", "101", "246", "48", "190", "32", "151", "99", "68", "267", "31", "91", "192", "143", "57", "100", "243", "105", "16", "134", "42", "196", "238", "187", "46", "23", "242", "152", "173", "48", "32", "246", "190", "151", "101", "31", "68", "99", "267", "91", "134", "105", "57", "16", "192", "100", "143", "243", "196", "42"); + private final CardRun commonC2 = new CardRun(true, "11", "193", "95", "158", "17", "239", "44", "159", "129", "7", "118", "85", "138", "74", "165", "11", "129", "193", "150", "72", "5", "95", "159", "74", "158", "17", "85", "239", "118", "138", "44", "7", "238", "193", "150", "165", "5", "72", "158", "95", "11", "44", "159", "239", "129", "17", "85", "74", "7", "118", "5", "150", "165", "138", "72"); + private final CardRun uncommonA = new CardRun(true, "215", "236", "212", "208", "195", "224", "332", "6", "232", "18", "106", "268", "209", "162", "8", "76", "122", "88", "182", "206", "202", "62", "110", "132", "200", "325", "271", "211", "144", "103", "215", "236", "258", "56", "163", "113", "28", "226", "2", "58", "263", "148", "232", "162", "224", "208", "195", "323", "268", "18", "106", "6", "233", "8", "76", "122", "209", "88", "182", "206", "202", "62", "110", "132", "321", "220", "271", "211", "144", "258", "2", "28", "263", "113", "226", "103", "236", "163", "56", "215", "148", "58", "329", "195", "6", "232", "233", "18", "212", "162", "268", "106", "208", "103", "322", "76", "122", "88", "182", "206", "202", "62", "110", "132", "200", "220", "271", "211", "144", "8", "58", "28", "258", "113", "56", "148", "2", "263", "226", "163"); + private final CardRun uncommonB = new CardRun(true, "30", "166", "75", "201", "265", "222", "45", "135", "256", "191", "231", "235", "36", "250", "316", "128", "25", "247", "264", "35", "97", "186", "223", "59", "60", "130", "216", "80", "244", "259", "217", "133", "64", "245", "108", "189", "331", "137", "116", "253", "30", "166", "75", "201", "265", "327", "45", "128", "256", "247", "235", "36", "191", "25", "170", "250", "135", "231", "186", "35", "60", "324", "97", "130", "59", "264", "244", "80", "328", "259", "133", "217", "64", "245", "108", "189", "230", "137", "116", "253", "30", "166", "75", "201", "265", "222", "45", "256", "191", "235", "170", "135", "36", "128", "25", "247", "250", "231", "35", "223", "60", "130", "97", "264", "216", "186", "59", "244", "80", "259", "217", "133", "304", "245", "108", "189", "230", "137", "116", "253"); + // rares and mythics with no variants that can appear in Draft Boosters + private final CardRun rareNoVariant = new CardRun(false, "9", "12", "20", "24", "26", "27", "43", "51", "52", "61", "63", "69", "73", "79", "82", "86", "90", "107", "109", "125", "131", "146", "153", "156", "161", "167", "177", "185", "188", "203", "204", "205", "207", "210", "213", "214", "219", "227", "229", "234", "237", "240", "241", "255", "272", "275", "9", "12", "20", "24", "26", "27", "43", "51", "52", "61", "63", "69", "73", "79", "82", "86", "90", "107", "109", "125", "131", "146", "153", "156", "161", "167", "177", "185", "188", "203", "204", "205", "207", "210", "213", "214", "219", "227", "229", "234", "237", "240", "241", "255", "272", "275", "22", "81", "94", "139"); + // non-variant versions of rares and mythics with one variant + private final CardRun rareNonVariant = new CardRun(false, "21", "29", "50", "92", "112", "115", "120", "123", "142", "169", "179", "181", "197", "228", "251", "252", "254", "260", "21", "29", "50", "92", "112", "115", "120", "123", "142", "169", "179", "181", "197", "228", "251", "252", "254", "260", "15", "33", "40", "41", "70", "98", "114", "145", "154", "160", "168", "198", "199", "218", "221", "225"); + // variant versions of rares and mythics + private final CardRun rareVariantA = new CardRun(false, "290", "291", "292", "293", "300", "301", "303", "306", "307", "309", "310", "311", "312", "315", "317", "318", "319", "330", "290", "291", "292", "293", "300", "301", "303", "306", "307", "309", "310", "311", "312", "315", "317", "318", "319", "330", "287", "288", "289", "294", "295", "296", "297", "298", "299", "302", "305", "313", "314", "326", "308", "320"); + private final CardRun rareVariantB = new CardRun(false, "290", "291", "292", "293", "300", "301", "303", "306", "307", "309", "310", "311", "312", "315", "317", "318", "319", "330", "290", "291", "292", "293", "300", "301", "303", "306", "307", "309", "310", "311", "312", "315", "317", "318", "319", "330", "287", "288", "289", "294", "295", "296", "297", "298", "299", "302", "305", "313", "314", "326", "286", "333"); + private final CardRun land = new CardRun(true, "270", "282", "248", "277", "276", "280", "278", "266", "270", "283", "282", "285", "274", "277", "281", "279", "262", "284", "282", "248", "283", "276", "269", "276", "280", "285", "281", "249", "257", "284", "277", "249", "281", "284", "283", "266", "257", "281", "269", "280", "261", "276", "277", "283", "249", "278", "285", "248", "276", "285", "279", "261", "269", "257", "249", "248", "283", "270", "285", "277", "282", "284", "270", "278", "248", "279", "269", "281", "274", "280", "279", "257", "281", "284", "277", "257", "274", "273", "279", "276", "262", "266", "284", "281", "273", "282", "278", "262", "280", "279", "274", "262", "282", "283", "278", "262", "279", "261", "285", "273", "266", "283", "261", "280", "284", "266", "278", "270", "285", "282", "280", "276", "277", "273", "278", "269", "273", "249", "261", "274"); private final BoosterStructure AABBC1C1C1C1C1C1 = new BoosterStructure( commonA, commonA, @@ -535,8 +501,10 @@ class KaldheimCollator implements BoosterCollator { ); private final BoosterStructure AAA = new BoosterStructure(uncommonA, uncommonA, uncommonA); private final BoosterStructure BBB = new BoosterStructure(uncommonB, uncommonB, uncommonB); - private final BoosterStructure R1 = new BoosterStructure(rareA); - private final BoosterStructure R2 = new BoosterStructure(rareB); + private final BoosterStructure R1 = new BoosterStructure(rareNoVariant); + private final BoosterStructure R2 = new BoosterStructure(rareNonVariant); + private final BoosterStructure R3 = new BoosterStructure(rareVariantA); + private final BoosterStructure R4 = new BoosterStructure(rareVariantB); private final BoosterStructure L1 = new BoosterStructure(land); // In order for equal numbers of each common to exist, the average booster must contain: @@ -572,7 +540,12 @@ class KaldheimCollator implements BoosterCollator { AAAABBBC2C2C2 ); private final RarityConfiguration uncommonRuns = new RarityConfiguration(AAA, BBB); - private final RarityConfiguration rareRuns = new RarityConfiguration(R1, R1, R2); + private final RarityConfiguration rareRuns = new RarityConfiguration( + R1, R1, R1, R1, R1, R1, R1, R1, R1, R1, R1, R1, + R1, R1, R1, R1, R1, R1, R1, R1, R1, R1, R1, R1, + R2, R2, R2, R2, R2, R2, R2, R2, R2, + R3, R3, R4, R4 + ); private final RarityConfiguration landRuns = new RarityConfiguration(L1); @Override diff --git a/Mage.Sets/src/mage/sets/KamigawaNeonDynasty.java b/Mage.Sets/src/mage/sets/KamigawaNeonDynasty.java new file mode 100644 index 00000000000..199f453f314 --- /dev/null +++ b/Mage.Sets/src/mage/sets/KamigawaNeonDynasty.java @@ -0,0 +1,332 @@ +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * @author TheElk801 + */ +public final class KamigawaNeonDynasty extends ExpansionSet { + + private static final KamigawaNeonDynasty instance = new KamigawaNeonDynasty(); + + public static KamigawaNeonDynasty getInstance() { + return instance; + } + + private KamigawaNeonDynasty() { + super("Kamigawa: Neon Dynasty", "NEO", ExpansionSet.buildDate(2022, 2, 18), SetType.EXPANSION); + this.blockName = "Kamigawa: Neon Dynasty"; + this.hasBoosters = true; + this.hasBasicLands = true; + this.numBoosterDoubleFaced = 1; // temporary test fix + + cards.add(new SetCardInfo("Acquisition Octopus", 44, Rarity.UNCOMMON, mage.cards.a.AcquisitionOctopus.class)); + cards.add(new SetCardInfo("Akki Ember-Keeper", 130, Rarity.COMMON, mage.cards.a.AkkiEmberKeeper.class)); + cards.add(new SetCardInfo("Akki Ronin", 131, Rarity.COMMON, mage.cards.a.AkkiRonin.class)); + cards.add(new SetCardInfo("Akki War Paint", 132, Rarity.COMMON, mage.cards.a.AkkiWarPaint.class)); + cards.add(new SetCardInfo("Ambitious Assault", 133, Rarity.COMMON, mage.cards.a.AmbitiousAssault.class)); + cards.add(new SetCardInfo("Ancestral Katana", 1, Rarity.COMMON, mage.cards.a.AncestralKatana.class)); + cards.add(new SetCardInfo("Anchor to Reality", 45, Rarity.UNCOMMON, mage.cards.a.AnchorToReality.class)); + cards.add(new SetCardInfo("Animus of Night's Reach", 109, Rarity.UNCOMMON, mage.cards.a.AnimusOfNightsReach.class)); + cards.add(new SetCardInfo("Ao, the Dawn Sky", 2, Rarity.MYTHIC, mage.cards.a.AoTheDawnSky.class)); + cards.add(new SetCardInfo("Architect of Restoration", 34, Rarity.RARE, mage.cards.a.ArchitectOfRestoration.class)); + cards.add(new SetCardInfo("Armguard Familiar", 46, Rarity.COMMON, mage.cards.a.ArmguardFamiliar.class)); + cards.add(new SetCardInfo("Asari Captain", 215, Rarity.UNCOMMON, mage.cards.a.AsariCaptain.class)); + cards.add(new SetCardInfo("Assassin's Ink", 87, Rarity.UNCOMMON, mage.cards.a.AssassinsInk.class)); + cards.add(new SetCardInfo("Atsushi, the Blazing Sky", 134, Rarity.MYTHIC, mage.cards.a.AtsushiTheBlazingSky.class)); + cards.add(new SetCardInfo("Automated Artificer", 239, Rarity.COMMON, mage.cards.a.AutomatedArtificer.class)); + cards.add(new SetCardInfo("Awakened Awareness", 47, Rarity.UNCOMMON, mage.cards.a.AwakenedAwareness.class)); + cards.add(new SetCardInfo("Azusa's Many Journeys", 172, Rarity.UNCOMMON, mage.cards.a.AzusasManyJourneys.class)); + cards.add(new SetCardInfo("Bamboo Grove Archer", 173, Rarity.COMMON, mage.cards.b.BambooGroveArcher.class)); + cards.add(new SetCardInfo("Banishing Slash", 3, Rarity.UNCOMMON, mage.cards.b.BanishingSlash.class)); + cards.add(new SetCardInfo("Bearer of Memory", 174, Rarity.COMMON, mage.cards.b.BearerOfMemory.class)); + cards.add(new SetCardInfo("Befriending the Moths", 4, Rarity.COMMON, mage.cards.b.BefriendingTheMoths.class)); + cards.add(new SetCardInfo("Behold the Unspeakable", 48, Rarity.UNCOMMON, mage.cards.b.BeholdTheUnspeakable.class)); + cards.add(new SetCardInfo("Biting-Palm Ninja", 88, Rarity.RARE, mage.cards.b.BitingPalmNinja.class)); + cards.add(new SetCardInfo("Blade of the Oni", 89, Rarity.MYTHIC, mage.cards.b.BladeOfTheOni.class)); + cards.add(new SetCardInfo("Blade-Blizzard Kitsune", 5, Rarity.UNCOMMON, mage.cards.b.BladeBlizzardKitsune.class)); + cards.add(new SetCardInfo("Bloodfell Caves", 264, Rarity.COMMON, mage.cards.b.BloodfellCaves.class)); + cards.add(new SetCardInfo("Blossom Prancer", 175, Rarity.UNCOMMON, mage.cards.b.BlossomPrancer.class)); + cards.add(new SetCardInfo("Blossoming Sands", 265, Rarity.COMMON, mage.cards.b.BlossomingSands.class)); + cards.add(new SetCardInfo("Boon of Boseiju", 176, Rarity.UNCOMMON, mage.cards.b.BoonOfBoseiju.class)); + cards.add(new SetCardInfo("Born to Drive", 6, Rarity.UNCOMMON, mage.cards.b.BornToDrive.class)); + cards.add(new SetCardInfo("Boseiju Reaches Skyward", 177, Rarity.UNCOMMON, mage.cards.b.BoseijuReachesSkyward.class)); + cards.add(new SetCardInfo("Boseiju, Who Endures", 266, Rarity.RARE, mage.cards.b.BoseijuWhoEndures.class)); + cards.add(new SetCardInfo("Branch of Boseiju", 177, Rarity.UNCOMMON, mage.cards.b.BranchOfBoseiju.class)); + cards.add(new SetCardInfo("Brilliant Restoration", 7, Rarity.RARE, mage.cards.b.BrilliantRestoration.class)); + cards.add(new SetCardInfo("Bronze Cudgels", 240, Rarity.UNCOMMON, mage.cards.b.BronzeCudgels.class)); + cards.add(new SetCardInfo("Bronzeplate Boar", 135, Rarity.UNCOMMON, mage.cards.b.BronzeplateBoar.class)); + cards.add(new SetCardInfo("Brute Suit", 241, Rarity.COMMON, mage.cards.b.BruteSuit.class)); + cards.add(new SetCardInfo("Careful Cultivation", 178, Rarity.COMMON, mage.cards.c.CarefulCultivation.class)); + cards.add(new SetCardInfo("Chainflail Centipede", 90, Rarity.COMMON, mage.cards.c.ChainflailCentipede.class)); + cards.add(new SetCardInfo("Circuit Mender", 242, Rarity.UNCOMMON, mage.cards.c.CircuitMender.class)); + cards.add(new SetCardInfo("Clawing Torment", 91, Rarity.COMMON, mage.cards.c.ClawingTorment.class)); + cards.add(new SetCardInfo("Cloudsteel Kirin", 8, Rarity.RARE, mage.cards.c.CloudsteelKirin.class)); + cards.add(new SetCardInfo("Coiling Stalker", 179, Rarity.COMMON, mage.cards.c.CoilingStalker.class)); + cards.add(new SetCardInfo("Colossal Skyturtle", 216, Rarity.UNCOMMON, mage.cards.c.ColossalSkyturtle.class)); + cards.add(new SetCardInfo("Commune with Spirits", 180, Rarity.COMMON, mage.cards.c.CommuneWithSpirits.class)); + cards.add(new SetCardInfo("Containment Construct", 243, Rarity.UNCOMMON, mage.cards.c.ContainmentConstruct.class)); + cards.add(new SetCardInfo("Covert Technician", 49, Rarity.UNCOMMON, mage.cards.c.CovertTechnician.class)); + cards.add(new SetCardInfo("Crackling Emergence", 136, Rarity.COMMON, mage.cards.c.CracklingEmergence.class)); + cards.add(new SetCardInfo("Debt to the Kami", 92, Rarity.COMMON, mage.cards.d.DebtToTheKami.class)); + cards.add(new SetCardInfo("Dismal Backwater", 267, Rarity.COMMON, mage.cards.d.DismalBackwater.class)); + cards.add(new SetCardInfo("Disruption Protocol", 51, Rarity.COMMON, mage.cards.d.DisruptionProtocol.class)); + cards.add(new SetCardInfo("Dockside Chef", 93, Rarity.UNCOMMON, mage.cards.d.DocksideChef.class)); + cards.add(new SetCardInfo("Dokuchi Shadow-Walker", 94, Rarity.COMMON, mage.cards.d.DokuchiShadowWalker.class)); + cards.add(new SetCardInfo("Dokuchi Silencer", 95, Rarity.UNCOMMON, mage.cards.d.DokuchiSilencer.class)); + cards.add(new SetCardInfo("Dragonfly Suit", 9, Rarity.COMMON, mage.cards.d.DragonflySuit.class)); + cards.add(new SetCardInfo("Dragonspark Reactor", 137, Rarity.UNCOMMON, mage.cards.d.DragonsparkReactor.class)); + cards.add(new SetCardInfo("Dramatist's Puppet", 244, Rarity.COMMON, mage.cards.d.DramatistsPuppet.class)); + cards.add(new SetCardInfo("Eater of Virtue", 245, Rarity.RARE, mage.cards.e.EaterOfVirtue.class)); + cards.add(new SetCardInfo("Echo of Death's Wail", 124, Rarity.RARE, mage.cards.e.EchoOfDeathsWail.class)); + cards.add(new SetCardInfo("Ecologist's Terrarium", 246, Rarity.COMMON, mage.cards.e.EcologistsTerrarium.class)); + cards.add(new SetCardInfo("Eiganjo Exemplar", 10, Rarity.COMMON, mage.cards.e.EiganjoExemplar.class)); + cards.add(new SetCardInfo("Eiganjo Uprising", 217, Rarity.RARE, mage.cards.e.EiganjoUprising.class)); + cards.add(new SetCardInfo("Eiganjo, Seat of the Empire", 268, Rarity.RARE, mage.cards.e.EiganjoSeatOfTheEmpire.class)); + cards.add(new SetCardInfo("Enormous Energy Blade", 96, Rarity.UNCOMMON, mage.cards.e.EnormousEnergyBlade.class)); + cards.add(new SetCardInfo("Enthusiastic Mechanaut", 218, Rarity.UNCOMMON, mage.cards.e.EnthusiasticMechanaut.class)); + cards.add(new SetCardInfo("Era of Enlightenment", 11, Rarity.COMMON, mage.cards.e.EraOfEnlightenment.class)); + cards.add(new SetCardInfo("Essence Capture", 52, Rarity.UNCOMMON, mage.cards.e.EssenceCapture.class)); + cards.add(new SetCardInfo("Etching of Kumano", 152, Rarity.UNCOMMON, mage.cards.e.EtchingOfKumano.class)); + cards.add(new SetCardInfo("Experimental Synthesizer", 138, Rarity.COMMON, mage.cards.e.ExperimentalSynthesizer.class)); + cards.add(new SetCardInfo("Explosive Entry", 139, Rarity.COMMON, mage.cards.e.ExplosiveEntry.class)); + cards.add(new SetCardInfo("Explosive Singularity", 140, Rarity.MYTHIC, mage.cards.e.ExplosiveSingularity.class)); + cards.add(new SetCardInfo("Fable of the Mirror-Breaker", 141, Rarity.RARE, mage.cards.f.FableOfTheMirrorBreaker.class)); + cards.add(new SetCardInfo("Fade into Antiquity", 182, Rarity.COMMON, mage.cards.f.FadeIntoAntiquity.class)); + cards.add(new SetCardInfo("Fang of Shigeki", 183, Rarity.COMMON, mage.cards.f.FangOfShigeki.class)); + cards.add(new SetCardInfo("Farewell", 13, Rarity.RARE, mage.cards.f.Farewell.class)); + cards.add(new SetCardInfo("Favor of Jukai", 184, Rarity.COMMON, mage.cards.f.FavorOfJukai.class)); + cards.add(new SetCardInfo("Flame Discharge", 142, Rarity.UNCOMMON, mage.cards.f.FlameDischarge.class)); + cards.add(new SetCardInfo("Forest", 291, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fragment of Konda", 12, Rarity.UNCOMMON, mage.cards.f.FragmentOfKonda.class)); + cards.add(new SetCardInfo("Futurist Operative", 53, Rarity.UNCOMMON, mage.cards.f.FuturistOperative.class)); + cards.add(new SetCardInfo("Futurist Sentinel", 54, Rarity.COMMON, mage.cards.f.FuturistSentinel.class)); + cards.add(new SetCardInfo("Generous Visitor", 185, Rarity.UNCOMMON, mage.cards.g.GenerousVisitor.class)); + cards.add(new SetCardInfo("Geothermal Kami", 186, Rarity.COMMON, mage.cards.g.GeothermalKami.class)); + cards.add(new SetCardInfo("Gift of Wrath", 143, Rarity.COMMON, mage.cards.g.GiftOfWrath.class)); + cards.add(new SetCardInfo("Gloomshrieker", 219, Rarity.UNCOMMON, mage.cards.g.Gloomshrieker.class)); + cards.add(new SetCardInfo("Go-Shintai of Ancient Wars", 144, Rarity.UNCOMMON, mage.cards.g.GoShintaiOfAncientWars.class)); + cards.add(new SetCardInfo("Go-Shintai of Boundless Vigor", 187, Rarity.UNCOMMON, mage.cards.g.GoShintaiOfBoundlessVigor.class)); + cards.add(new SetCardInfo("Go-Shintai of Hidden Cruelty", 97, Rarity.UNCOMMON, mage.cards.g.GoShintaiOfHiddenCruelty.class)); + cards.add(new SetCardInfo("Go-Shintai of Lost Wisdom", 55, Rarity.UNCOMMON, mage.cards.g.GoShintaiOfLostWisdom.class)); + cards.add(new SetCardInfo("Go-Shintai of Shared Purpose", 14, Rarity.UNCOMMON, mage.cards.g.GoShintaiOfSharedPurpose.class)); + cards.add(new SetCardInfo("Golden-Tail Disciple", 15, Rarity.COMMON, mage.cards.g.GoldenTailDisciple.class)); + cards.add(new SetCardInfo("Goro-Goro, Disciple of Ryusei", 145, Rarity.RARE, mage.cards.g.GoroGoroDiscipleOfRyusei.class)); + cards.add(new SetCardInfo("Grafted Growth", 188, Rarity.COMMON, mage.cards.g.GraftedGrowth.class)); + cards.add(new SetCardInfo("Gravelighter", 98, Rarity.UNCOMMON, mage.cards.g.Gravelighter.class)); + cards.add(new SetCardInfo("Greasefang, Okiba Boss", 220, Rarity.RARE, mage.cards.g.GreasefangOkibaBoss.class)); + cards.add(new SetCardInfo("Greater Tanuki", 189, Rarity.COMMON, mage.cards.g.GreaterTanuki.class)); + cards.add(new SetCardInfo("Guardians of Oboro", 56, Rarity.COMMON, mage.cards.g.GuardiansOfOboro.class)); + cards.add(new SetCardInfo("Hand of Enlightenment", 11, Rarity.COMMON, mage.cards.h.HandOfEnlightenment.class)); + cards.add(new SetCardInfo("Harmonious Emergence", 190, Rarity.COMMON, mage.cards.h.HarmoniousEmergence.class)); + cards.add(new SetCardInfo("Heiko Yamazaki, the General", 146, Rarity.UNCOMMON, mage.cards.h.HeikoYamazakiTheGeneral.class)); + cards.add(new SetCardInfo("Heir of the Ancient Fang", 191, Rarity.COMMON, mage.cards.h.HeirOfTheAncientFang.class)); + cards.add(new SetCardInfo("Hidetsugu Consumes All", 221, Rarity.MYTHIC, mage.cards.h.HidetsuguConsumesAll.class)); + cards.add(new SetCardInfo("Hidetsugu, Devouring Chaos", 99, Rarity.RARE, mage.cards.h.HidetsuguDevouringChaos.class)); + cards.add(new SetCardInfo("High-Speed Hoverbike", 247, Rarity.UNCOMMON, mage.cards.h.HighSpeedHoverbike.class)); + cards.add(new SetCardInfo("Hinata, Dawn-Crowned", 222, Rarity.RARE, mage.cards.h.HinataDawnCrowned.class)); + cards.add(new SetCardInfo("Historian's Wisdom", 192, Rarity.UNCOMMON, mage.cards.h.HistoriansWisdom.class)); + cards.add(new SetCardInfo("Hotshot Mechanic", 16, Rarity.UNCOMMON, mage.cards.h.HotshotMechanic.class)); + cards.add(new SetCardInfo("Imperial Moth", 4, Rarity.COMMON, mage.cards.i.ImperialMoth.class)); + cards.add(new SetCardInfo("Imperial Oath", 17, Rarity.COMMON, mage.cards.i.ImperialOath.class)); + cards.add(new SetCardInfo("Imperial Recovery Unit", 18, Rarity.UNCOMMON, mage.cards.i.ImperialRecoveryUnit.class)); + cards.add(new SetCardInfo("Imperial Subduer", 19, Rarity.COMMON, mage.cards.i.ImperialSubduer.class)); + cards.add(new SetCardInfo("Inkrise Infiltrator", 100, Rarity.COMMON, mage.cards.i.InkriseInfiltrator.class)); + cards.add(new SetCardInfo("Intercessor's Arrest", 20, Rarity.COMMON, mage.cards.i.IntercessorsArrest.class)); + cards.add(new SetCardInfo("Inventive Iteration", 57, Rarity.RARE, mage.cards.i.InventiveIteration.class)); + cards.add(new SetCardInfo("Invigorating Hot Spring", 223, Rarity.UNCOMMON, mage.cards.i.InvigoratingHotSpring.class)); + cards.add(new SetCardInfo("Invoke Despair", 101, Rarity.RARE, mage.cards.i.InvokeDespair.class)); + cards.add(new SetCardInfo("Invoke Justice", 21, Rarity.RARE, mage.cards.i.InvokeJustice.class)); + cards.add(new SetCardInfo("Invoke the Ancients", 193, Rarity.RARE, mage.cards.i.InvokeTheAncients.class)); + cards.add(new SetCardInfo("Invoke the Winds", 58, Rarity.RARE, mage.cards.i.InvokeTheWinds.class)); + cards.add(new SetCardInfo("Iron Apprentice", 248, Rarity.COMMON, mage.cards.i.IronApprentice.class)); + cards.add(new SetCardInfo("Ironhoof Boar", 148, Rarity.COMMON, mage.cards.i.IronhoofBoar.class)); + cards.add(new SetCardInfo("Island", 285, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Isshin, Two Heavens as One", 224, Rarity.RARE, mage.cards.i.IsshinTwoHeavensAsOne.class)); + cards.add(new SetCardInfo("Jin-Gitaxias, Progress Tyrant", 59, Rarity.MYTHIC, mage.cards.j.JinGitaxiasProgressTyrant.class)); + cards.add(new SetCardInfo("Jugan Defends the Temple", 194, Rarity.MYTHIC, mage.cards.j.JuganDefendsTheTemple.class)); + cards.add(new SetCardInfo("Jukai Naturalist", 225, Rarity.UNCOMMON, mage.cards.j.JukaiNaturalist.class)); + cards.add(new SetCardInfo("Jukai Preserver", 195, Rarity.COMMON, mage.cards.j.JukaiPreserver.class)); + cards.add(new SetCardInfo("Jukai Trainee", 196, Rarity.COMMON, mage.cards.j.JukaiTrainee.class)); + cards.add(new SetCardInfo("Jungle Hollow", 269, Rarity.COMMON, mage.cards.j.JungleHollow.class)); + cards.add(new SetCardInfo("Junji, the Midnight Sky", 102, Rarity.MYTHIC, mage.cards.j.JunjiTheMidnightSky.class)); + cards.add(new SetCardInfo("Kairi, the Swirling Sky", 60, Rarity.MYTHIC, mage.cards.k.KairiTheSwirlingSky.class)); + cards.add(new SetCardInfo("Kaito Shizuki", 226, Rarity.MYTHIC, mage.cards.k.KaitoShizuki.class)); + cards.add(new SetCardInfo("Kaito's Pursuit", 103, Rarity.COMMON, mage.cards.k.KaitosPursuit.class)); + cards.add(new SetCardInfo("Kami of Industry", 149, Rarity.COMMON, mage.cards.k.KamiOfIndustry.class)); + cards.add(new SetCardInfo("Kami of Restless Shadows", 104, Rarity.COMMON, mage.cards.k.KamiOfRestlessShadows.class)); + cards.add(new SetCardInfo("Kami of Terrible Secrets", 105, Rarity.COMMON, mage.cards.k.KamiOfTerribleSecrets.class)); + cards.add(new SetCardInfo("Kami of Transience", 197, Rarity.RARE, mage.cards.k.KamiOfTransience.class)); + cards.add(new SetCardInfo("Kami's Flare", 150, Rarity.COMMON, mage.cards.k.KamisFlare.class)); + cards.add(new SetCardInfo("Kappa Tech-Wrecker", 198, Rarity.UNCOMMON, mage.cards.k.KappaTechWrecker.class)); + cards.add(new SetCardInfo("Kindled Fury", 151, Rarity.COMMON, mage.cards.k.KindledFury.class)); + cards.add(new SetCardInfo("Kirin-Touched Orochi", 212, Rarity.RARE, mage.cards.k.KirinTouchedOrochi.class)); + cards.add(new SetCardInfo("Kitsune Ace", 22, Rarity.COMMON, mage.cards.k.KitsuneAce.class)); + cards.add(new SetCardInfo("Kodama of the West Tree", 199, Rarity.MYTHIC, mage.cards.k.KodamaOfTheWestTree.class)); + cards.add(new SetCardInfo("Kotose, the Silent Spider", 351, Rarity.RARE, mage.cards.k.KotoseTheSilentSpider.class)); + cards.add(new SetCardInfo("Kumano Faces Kakkazan", 152, Rarity.UNCOMMON, mage.cards.k.KumanoFacesKakkazan.class)); + cards.add(new SetCardInfo("Kura, the Boundless Sky", 200, Rarity.MYTHIC, mage.cards.k.KuraTheBoundlessSky.class)); + cards.add(new SetCardInfo("Kyodai, Soul of Kamigawa", 23, Rarity.RARE, mage.cards.k.KyodaiSoulOfKamigawa.class)); + cards.add(new SetCardInfo("Leech Gauntlet", 106, Rarity.UNCOMMON, mage.cards.l.LeechGauntlet.class)); + cards.add(new SetCardInfo("Lethal Exploit", 107, Rarity.COMMON, mage.cards.l.LethalExploit.class)); + cards.add(new SetCardInfo("Life of Toshiro Umezawa", 108, Rarity.UNCOMMON, mage.cards.l.LifeOfToshiroUmezawa.class)); + cards.add(new SetCardInfo("Light the Way", 24, Rarity.COMMON, mage.cards.l.LightTheWay.class)); + cards.add(new SetCardInfo("Light-Paws, Emperor's Voice", 25, Rarity.RARE, mage.cards.l.LightPawsEmperorsVoice.class)); + cards.add(new SetCardInfo("Likeness of the Seeker", 172, Rarity.UNCOMMON, mage.cards.l.LikenessOfTheSeeker.class)); + cards.add(new SetCardInfo("Lion Sash", 26, Rarity.RARE, mage.cards.l.LionSash.class)); + cards.add(new SetCardInfo("Living Breakthrough", 57, Rarity.RARE, mage.cards.l.LivingBreakthrough.class)); + cards.add(new SetCardInfo("Lizard Blades", 153, Rarity.RARE, mage.cards.l.LizardBlades.class)); + cards.add(new SetCardInfo("Lucky Offering", 27, Rarity.COMMON, mage.cards.l.LuckyOffering.class)); + cards.add(new SetCardInfo("Malicious Malfunction", 110, Rarity.UNCOMMON, mage.cards.m.MaliciousMalfunction.class)); + cards.add(new SetCardInfo("March of Burgeoning Life", 201, Rarity.RARE, mage.cards.m.MarchOfBurgeoningLife.class)); + cards.add(new SetCardInfo("March of Otherworldly Light", 28, Rarity.RARE, mage.cards.m.MarchOfOtherworldlyLight.class)); + cards.add(new SetCardInfo("March of Reckless Joy", 154, Rarity.RARE, mage.cards.m.MarchOfRecklessJoy.class)); + cards.add(new SetCardInfo("March of Swirling Mist", 61, Rarity.RARE, mage.cards.m.MarchOfSwirlingMist.class)); + cards.add(new SetCardInfo("March of Wretched Sorrow", 111, Rarity.RARE, mage.cards.m.MarchOfWretchedSorrow.class)); + cards.add(new SetCardInfo("Master's Rebuke", 202, Rarity.COMMON, mage.cards.m.MastersRebuke.class)); + cards.add(new SetCardInfo("Mech Hangar", 270, Rarity.UNCOMMON, mage.cards.m.MechHangar.class)); + cards.add(new SetCardInfo("Mechtitan Core", 249, Rarity.RARE, mage.cards.m.MechtitanCore.class)); + cards.add(new SetCardInfo("Memory of Toshiro", 108, Rarity.UNCOMMON, mage.cards.m.MemoryOfToshiro.class)); + cards.add(new SetCardInfo("Michiko's Reign of Truth", 29, Rarity.UNCOMMON, mage.cards.m.MichikosReignOfTruth.class)); + cards.add(new SetCardInfo("Mindlink Mech", 62, Rarity.RARE, mage.cards.m.MindlinkMech.class)); + cards.add(new SetCardInfo("Mirror Box", 250, Rarity.RARE, mage.cards.m.MirrorBox.class)); + cards.add(new SetCardInfo("Mirrorshell Crab", 63, Rarity.COMMON, mage.cards.m.MirrorshellCrab.class)); + cards.add(new SetCardInfo("Mnemonic Sphere", 64, Rarity.COMMON, mage.cards.m.MnemonicSphere.class)); + cards.add(new SetCardInfo("Mobilizer Mech", 65, Rarity.UNCOMMON, mage.cards.m.MobilizerMech.class)); + cards.add(new SetCardInfo("Moon-Circuit Hacker", 67, Rarity.COMMON, mage.cards.m.MoonCircuitHacker.class)); + cards.add(new SetCardInfo("Moonfolk Puzzlemaker", 68, Rarity.COMMON, mage.cards.m.MoonfolkPuzzlemaker.class)); + cards.add(new SetCardInfo("Moonsnare Prototype", 69, Rarity.COMMON, mage.cards.m.MoonsnarePrototype.class)); + cards.add(new SetCardInfo("Moonsnare Specialist", 70, Rarity.COMMON, mage.cards.m.MoonsnareSpecialist.class)); + cards.add(new SetCardInfo("Mothrider Patrol", 30, Rarity.COMMON, mage.cards.m.MothriderPatrol.class)); + cards.add(new SetCardInfo("Mountain", 289, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mukotai Ambusher", 112, Rarity.COMMON, mage.cards.m.MukotaiAmbusher.class)); + cards.add(new SetCardInfo("Mukotai Soulripper", 113, Rarity.RARE, mage.cards.m.MukotaiSoulripper.class)); + cards.add(new SetCardInfo("Nameless Conqueror", 162, Rarity.COMMON, mage.cards.n.NamelessConqueror.class)); + cards.add(new SetCardInfo("Naomi, Pillar of Order", 229, Rarity.UNCOMMON, mage.cards.n.NaomiPillarOfOrder.class)); + cards.add(new SetCardInfo("Nashi, Moon Sage's Scion", 114, Rarity.MYTHIC, mage.cards.n.NashiMoonSagesScion.class)); + cards.add(new SetCardInfo("Network Disruptor", 71, Rarity.COMMON, mage.cards.n.NetworkDisruptor.class)); + cards.add(new SetCardInfo("Network Terminal", 251, Rarity.COMMON, mage.cards.n.NetworkTerminal.class)); + cards.add(new SetCardInfo("Nezumi Bladeblesser", 115, Rarity.COMMON, mage.cards.n.NezumiBladeblesser.class)); + cards.add(new SetCardInfo("Nezumi Prowler", 116, Rarity.UNCOMMON, mage.cards.n.NezumiProwler.class)); + cards.add(new SetCardInfo("Nezumi Road Captain", 117, Rarity.COMMON, mage.cards.n.NezumiRoadCaptain.class)); + cards.add(new SetCardInfo("Ninja's Kunai", 252, Rarity.COMMON, mage.cards.n.NinjasKunai.class)); + cards.add(new SetCardInfo("Norika Yamazaki, the Poet", 31, Rarity.UNCOMMON, mage.cards.n.NorikaYamazakiThePoet.class)); + cards.add(new SetCardInfo("O-Kagachi Made Manifest", 227, Rarity.MYTHIC, mage.cards.o.OKagachiMadeManifest.class)); + cards.add(new SetCardInfo("Ogre-Head Helm", 155, Rarity.RARE, mage.cards.o.OgreHeadHelm.class)); + cards.add(new SetCardInfo("Okiba Reckoner Raid", 117, Rarity.COMMON, mage.cards.o.OkibaReckonerRaid.class)); + cards.add(new SetCardInfo("Okiba Salvage", 118, Rarity.UNCOMMON, mage.cards.o.OkibaSalvage.class)); + cards.add(new SetCardInfo("Oni-Cult Anvil", 230, Rarity.UNCOMMON, mage.cards.o.OniCultAnvil.class)); + cards.add(new SetCardInfo("Orochi Merge-Keeper", 203, Rarity.UNCOMMON, mage.cards.o.OrochiMergeKeeper.class)); + cards.add(new SetCardInfo("Otawara, Soaring City", 271, Rarity.RARE, mage.cards.o.OtawaraSoaringCity.class)); + cards.add(new SetCardInfo("Papercraft Decoy", 253, Rarity.COMMON, mage.cards.p.PapercraftDecoy.class)); + cards.add(new SetCardInfo("Patchwork Automaton", 254, Rarity.UNCOMMON, mage.cards.p.PatchworkAutomaton.class)); + cards.add(new SetCardInfo("Peerless Samurai", 156, Rarity.COMMON, mage.cards.p.PeerlessSamurai.class)); + cards.add(new SetCardInfo("Plains", 283, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Planar Incision", 72, Rarity.COMMON, mage.cards.p.PlanarIncision.class)); + cards.add(new SetCardInfo("Portrait of Michiko", 29, Rarity.UNCOMMON, mage.cards.p.PortraitOfMichiko.class)); + cards.add(new SetCardInfo("Prodigy's Prototype", 231, Rarity.UNCOMMON, mage.cards.p.ProdigysPrototype.class)); + cards.add(new SetCardInfo("Prosperous Thief", 73, Rarity.UNCOMMON, mage.cards.p.ProsperousThief.class)); + cards.add(new SetCardInfo("Rabbit Battery", 157, Rarity.UNCOMMON, mage.cards.r.RabbitBattery.class)); + cards.add(new SetCardInfo("Raiyuu, Storm's Edge", 232, Rarity.RARE, mage.cards.r.RaiyuuStormsEdge.class)); + cards.add(new SetCardInfo("Reality Heist", 75, Rarity.UNCOMMON, mage.cards.r.RealityHeist.class)); + cards.add(new SetCardInfo("Reckoner Bankbuster", 255, Rarity.RARE, mage.cards.r.ReckonerBankbuster.class)); + cards.add(new SetCardInfo("Reckoner Shakedown", 119, Rarity.COMMON, mage.cards.r.ReckonerShakedown.class)); + cards.add(new SetCardInfo("Reckoner's Bargain", 120, Rarity.COMMON, mage.cards.r.ReckonersBargain.class)); + cards.add(new SetCardInfo("Reflection of Kiki-Jiki", 141, Rarity.RARE, mage.cards.r.ReflectionOfKikiJiki.class)); + cards.add(new SetCardInfo("Regent's Authority", 32, Rarity.COMMON, mage.cards.r.RegentsAuthority.class)); + cards.add(new SetCardInfo("Reinforced Ronin", 158, Rarity.UNCOMMON, mage.cards.r.ReinforcedRonin.class)); + cards.add(new SetCardInfo("Reito Sentinel", 256, Rarity.UNCOMMON, mage.cards.r.ReitoSentinel.class)); + cards.add(new SetCardInfo("Remnant of the Rising Star", 194, Rarity.MYTHIC, mage.cards.r.RemnantOfTheRisingStar.class)); + cards.add(new SetCardInfo("Repel the Vile", 33, Rarity.COMMON, mage.cards.r.RepelTheVile.class)); + cards.add(new SetCardInfo("Replication Specialist", 76, Rarity.UNCOMMON, mage.cards.r.ReplicationSpecialist.class)); + cards.add(new SetCardInfo("Return to Action", 121, Rarity.COMMON, mage.cards.r.ReturnToAction.class)); + cards.add(new SetCardInfo("Risona, Asari Commander", 233, Rarity.RARE, mage.cards.r.RisonaAsariCommander.class)); + cards.add(new SetCardInfo("Roadside Reliquary", 272, Rarity.UNCOMMON, mage.cards.r.RoadsideReliquary.class)); + cards.add(new SetCardInfo("Roaring Earth", 204, Rarity.UNCOMMON, mage.cards.r.RoaringEarth.class)); + cards.add(new SetCardInfo("Rugged Highlands", 273, Rarity.COMMON, mage.cards.r.RuggedHighlands.class)); + cards.add(new SetCardInfo("Runaway Trash-Bot", 257, Rarity.UNCOMMON, mage.cards.r.RunawayTrashBot.class)); + cards.add(new SetCardInfo("Saiba Trespassers", 77, Rarity.COMMON, mage.cards.s.SaibaTrespassers.class)); + cards.add(new SetCardInfo("Satoru Umezawa", 234, Rarity.RARE, mage.cards.s.SatoruUmezawa.class)); + cards.add(new SetCardInfo("Satsuki, the Living Lore", 235, Rarity.RARE, mage.cards.s.SatsukiTheLivingLore.class)); + cards.add(new SetCardInfo("Scoured Barrens", 274, Rarity.COMMON, mage.cards.s.ScouredBarrens.class)); + cards.add(new SetCardInfo("Scrap Welder", 159, Rarity.RARE, mage.cards.s.ScrapWelder.class)); + cards.add(new SetCardInfo("Scrapyard Steelbreaker", 160, Rarity.COMMON, mage.cards.s.ScrapyardSteelbreaker.class)); + cards.add(new SetCardInfo("Searchlight Companion", 258, Rarity.COMMON, mage.cards.s.SearchlightCompanion.class)); + cards.add(new SetCardInfo("Season of Renewal", 205, Rarity.COMMON, mage.cards.s.SeasonOfRenewal.class)); + cards.add(new SetCardInfo("Secluded Courtyard", 275, Rarity.UNCOMMON, mage.cards.s.SecludedCourtyard.class)); + cards.add(new SetCardInfo("Seismic Wave", 161, Rarity.UNCOMMON, mage.cards.s.SeismicWave.class)); + cards.add(new SetCardInfo("Selfless Samurai", 35, Rarity.UNCOMMON, mage.cards.s.SelflessSamurai.class)); + cards.add(new SetCardInfo("Seshiro's Living Legacy", 210, Rarity.COMMON, mage.cards.s.SeshirosLivingLegacy.class)); + cards.add(new SetCardInfo("Seven-Tail Mentor", 36, Rarity.COMMON, mage.cards.s.SevenTailMentor.class)); + cards.add(new SetCardInfo("Shigeki, Jukai Visionary", 206, Rarity.RARE, mage.cards.s.ShigekiJukaiVisionary.class)); + cards.add(new SetCardInfo("Short Circuit", 78, Rarity.COMMON, mage.cards.s.ShortCircuit.class)); + cards.add(new SetCardInfo("Shrine Steward", 259, Rarity.COMMON, mage.cards.s.ShrineSteward.class)); + cards.add(new SetCardInfo("Silver-Fur Master", 236, Rarity.UNCOMMON, mage.cards.s.SilverFurMaster.class)); + cards.add(new SetCardInfo("Simian Sling", 163, Rarity.COMMON, mage.cards.s.SimianSling.class)); + cards.add(new SetCardInfo("Sky-Blessed Samurai", 37, Rarity.UNCOMMON, mage.cards.s.SkyBlessedSamurai.class)); + cards.add(new SetCardInfo("Skyswimmer Koi", 79, Rarity.COMMON, mage.cards.s.SkyswimmerKoi.class)); + cards.add(new SetCardInfo("Sokenzan Smelter", 164, Rarity.UNCOMMON, mage.cards.s.SokenzanSmelter.class)); + cards.add(new SetCardInfo("Sokenzan, Crucible of Defiance", 276, Rarity.RARE, mage.cards.s.SokenzanCrucibleOfDefiance.class)); + cards.add(new SetCardInfo("Soul Transfer", 122, Rarity.RARE, mage.cards.s.SoulTransfer.class)); + cards.add(new SetCardInfo("Spell Pierce", 80, Rarity.COMMON, mage.cards.s.SpellPierce.class)); + cards.add(new SetCardInfo("Spinning Wheel Kick", 207, Rarity.UNCOMMON, mage.cards.s.SpinningWheelKick.class)); + cards.add(new SetCardInfo("Spirit-Sister's Call", 237, Rarity.MYTHIC, mage.cards.s.SpiritSistersCall.class)); + cards.add(new SetCardInfo("Spirited Companion", 38, Rarity.COMMON, mage.cards.s.SpiritedCompanion.class)); + cards.add(new SetCardInfo("Spring-Leaf Avenger", 208, Rarity.RARE, mage.cards.s.SpringLeafAvenger.class)); + cards.add(new SetCardInfo("Storyweave", 209, Rarity.UNCOMMON, mage.cards.s.Storyweave.class)); + cards.add(new SetCardInfo("Suit Up", 81, Rarity.COMMON, mage.cards.s.SuitUp.class)); + cards.add(new SetCardInfo("Sunblade Samurai", 39, Rarity.COMMON, mage.cards.s.SunbladeSamurai.class)); + cards.add(new SetCardInfo("Surgehacker Mech", 260, Rarity.RARE, mage.cards.s.SurgehackerMech.class)); + cards.add(new SetCardInfo("Swamp", 287, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swiftwater Cliffs", 277, Rarity.COMMON, mage.cards.s.SwiftwaterCliffs.class)); + cards.add(new SetCardInfo("Takenuma, Abandoned Mire", 278, Rarity.RARE, mage.cards.t.TakenumaAbandonedMire.class)); + cards.add(new SetCardInfo("Tales of Master Seshiro", 210, Rarity.COMMON, mage.cards.t.TalesOfMasterSeshiro.class)); + cards.add(new SetCardInfo("Tameshi, Reality Architect", 82, Rarity.RARE, mage.cards.t.TameshiRealityArchitect.class)); + cards.add(new SetCardInfo("Tamiyo's Compleation", 83, Rarity.COMMON, mage.cards.t.TamiyosCompleation.class)); + cards.add(new SetCardInfo("Tamiyo's Safekeeping", 211, Rarity.COMMON, mage.cards.t.TamiyosSafekeeping.class)); + cards.add(new SetCardInfo("Tamiyo, Compleated Sage", 238, Rarity.MYTHIC, mage.cards.t.TamiyoCompleatedSage.class)); + cards.add(new SetCardInfo("Tatsunari, Toad Rider", 123, Rarity.RARE, mage.cards.t.TatsunariToadRider.class)); + cards.add(new SetCardInfo("Teachings of the Kirin", 212, Rarity.RARE, mage.cards.t.TeachingsOfTheKirin.class)); + cards.add(new SetCardInfo("Tempered in Solitude", 165, Rarity.UNCOMMON, mage.cards.t.TemperedInSolitude.class)); + cards.add(new SetCardInfo("Tezzeret, Betrayer of Flesh", 84, Rarity.MYTHIC, mage.cards.t.TezzeretBetrayerOfFlesh.class)); + cards.add(new SetCardInfo("The Fall of Lord Konda", 12, Rarity.UNCOMMON, mage.cards.t.TheFallOfLordKonda.class)); + cards.add(new SetCardInfo("The Kami War", 227, Rarity.MYTHIC, mage.cards.t.TheKamiWar.class)); + cards.add(new SetCardInfo("The Long Reach of Night", 109, Rarity.UNCOMMON, mage.cards.t.TheLongReachOfNight.class)); + cards.add(new SetCardInfo("The Modern Age", 66, Rarity.COMMON, mage.cards.t.TheModernAge.class)); + cards.add(new SetCardInfo("The Reality Chip", 74, Rarity.RARE, mage.cards.t.TheRealityChip.class)); + cards.add(new SetCardInfo("The Restoration of Eiganjo", 34, Rarity.RARE, mage.cards.t.TheRestorationOfEiganjo.class)); + cards.add(new SetCardInfo("The Shattered States Era", 162, Rarity.COMMON, mage.cards.t.TheShatteredStatesEra.class)); + cards.add(new SetCardInfo("The Wandering Emperor", 42, Rarity.MYTHIC, mage.cards.t.TheWanderingEmperor.class)); + cards.add(new SetCardInfo("Thirst for Knowledge", 85, Rarity.UNCOMMON, mage.cards.t.ThirstForKnowledge.class)); + cards.add(new SetCardInfo("Thornwood Falls", 279, Rarity.COMMON, mage.cards.t.ThornwoodFalls.class)); + cards.add(new SetCardInfo("Thousand-Faced Shadow", 86, Rarity.RARE, mage.cards.t.ThousandFacedShadow.class)); + cards.add(new SetCardInfo("Thundering Raiju", 166, Rarity.RARE, mage.cards.t.ThunderingRaiju.class)); + cards.add(new SetCardInfo("Thundersteel Colossus", 261, Rarity.COMMON, mage.cards.t.ThundersteelColossus.class)); + cards.add(new SetCardInfo("Touch the Spirit Realm", 40, Rarity.UNCOMMON, mage.cards.t.TouchTheSpiritRealm.class)); + cards.add(new SetCardInfo("Towashi Guide-Bot", 262, Rarity.UNCOMMON, mage.cards.t.TowashiGuideBot.class)); + cards.add(new SetCardInfo("Towashi Songshaper", 167, Rarity.COMMON, mage.cards.t.TowashiSongshaper.class)); + cards.add(new SetCardInfo("Tranquil Cove", 280, Rarity.COMMON, mage.cards.t.TranquilCove.class)); + cards.add(new SetCardInfo("Tribute to Horobi", 124, Rarity.RARE, mage.cards.t.TributeToHorobi.class)); + cards.add(new SetCardInfo("Twinshot Sniper", 168, Rarity.UNCOMMON, mage.cards.t.TwinshotSniper.class)); + cards.add(new SetCardInfo("Twisted Embrace", 125, Rarity.COMMON, mage.cards.t.TwistedEmbrace.class)); + cards.add(new SetCardInfo("Uncharted Haven", 281, Rarity.COMMON, mage.cards.u.UnchartedHaven.class)); + cards.add(new SetCardInfo("Undercity Scrounger", 126, Rarity.COMMON, mage.cards.u.UndercityScrounger.class)); + cards.add(new SetCardInfo("Unforgiving One", 127, Rarity.UNCOMMON, mage.cards.u.UnforgivingOne.class)); + cards.add(new SetCardInfo("Unstoppable Ogre", 169, Rarity.COMMON, mage.cards.u.UnstoppableOgre.class)); + cards.add(new SetCardInfo("Upriser Renegade", 170, Rarity.UNCOMMON, mage.cards.u.UpriserRenegade.class)); + cards.add(new SetCardInfo("Vector Glider", 66, Rarity.COMMON, mage.cards.v.VectorGlider.class)); + cards.add(new SetCardInfo("Vessel of the All-Consuming", 221, Rarity.MYTHIC, mage.cards.v.VesselOfTheAllConsuming.class)); + cards.add(new SetCardInfo("Virus Beetle", 128, Rarity.COMMON, mage.cards.v.VirusBeetle.class)); + cards.add(new SetCardInfo("Vision of the Unspeakable", 48, Rarity.UNCOMMON, mage.cards.v.VisionOfTheUnspeakable.class)); + cards.add(new SetCardInfo("Voltage Surge", 171, Rarity.COMMON, mage.cards.v.VoltageSurge.class)); + cards.add(new SetCardInfo("Walking Skyscraper", 263, Rarity.UNCOMMON, mage.cards.w.WalkingSkyscraper.class)); + cards.add(new SetCardInfo("Wanderer's Intervention", 41, Rarity.COMMON, mage.cards.w.WanderersIntervention.class)); + cards.add(new SetCardInfo("Weaver of Harmony", 213, Rarity.RARE, mage.cards.w.WeaverOfHarmony.class)); + cards.add(new SetCardInfo("Webspinner Cuff", 214, Rarity.UNCOMMON, mage.cards.w.WebspinnerCuff.class)); + cards.add(new SetCardInfo("When We Were Young", 43, Rarity.UNCOMMON, mage.cards.w.WhenWeWereYoung.class)); + cards.add(new SetCardInfo("Wind-Scarred Crag", 282, Rarity.COMMON, mage.cards.w.WindScarredCrag.class)); + cards.add(new SetCardInfo("You Are Already Dead", 129, Rarity.COMMON, mage.cards.y.YouAreAlreadyDead.class)); + } +} diff --git a/Mage.Sets/src/mage/sets/KhansOfTarkir.java b/Mage.Sets/src/mage/sets/KhansOfTarkir.java index 101aa00c42a..87936ce2ef6 100644 --- a/Mage.Sets/src/mage/sets/KhansOfTarkir.java +++ b/Mage.Sets/src/mage/sets/KhansOfTarkir.java @@ -1,10 +1,16 @@ - package mage.sets; import mage.cards.ExpansionSet; +import mage.collation.BoosterCollator; +import mage.collation.BoosterStructure; +import mage.collation.CardRun; +import mage.collation.RarityConfiguration; import mage.constants.Rarity; import mage.constants.SetType; +import java.util.ArrayList; +import java.util.List; + /** * * @author LevelX2 @@ -298,4 +304,113 @@ public final class KhansOfTarkir extends ExpansionSet { cards.add(new SetCardInfo("Zurgo Helmsmasher", 214, Rarity.MYTHIC, mage.cards.z.ZurgoHelmsmasher.class)); } + @Override + public BoosterCollator createCollator() { + return new KhansOfTarkirCollator(); + } +} + +// Booster collation info from https://www.lethe.xyz/mtg/collation/ktk.html +// Using USA collation for all rarities +// Foil rare sheet used for regular rares as regular rare sheet is not known +class KhansOfTarkirCollator implements BoosterCollator { + private final CardRun commonA = new CardRun(true, "37", "105", "24", "59", "98", "17", "41", "120", "11", "62", "125", "9", "57", "124", "3", "63", "117", "7", "35", "96", "10", "33", "100", "16", "47", "114", "19", "37", "122", "20", "61", "101", "24", "41", "105", "21", "52", "98", "17", "63", "120", "3", "62", "125", "11", "47", "114", "16", "59", "117", "9", "57", "100", "20", "35", "96", "7", "52", "101", "10", "33", "122", "19", "61", "124", "21"); + private final CardRun commonB = new CardRun(true, "91", "129", "71", "137", "70", "128", "86", "131", "80", "158", "77", "139", "78", "132", "87", "147", "68", "151", "90", "129", "65", "146", "71", "128", "70", "127", "91", "137", "86", "131", "80", "158", "77", "132", "65", "139", "87", "128", "71", "147", "68", "151", "91", "137", "86", "146", "78", "129", "80", "131", "90", "127", "87", "151", "70", "158", "65", "139", "78", "132", "68", "146", "77", "147", "90", "127"); + private final CardRun commonC1 = new CardRun(true, "215", "39", "232", "123", "175", "29", "229", "48", "121", "225", "4", "244", "44", "201", "95", "247", "15", "226", "40", "103", "240", "14", "159", "55", "232", "102", "223", "29", "242", "39", "95", "175", "23", "244", "44", "215", "123", "229", "4", "159", "48", "121", "240", "15", "225", "40", "247", "103", "201", "23", "226", "55", "102", "242", "14"); + private final CardRun commonC2 = new CardRun(true, "94", "135", "246", "84", "222", "142", "231", "76", "191", "140", "243", "93", "148", "223", "89", "235", "135", "162", "94", "155", "246", "84", "224", "142", "231", "93", "191", "148", "222", "76", "140", "235", "94", "224", "135", "243", "76", "162", "155", "246", "89", "222", "142", "235", "84", "191", "140", "231", "93", "148", "224", "89", "243", "155", "162"); + private final CardRun uncommonA = new CardRun(true, "32", "170", "27", "97", "169", "1", "56", "28", "51", "104", "213", "111", "188", "115", "32", "109", "27", "56", "97", "6", "107", "46", "1", "183", "115", "5", "213", "28", "116", "169", "104", "53", "188", "97", "183", "46", "111", "5", "51", "213", "109", "1", "53", "170", "116", "6", "46", "188", "107", "27", "51", "115", "28", "32", "169", "111", "170", "56", "109", "5", "116", "53", "107", "183", "104", "6"); + private final CardRun uncommonB = new CardRun(true, "161", "88", "145", "208", "130", "186", "138", "79", "218", "75", "238", "150", "64", "161", "74", "152", "88", "217", "165", "130", "181", "143", "92", "238", "79", "208", "75", "145", "236", "74", "138", "181", "241", "150", "161", "79", "152", "64", "217", "130", "238", "143", "218", "165", "241", "74", "208", "186", "152", "181", "88", "236", "92", "138", "218", "64", "145", "165", "143", "186", "241", "75", "150", "92", "217", "236"); + private final CardRun uncommonC = new CardRun(true, "194", "60", "26", "110", "118", "198", "30", "126", "177", "50", "2", "38", "194", "25", "178", "26", "172", "30", "54", "22", "177", "110", "198", "126", "43", "178", "38", "25", "50", "26", "194", "30", "118", "60", "172", "54", "110", "2", "198", "38", "22", "177", "43", "25", "60", "118", "50", "178", "54", "2", "126", "172", "43", "22"); + private final CardRun uncommonD = new CardRun(true, "67", "228", "212", "234", "81", "156", "205", "134", "245", "204", "221", "153", "187", "69", "167", "228", "212", "234", "134", "83", "81", "245", "153", "67", "156", "237", "167", "221", "187", "212", "69", "204", "157", "245", "205", "67", "134", "167", "83", "237", "153", "69", "228", "157", "234", "187", "156", "221", "81", "205", "83", "204", "237", "157"); + private final CardRun rare = new CardRun(true, "214", "18", "163", "108", "209", "219", "180", "73", "164", "141", "166", "133", "210", "248", "85", "206", "42", "173", "31", "182", "230", "195", "113", "203", "133", "199", "249", "211", "219", "197", "34", "171", "12", "106", "192", "149", "179", "8", "233", "202", "82", "112", "211", "42", "230", "196", "106", "173", "239", "174", "216", "185", "13", "171", "36", "144", "184", "119", "164", "233", "192", "73", "168", "49", "174", "13", "249", "179", "108", "18", "190", "58", "200", "220", "193", "112", "239", "196", "45", "160", "248", "168", "113", "176", "66", "58", "193", "154", "200", "8", "160", "36", "185", "82", "184", "220", "207", "141", "182", "227", "189", "154", "210", "85", "180", "216", "203", "45", "166", "99", "207", "72", "209", "136", "176", "144", "197", "12", "189", "66", "195"); + private final CardRun land = new CardRun(false, "250", "251", "252", "253", "254", "255", "256", "257", "258", "259", "260", "261", "262", "263", "264", "265", "266", "267", "268", "269"); + + private final BoosterStructure AABBC1C1C1C1C1C1 = new BoosterStructure( + commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AAABBC1C1C1C1C1 = new BoosterStructure( + commonA, commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AAAABBC2C2C2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, + commonC2, commonC2, commonC2, commonC2 + ); + private final BoosterStructure AAAABBBC2C2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, commonB, + commonC2, commonC2, commonC2 + ); + private final BoosterStructure AAAABBBBC2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, commonB, commonB, + commonC2, commonC2 + ); + private final BoosterStructure ABC = new BoosterStructure(uncommonA, uncommonB, uncommonC); + private final BoosterStructure ABD = new BoosterStructure(uncommonA, uncommonB, uncommonD); + private final BoosterStructure ACD = new BoosterStructure(uncommonA, uncommonC, uncommonD); + private final BoosterStructure BCD = new BoosterStructure(uncommonB, uncommonC, uncommonD); + private final BoosterStructure R1 = new BoosterStructure(rare); + private final BoosterStructure L1 = new BoosterStructure(land); + + // In order for equal numbers of each common to exist, the average booster must contain: + // 3.27 A commons (36 / 11) + // 2.18 B commons (24 / 11) + // 2.73 C1 commons (30 / 11, or 60 / 11 in each C1 booster) + // 1.82 C2 commons (20 / 11, or 40 / 11 in each C2 booster) + // These numbers are the same for all sets with 101 commons in A/B/C1/C2 print runs + // and with 10 common slots per booster + private final RarityConfiguration commonRuns = new RarityConfiguration( + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBBC2C2C2, + AAAABBBC2C2C2, + AAAABBBBC2C2 + ); + // In order for equal numbers of each uncommon to exist, the average booster must contain: + // 0.825 A uncommons (33 / 40) + // 0.825 B uncommons (33 / 40) + // 0.675 C uncommons (27 / 40) + // 0.675 D uncommons (27 / 40) + // These numbers are the same for all sets with 80 uncommons in asymmetrical A/B/C/D print runs + private final RarityConfiguration uncommonRuns = new RarityConfiguration( + ABC, ABD, ABC, ABD, ABC, ABD, ABC, ABD, ABC, ABD, ABC, ABD, ABC, + ABD, ABC, ABD, ABC, ABD, ABC, ABD, ABC, ABD, ABC, ABD, ABC, ABD, + ACD, BCD, ACD, BCD, ACD, BCD, ACD, + BCD, ACD, BCD, ACD, BCD, ACD, BCD + ); + private final RarityConfiguration rareRuns = new RarityConfiguration(R1); + private final RarityConfiguration landRuns = new RarityConfiguration(L1); + + @Override + public List makeBooster() { + List booster = new ArrayList<>(); + booster.addAll(commonRuns.getNext().makeRun()); + booster.addAll(uncommonRuns.getNext().makeRun()); + booster.addAll(rareRuns.getNext().makeRun()); + booster.addAll(landRuns.getNext().makeRun()); + return booster; + } } diff --git a/Mage.Sets/src/mage/sets/Magic2014.java b/Mage.Sets/src/mage/sets/Magic2014.java index be6793a89bd..3ab71f70a04 100644 --- a/Mage.Sets/src/mage/sets/Magic2014.java +++ b/Mage.Sets/src/mage/sets/Magic2014.java @@ -1,9 +1,16 @@ package mage.sets; import mage.cards.ExpansionSet; +import mage.collation.BoosterCollator; +import mage.collation.BoosterStructure; +import mage.collation.CardRun; +import mage.collation.RarityConfiguration; import mage.constants.Rarity; import mage.constants.SetType; +import java.util.ArrayList; +import java.util.List; + /** * * @author North @@ -274,4 +281,101 @@ public final class Magic2014 extends ExpansionSet { cards.add(new SetCardInfo("Young Pyromancer", 163, Rarity.UNCOMMON, mage.cards.y.YoungPyromancer.class)); cards.add(new SetCardInfo("Zephyr Charge", 82, Rarity.COMMON, mage.cards.z.ZephyrCharge.class)); } + + @Override + public BoosterCollator createCollator() { + return new Magic2014Collator(); + } +} + +// Booster collation info from https://www.lethe.xyz/mtg/collation/m14.html +// Using USA collation for all rarities +// Foil rare sheet used for regular rares as regular rare sheet is not known +class Magic2014Collator implements BoosterCollator { + private final CardRun commonA = new CardRun(true, "119", "149", "199", "89", "4", "171", "152", "52", "98", "24", "74", "156", "191", "83", "56", "30", "114", "145", "197", "186", "4", "87", "156", "64", "89", "183", "11", "55", "152", "119", "56", "188", "24", "155", "114", "74", "37", "199", "46", "145", "90", "40", "98", "191", "149", "55", "183", "37", "83", "135", "64", "186", "82", "30", "87", "155", "46", "171", "197", "11", "90", "135", "52", "188", "40", "82"); + private final CardRun commonB = new CardRun(true, "192", "63", "34", "136", "43", "175", "144", "121", "15", "65", "92", "179", "133", "150", "193", "43", "97", "34", "144", "167", "121", "63", "92", "33", "136", "175", "44", "193", "15", "97", "143", "122", "167", "150", "27", "121", "65", "179", "136", "192", "33", "133", "92", "175", "122", "27", "193", "143", "144", "65", "192", "15", "44", "150", "63", "179", "34", "33", "143", "43", "167", "97", "27", "44", "133", "122"); + private final CardRun commonC1 = new CardRun(true, "17", "104", "106", "168", "157", "12", "176", "76", "84", "138", "21", "164", "218", "69", "110", "124", "36", "189", "62", "105", "129", "25", "19", "169", "116", "49", "157", "70", "36", "110", "17", "176", "138", "62", "104", "84", "25", "168", "124", "49", "106", "21", "169", "142", "69", "105", "12", "164", "76", "129", "116", "19", "189", "70", "142"); + private final CardRun commonC2 = new CardRun(true, "131", "20", "75", "107", "71", "125", "6", "45", "174", "28", "94", "161", "51", "196", "75", "131", "107", "20", "71", "174", "159", "28", "109", "45", "94", "161", "131", "13", "196", "177", "20", "159", "218", "6", "51", "109", "125", "174", "75", "13", "177", "107", "159", "45", "71", "196", "6", "94", "125", "161", "28", "177", "109", "13", "51"); + private final CardRun uncommonA = new CardRun(true, "217", "153", "184", "219", "120", "32", "227", "147", "182", "213", "91", "7", "229", "72", "207", "200", "86", "41", "160", "223", "204", "184", "66", "59", "139", "229", "38", "91", "219", "166", "147", "205", "120", "220", "86", "204", "213", "7", "203", "118", "58", "160", "59", "210", "207", "38", "166", "72", "99", "153", "223", "205", "182", "66", "227", "217", "58", "210", "203", "32", "139", "118", "220", "200", "99", "41"); + private final CardRun uncommonB = new CardRun(true, "170", "79", "14", "128", "85", "201", "137", "42", "39", "178", "226", "163", "96", "209", "10", "67", "221", "127", "170", "80", "8", "95", "178", "79", "140", "10", "222", "113", "3", "165", "128", "67", "209", "85", "14", "78", "140", "42", "95", "39", "222", "137", "80", "113", "201", "163", "8", "226", "78", "96", "127", "3", "221", "165"); + private final CardRun rare = new CardRun(true, "228", "9", "117", "216", "172", "146", "208", "132", "100", "23", "134", "47", "185", "126", "93", "5", "173", "26", "111", "81", "148", "208", "61", "35", "225", "195", "54", "108", "2", "103", "47", "141", "29", "68", "53", "162", "194", "212", "117", "50", "31", "185", "77", "151", "18", "134", "48", "130", "202", "215", "180", "187", "148", "73", "29", "198", "53", "211", "195", "50", "93", "68", "108", "214", "9", "126", "31", "101", "130", "158", "202", "224", "187", "123", "154", "100", "16", "60", "88", "77", "141", "103", "146", "228", "194", "73", "158", "22", "112", "57", "2", "35", "215", "198", "173", "211", "224", "101", "123", "206", "48", "112", "54", "162", "26", "88", "102", "214", "22", "190", "18", "181", "212", "1", "61", "23", "225", "115", "154", "57", "180"); + private final CardRun land = new CardRun(false, "230", "231", "232", "233", "234", "235", "236", "237", "238", "239", "240", "241", "242", "243", "244", "245", "246", "247", "248", "249"); + + private final BoosterStructure AABBC1C1C1C1C1C1 = new BoosterStructure( + commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AAABBC1C1C1C1C1 = new BoosterStructure( + commonA, commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AAAABBC2C2C2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, + commonC2, commonC2, commonC2, commonC2 + ); + private final BoosterStructure AAAABBBBC2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, commonB, commonB, + commonC2, commonC2 + ); + private final BoosterStructure AAB = new BoosterStructure(uncommonA, uncommonA, uncommonB); + private final BoosterStructure ABB = new BoosterStructure(uncommonA, uncommonB, uncommonB); + private final BoosterStructure R1 = new BoosterStructure(rare); + private final BoosterStructure L1 = new BoosterStructure(land); + + // In order for equal numbers of each common to exist, the average booster must contain: + // 3.27 A commons (36 / 11) + // 2.18 B commons (24 / 11) + // 2.73 C1 commons (30 / 11, or 60 / 11 in each C1 booster) + // 1.82 C2 commons (20 / 11, or 40 / 11 in each C2 booster) + // These numbers are the same for all sets with 101 commons in A/B/C1/C2 print runs + // and with 10 common slots per booster + private final RarityConfiguration commonRuns = new RarityConfiguration( + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBBBC2C2, + AAAABBBBC2C2 + ); + // In order for equal numbers of each uncommon to exist, the average booster must contain: + // 1.65 A uncommons (33 / 20) + // 1.35 B uncommons (27 / 20) + // These numbers are the same for all sets with 60 uncommons in asymmetrical A/B print runs + private final RarityConfiguration uncommonRuns = new RarityConfiguration( + AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, + ABB, ABB, ABB, ABB, ABB, ABB, ABB + ); + private final RarityConfiguration rareRuns = new RarityConfiguration(R1); + private final RarityConfiguration landRuns = new RarityConfiguration(L1); + + @Override + public List makeBooster() { + List booster = new ArrayList<>(); + booster.addAll(commonRuns.getNext().makeRun()); + booster.addAll(uncommonRuns.getNext().makeRun()); + booster.addAll(rareRuns.getNext().makeRun()); + booster.addAll(landRuns.getNext().makeRun()); + return booster; + } } diff --git a/Mage.Sets/src/mage/sets/Magic2015.java b/Mage.Sets/src/mage/sets/Magic2015.java index b35d136022a..90915cd6dd5 100644 --- a/Mage.Sets/src/mage/sets/Magic2015.java +++ b/Mage.Sets/src/mage/sets/Magic2015.java @@ -1,10 +1,16 @@ - package mage.sets; import mage.cards.ExpansionSet; +import mage.collation.BoosterCollator; +import mage.collation.BoosterStructure; +import mage.collation.CardRun; +import mage.collation.RarityConfiguration; import mage.constants.Rarity; import mage.constants.SetType; +import java.util.ArrayList; +import java.util.List; + /** * * @author LevelX2 @@ -311,4 +317,114 @@ public final class Magic2015 extends ExpansionSet { cards.add(new SetCardInfo("Yisan, the Wanderer Bard", 209, Rarity.RARE, mage.cards.y.YisanTheWandererBard.class)); cards.add(new SetCardInfo("Zof Shade", 125, Rarity.COMMON, mage.cards.z.ZofShade.class)); } + + @Override + public BoosterCollator createCollator() { + return new Magic2015Collator(); + } +} + +// Booster collation info from https://www.lethe.xyz/mtg/collation/m15.html +// Using USA collation for all rarities +// Foil rare sheet used for regular rares as regular rare sheet is not known +class Magic2015Collator implements BoosterCollator { + private final CardRun commonA = new CardRun(true, "155", "27", "84", "157", "11", "55", "147", "21", "49", "140", "40", "72", "154", "39", "57", "136", "33", "43", "138", "38", "74", "165", "14", "45", "167", "20", "82", "136", "40", "57", "155", "16", "43", "140", "39", "74", "150", "38", "77", "165", "11", "49", "157", "27", "56", "147", "10", "72", "141", "14", "55", "167", "21", "84", "138", "20", "77", "154", "10", "82", "150", "16", "56", "141", "33", "45"); + private final CardRun commonB = new CardRun(true, "90", "171", "123", "193", "118", "192", "125", "180", "85", "207", "91", "206", "90", "203", "88", "185", "107", "179", "123", "199", "113", "171", "85", "200", "98", "207", "118", "206", "86", "192", "88", "180", "91", "179", "85", "199", "107", "200", "125", "207", "113", "193", "98", "192", "91", "203", "123", "185", "86", "179", "125", "171", "118", "199", "90", "193", "88", "200", "113", "180", "86", "203", "107", "206", "98", "185"); + private final CardRun commonC1 = new CardRun(true, "61", "130", "35", "70", "166", "214", "19", "79", "152", "17", "58", "143", "30", "239", "48", "131", "22", "53", "166", "223", "35", "52", "143", "26", "79", "238", "152", "31", "48", "159", "214", "22", "71", "160", "24", "58", "146", "19", "70", "130", "17", "61", "131", "26", "52", "239", "160", "31", "53", "146", "24", "223", "71", "159", "30"); + private final CardRun commonC2 = new CardRun(true, "186", "112", "181", "104", "245", "198", "114", "197", "97", "204", "92", "243", "184", "120", "170", "104", "198", "105", "173", "95", "186", "112", "204", "114", "181", "120", "197", "97", "170", "105", "92", "186", "95", "184", "114", "245", "198", "243", "173", "112", "120", "197", "105", "181", "238", "184", "104", "204", "245", "97", "173", "92", "170", "95", "243"); + private final CardRun uncommonA = new CardRun(true, "237", "54", "87", "219", "158", "208", "220", "41", "69", "242", "119", "151", "202", "216", "2", "81", "96", "221", "132", "205", "8", "237", "69", "87", "233", "158", "202", "41", "219", "54", "96", "220", "2", "69", "216", "119", "132", "208", "221", "41", "81", "233", "96", "151", "220", "202", "2", "242", "237", "54", "119", "158", "205", "219", "8", "81", "221", "87", "151", "208", "233", "132", "242", "205", "216", "8"); + private final CardRun uncommonB = new CardRun(true, "111", "148", "195", "12", "66", "196", "7", "73", "99", "161", "169", "5", "65", "236", "102", "137", "175", "32", "232", "50", "109", "126", "195", "7", "236", "66", "102", "148", "196", "5", "73", "109", "232", "137", "169", "12", "65", "99", "148", "236", "111", "161", "195", "32", "73", "102", "126", "175", "232", "7", "50", "111", "137", "196", "12", "66", "99", "126", "169", "32", "65", "109", "161", "175", "5", "50"); + private final CardRun uncommonC = new CardRun(true, "89", "129", "168", "213", "42", "60", "94", "128", "235", "190", "23", "63", "116", "234", "129", "194", "42", "51", "89", "142", "168", "13", "60", "234", "94", "129", "190", "213", "23", "63", "116", "235", "128", "194", "13", "51", "94", "142", "168", "234", "23", "60", "89", "128", "213", "190", "42", "51", "116", "142", "194", "235", "13", "63"); + private final CardRun uncommonD = new CardRun(true, "76", "226", "106", "156", "189", "9", "83", "121", "135", "188", "4", "76", "124", "156", "189", "227", "6", "59", "226", "121", "164", "188", "228", "9", "83", "106", "135", "174", "227", "4", "59", "124", "226", "164", "189", "6", "76", "121", "228", "156", "174", "9", "59", "106", "227", "135", "188", "4", "83", "228", "124", "164", "174", "6"); + private final CardRun rare = new CardRun(true, "177", "230", "149", "246", "101", "182", "28", "68", "217", "1", "172", "218", "247", "122", "127", "183", "62", "212", "153", "37", "44", "231", "15", "64", "127", "230", "108", "249", "18", "144", "68", "110", "217", "218", "37", "133", "67", "191", "36", "80", "211", "162", "100", "229", "215", "29", "93", "178", "225", "153", "64", "117", "178", "18", "163", "201", "224", "122", "191", "75", "3", "139", "244", "110", "36", "187", "115", "25", "75", "183", "80", "108", "248", "3", "162", "209", "225", "144", "101", "176", "145", "67", "34", "103", "246", "78", "100", "240", "229", "177", "145", "222", "28", "248", "15", "240", "172", "139", "44", "249", "222", "93", "134", "241", "117", "176", "149", "46", "244", "29", "209", "212", "46", "247", "133", "210", "47", "182", "241", "25", "47"); + private final CardRun land = new CardRun(false, "250", "251", "252", "253", "254", "255", "256", "257", "258", "259", "260", "261", "262", "263", "264", "265", "266", "267", "268", "269"); + + private final BoosterStructure AABBC1C1C1C1C1C1 = new BoosterStructure( + commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AAABBC1C1C1C1C1 = new BoosterStructure( + commonA, commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AAAABBC2C2C2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, + commonC2, commonC2, commonC2, commonC2 + ); + private final BoosterStructure AAAABBBC2C2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, commonB, + commonC2, commonC2, commonC2 + ); + private final BoosterStructure AAAABBBBC2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, commonB, commonB, + commonC2, commonC2 + ); + private final BoosterStructure ABC = new BoosterStructure(uncommonA, uncommonB, uncommonC); + private final BoosterStructure ABD = new BoosterStructure(uncommonA, uncommonB, uncommonD); + private final BoosterStructure ACD = new BoosterStructure(uncommonA, uncommonC, uncommonD); + private final BoosterStructure BCD = new BoosterStructure(uncommonB, uncommonC, uncommonD); + private final BoosterStructure R1 = new BoosterStructure(rare); + private final BoosterStructure L1 = new BoosterStructure(land); + + // In order for equal numbers of each common to exist, the average booster must contain: + // 3.27 A commons (36 / 11) + // 2.18 B commons (24 / 11) + // 2.73 C1 commons (30 / 11, or 60 / 11 in each C1 booster) + // 1.82 C2 commons (20 / 11, or 40 / 11 in each C2 booster) + // These numbers are the same for all sets with 101 commons in A/B/C1/C2 print runs + // and with 10 common slots per booster + private final RarityConfiguration commonRuns = new RarityConfiguration( + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBBC2C2C2, + AAAABBBC2C2C2, + AAAABBBBC2C2 + ); + // In order for equal numbers of each uncommon to exist, the average booster must contain: + // 0.825 A uncommons (33 / 40) + // 0.825 B uncommons (33 / 40) + // 0.675 C uncommons (27 / 40) + // 0.675 D uncommons (27 / 40) + // These numbers are the same for all sets with 80 uncommons in asymmetrical A/B/C/D print runs + private final RarityConfiguration uncommonRuns = new RarityConfiguration( + ABC, ABD, ABC, ABD, ABC, ABD, ABC, ABD, ABC, ABD, ABC, ABD, ABC, + ABD, ABC, ABD, ABC, ABD, ABC, ABD, ABC, ABD, ABC, ABD, ABC, ABD, + ACD, BCD, ACD, BCD, ACD, BCD, ACD, + BCD, ACD, BCD, ACD, BCD, ACD, BCD + ); + private final RarityConfiguration rareRuns = new RarityConfiguration(R1); + private final RarityConfiguration landRuns = new RarityConfiguration(L1); + + @Override + public List makeBooster() { + List booster = new ArrayList<>(); + booster.addAll(commonRuns.getNext().makeRun()); + booster.addAll(uncommonRuns.getNext().makeRun()); + booster.addAll(rareRuns.getNext().makeRun()); + booster.addAll(landRuns.getNext().makeRun()); + return booster; + } } diff --git a/Mage.Sets/src/mage/sets/MagicOrigins.java b/Mage.Sets/src/mage/sets/MagicOrigins.java index 0c5d331ba1a..5b3187549d1 100644 --- a/Mage.Sets/src/mage/sets/MagicOrigins.java +++ b/Mage.Sets/src/mage/sets/MagicOrigins.java @@ -1,10 +1,16 @@ - package mage.sets; import mage.cards.ExpansionSet; +import mage.collation.BoosterCollator; +import mage.collation.BoosterStructure; +import mage.collation.CardRun; +import mage.collation.RarityConfiguration; import mage.constants.Rarity; import mage.constants.SetType; +import java.util.ArrayList; +import java.util.List; + /** * * @author fireshoes @@ -321,4 +327,113 @@ public final class MagicOrigins extends ExpansionSet { cards.add(new SetCardInfo("Zendikar Incarnate", 219, Rarity.UNCOMMON, mage.cards.z.ZendikarIncarnate.class)); cards.add(new SetCardInfo("Zendikar's Roil", 209, Rarity.UNCOMMON, mage.cards.z.ZendikarsRoil.class)); } + + @Override + public BoosterCollator createCollator() { + return new MagicOriginsCollator(); + } +} + +// Booster collation info from https://www.lethe.xyz/mtg/collation/ori.html +// Using USA collation for common/uncommon, rare collation inferred from other sets +class MagicOriginsCollator implements BoosterCollator { + private final CardRun commonA = new CardRun(true, "138", "13", "126", "130", "9", "86", "164", "5", "123", "129", "26", "107", "167", "2", "97", "163", "17", "102", "140", "42", "91", "138", "6", "111", "139", "5", "126", "152", "13", "95", "154", "15", "123", "164", "1", "89", "136", "9", "107", "129", "8", "121", "167", "26", "102", "154", "17", "91", "163", "2", "97", "130", "42", "86", "139", "6", "95", "136", "15", "111", "140", "8", "89", "152", "1", "121"); + private final CardRun commonB = new CardRun(true, "192", "71", "188", "82", "168", "47", "208", "63", "181", "65", "204", "45", "200", "77", "194", "82", "175", "66", "188", "70", "201", "71", "170", "46", "208", "65", "194", "54", "181", "47", "168", "63", "200", "77", "201", "66", "192", "46", "175", "45", "188", "70", "204", "54", "170", "71", "208", "82", "192", "65", "168", "63", "181", "66", "200", "47", "194", "70", "175", "77", "204", "45", "170", "46", "201", "54"); + private final CardRun commonC1 = new CardRun(true, "115", "56", "196", "25", "246", "166", "114", "57", "190", "12", "149", "113", "72", "195", "34", "242", "146", "124", "69", "205", "10", "153", "119", "48", "196", "18", "223", "158", "115", "57", "185", "25", "149", "124", "56", "190", "34", "242", "166", "114", "48", "185", "12", "146", "113", "69", "195", "10", "223", "153", "119", "72", "205", "18", "158"); + private final CardRun commonC2 = new CardRun(true, "206", "227", "50", "132", "32", "116", "68", "187", "133", "220", "125", "36", "50", "228", "145", "20", "109", "52", "206", "246", "32", "227", "184", "220", "68", "132", "36", "116", "50", "187", "133", "228", "125", "187", "32", "227", "145", "20", "125", "52", "206", "220", "109", "132", "184", "36", "68", "133", "20", "109", "52", "184", "228", "145", "116"); + private final CardRun uncommonA = new CardRun(true, "219", "235", "148", "183", "155", "224", "88", "214", "67", "161", "239", "249", "197", "243", "35", "76", "83", "122", "159", "27", "174", "183", "219", "235", "249", "28", "148", "239", "88", "214", "224", "161", "67", "174", "197", "76", "35", "159", "155", "243", "183", "122", "27", "83", "219", "235", "28", "148", "88", "239", "67", "214", "83", "224", "76", "161", "249", "159", "35", "155", "28", "174", "243", "197", "27", "122"); + private final CardRun uncommonB = new CardRun(true, "225", "128", "199", "37", "210", "118", "74", "134", "87", "150", "49", "171", "59", "110", "213", "211", "222", "238", "225", "37", "210", "118", "128", "39", "171", "3", "213", "74", "178", "150", "233", "222", "134", "49", "59", "199", "87", "211", "39", "3", "37", "225", "110", "178", "134", "150", "199", "238", "118", "74", "211", "49", "171", "87", "233", "210", "128", "110", "213", "59", "39", "238", "178", "3", "233", "222"); + private final CardRun uncommonC = new CardRun(true, "247", "162", "81", "241", "96", "218", "41", "198", "209", "11", "22", "216", "226", "80", "218", "100", "98", "247", "162", "212", "11", "209", "241", "216", "144", "22", "96", "81", "41", "80", "198", "144", "162", "241", "226", "212", "209", "98", "41", "81", "100", "218", "96", "247", "198", "11", "22", "216", "212", "80", "226", "98", "100", "144"); + private final CardRun uncommonD = new CardRun(true, "250", "117", "217", "202", "44", "237", "165", "85", "231", "108", "203", "7", "173", "73", "117", "62", "142", "165", "85", "203", "202", "30", "237", "215", "250", "173", "217", "108", "44", "7", "142", "30", "231", "73", "202", "62", "117", "165", "215", "44", "85", "217", "237", "250", "7", "203", "30", "231", "73", "62", "173", "215", "142", "108"); + private final CardRun rare = new CardRun(false, "14", "16", "19", "21", "24", "29", "31", "38", "40", "43", "55", "58", "61", "64", "75", "78", "79", "84", "90", "93", "99", "101", "103", "104", "105", "112", "120", "127", "137", "141", "143", "147", "151", "156", "157", "160", "169", "172", "176", "177", "180", "182", "186", "191", "193", "229", "230", "232", "234", "240", "244", "245", "248", "251", "252", "14", "16", "19", "21", "24", "29", "31", "38", "40", "43", "55", "58", "61", "64", "75", "78", "79", "84", "90", "93", "99", "101", "103", "104", "105", "112", "120", "127", "137", "141", "143", "147", "151", "156", "157", "160", "169", "172", "176", "177", "180", "182", "186", "191", "193", "229", "230", "232", "234", "240", "244", "245", "248", "251", "252", "4", "23", "33", "51", "53", "60", "92", "94", "106", "131", "135", "179", "189", "207", "221", "236"); + private final CardRun land = new CardRun(false, "253", "254", "255", "256", "257", "258", "259", "260", "261", "262", "263", "264", "265", "266", "267", "268", "269", "270", "271", "272"); + + private final BoosterStructure AABBC1C1C1C1C1C1 = new BoosterStructure( + commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AAABBC1C1C1C1C1 = new BoosterStructure( + commonA, commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AAAABBC2C2C2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, + commonC2, commonC2, commonC2, commonC2 + ); + private final BoosterStructure AAAABBBC2C2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, commonB, + commonC2, commonC2, commonC2 + ); + private final BoosterStructure AAAABBBBC2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, commonB, commonB, + commonC2, commonC2 + ); + private final BoosterStructure ABC = new BoosterStructure(uncommonA, uncommonB, uncommonC); + private final BoosterStructure ABD = new BoosterStructure(uncommonA, uncommonB, uncommonD); + private final BoosterStructure ACD = new BoosterStructure(uncommonA, uncommonC, uncommonD); + private final BoosterStructure BCD = new BoosterStructure(uncommonB, uncommonC, uncommonD); + private final BoosterStructure R1 = new BoosterStructure(rare); + private final BoosterStructure L1 = new BoosterStructure(land); + + // In order for equal numbers of each common to exist, the average booster must contain: + // 3.27 A commons (36 / 11) + // 2.18 B commons (24 / 11) + // 2.73 C1 commons (30 / 11, or 60 / 11 in each C1 booster) + // 1.82 C2 commons (20 / 11, or 40 / 11 in each C2 booster) + // These numbers are the same for all sets with 101 commons in A/B/C1/C2 print runs + // and with 10 common slots per booster + private final RarityConfiguration commonRuns = new RarityConfiguration( + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBBC2C2C2, + AAAABBBC2C2C2, + AAAABBBBC2C2 + ); + // In order for equal numbers of each uncommon to exist, the average booster must contain: + // 0.825 A uncommons (33 / 40) + // 0.825 B uncommons (33 / 40) + // 0.675 C uncommons (27 / 40) + // 0.675 D uncommons (27 / 40) + // These numbers are the same for all sets with 80 uncommons in asymmetrical A/B/C/D print runs + private final RarityConfiguration uncommonRuns = new RarityConfiguration( + ABC, ABD, ABC, ABD, ABC, ABD, ABC, ABD, ABC, ABD, ABC, ABD, ABC, + ABD, ABC, ABD, ABC, ABD, ABC, ABD, ABC, ABD, ABC, ABD, ABC, ABD, + ACD, BCD, ACD, BCD, ACD, BCD, ACD, + BCD, ACD, BCD, ACD, BCD, ACD, BCD + ); + private final RarityConfiguration rareRuns = new RarityConfiguration(R1); + private final RarityConfiguration landRuns = new RarityConfiguration(L1); + + @Override + public List makeBooster() { + List booster = new ArrayList<>(); + booster.addAll(commonRuns.getNext().makeRun()); + booster.addAll(uncommonRuns.getNext().makeRun()); + booster.addAll(rareRuns.getNext().makeRun()); + booster.addAll(landRuns.getNext().makeRun()); + return booster; + } } diff --git a/Mage.Sets/src/mage/sets/MastersEdition.java b/Mage.Sets/src/mage/sets/MastersEdition.java index efb3378435f..9a9758447f9 100644 --- a/Mage.Sets/src/mage/sets/MastersEdition.java +++ b/Mage.Sets/src/mage/sets/MastersEdition.java @@ -74,6 +74,7 @@ public final class MastersEdition extends ExpansionSet { cards.add(new SetCardInfo("Dwarven Soldier", 92, Rarity.COMMON, mage.cards.d.DwarvenSoldier.class)); cards.add(new SetCardInfo("Eater of the Dead", 67, Rarity.UNCOMMON, mage.cards.e.EaterOfTheDead.class)); cards.add(new SetCardInfo("Elder Land Wurm", 11, Rarity.UNCOMMON, mage.cards.e.ElderLandWurm.class)); + cards.add(new SetCardInfo("Energy Arc", 144, Rarity.UNCOMMON, mage.cards.e.EnergyArc.class)); cards.add(new SetCardInfo("Erg Raiders", 68, Rarity.COMMON, mage.cards.e.ErgRaiders.class)); cards.add(new SetCardInfo("Eureka", 117, Rarity.RARE, mage.cards.e.Eureka.class)); cards.add(new SetCardInfo("Exile", 12, Rarity.COMMON, mage.cards.e.Exile.class)); @@ -88,6 +89,7 @@ public final class MastersEdition extends ExpansionSet { cards.add(new SetCardInfo("Forest", 195, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Fyndhorn Elves", 118, Rarity.COMMON, mage.cards.f.FyndhornElves.class)); cards.add(new SetCardInfo("Ghazban Ogre", 120, Rarity.COMMON, mage.cards.g.GhazbanOgre.class)); + cards.add(new SetCardInfo("Gargantuan Gorilla", 119, Rarity.RARE, mage.cards.g.GargantuanGorilla.class)); cards.add(new SetCardInfo("Giant Tortoise", 34, Rarity.COMMON, mage.cards.g.GiantTortoise.class)); cards.add(new SetCardInfo("Goblin Chirurgeon", 94, Rarity.COMMON, mage.cards.g.GoblinChirurgeon.class)); cards.add(new SetCardInfo("Goblin Grenade", 95, Rarity.UNCOMMON, mage.cards.g.GoblinGrenade.class)); diff --git a/Mage.Sets/src/mage/sets/MastersEditionII.java b/Mage.Sets/src/mage/sets/MastersEditionII.java index 35d1b0cc660..bd1ce1f5d68 100644 --- a/Mage.Sets/src/mage/sets/MastersEditionII.java +++ b/Mage.Sets/src/mage/sets/MastersEditionII.java @@ -114,6 +114,7 @@ public final class MastersEditionII extends ExpansionSet { cards.add(new SetCardInfo("Glacial Crevasses", 127, Rarity.RARE, mage.cards.g.GlacialCrevasses.class)); cards.add(new SetCardInfo("Gorilla Shaman", 129, Rarity.UNCOMMON, mage.cards.g.GorillaShaman.class)); cards.add(new SetCardInfo("Grandmother Sengir", 93, Rarity.RARE, mage.cards.g.GrandmotherSengir.class)); + cards.add(new SetCardInfo("Gustha's Scepter", 209, Rarity.RARE, mage.cards.g.GusthasScepter.class)); cards.add(new SetCardInfo("Havenwood Battleground", 230, Rarity.UNCOMMON, mage.cards.h.HavenwoodBattleground.class)); cards.add(new SetCardInfo("Heart of Yavimaya", 231, Rarity.RARE, mage.cards.h.HeartOfYavimaya.class)); cards.add(new SetCardInfo("Helm of Obedience", 210, Rarity.RARE, mage.cards.h.HelmOfObedience.class)); @@ -140,6 +141,7 @@ public final class MastersEditionII extends ExpansionSet { cards.add(new SetCardInfo("Karplusan Giant", 133, Rarity.UNCOMMON, mage.cards.k.KarplusanGiant.class)); cards.add(new SetCardInfo("Kaysa", 170, Rarity.RARE, mage.cards.k.Kaysa.class)); cards.add(new SetCardInfo("Kjeldoran Dead", 98, Rarity.COMMON, mage.cards.k.KjeldoranDead.class)); + cards.add(new SetCardInfo("Kjeldoran Elite Guard", 21, Rarity.COMMON, mage.cards.k.KjeldoranEliteGuard.class)); cards.add(new SetCardInfo("Kjeldoran Home Guard", 22, Rarity.UNCOMMON, mage.cards.k.KjeldoranHomeGuard.class)); cards.add(new SetCardInfo("Kjeldoran Outpost", 233, Rarity.RARE, mage.cards.k.KjeldoranOutpost.class)); cards.add(new SetCardInfo("Kjeldoran Skycaptain", 23, Rarity.COMMON, mage.cards.k.KjeldoranSkycaptain.class)); @@ -226,6 +228,7 @@ public final class MastersEditionII extends ExpansionSet { cards.add(new SetCardInfo("Stampede", 178, Rarity.UNCOMMON, mage.cards.s.Stampede.class)); cards.add(new SetCardInfo("Stone Spirit", 150, Rarity.UNCOMMON, mage.cards.s.StoneSpirit.class)); cards.add(new SetCardInfo("Stonehands", 151, Rarity.COMMON, mage.cards.s.Stonehands.class)); + cards.add(new SetCardInfo("Storm Elemental", 68, Rarity.UNCOMMON, mage.cards.s.StormElemental.class)); cards.add(new SetCardInfo("Storm Spirit", 198, Rarity.RARE, mage.cards.s.StormSpirit.class)); cards.add(new SetCardInfo("Stromgald Cabal", 113, Rarity.RARE, mage.cards.s.StromgaldCabal.class)); cards.add(new SetCardInfo("Stunted Growth", 179, Rarity.RARE, mage.cards.s.StuntedGrowth.class)); diff --git a/Mage.Sets/src/mage/sets/MastersEditionIV.java b/Mage.Sets/src/mage/sets/MastersEditionIV.java index 9c6fedd1638..12991d5f3b7 100644 --- a/Mage.Sets/src/mage/sets/MastersEditionIV.java +++ b/Mage.Sets/src/mage/sets/MastersEditionIV.java @@ -1,14 +1,10 @@ - package mage.sets; import mage.cards.ExpansionSet; -import mage.cards.repository.CardCriteria; import mage.cards.repository.CardInfo; -import mage.cards.repository.CardRepository; import mage.constants.Rarity; import mage.constants.SetType; -import java.util.ArrayList; import java.util.List; /** @@ -22,8 +18,6 @@ public final class MastersEditionIV extends ExpansionSet { return instance; } - private final List savedSpecialLand = new ArrayList<>(); - private MastersEditionIV() { super("Masters Edition IV", "ME4", ExpansionSet.buildDate(2011, 1, 10), SetType.MAGIC_ONLINE); this.hasBasicLands = false; @@ -305,16 +299,12 @@ public final class MastersEditionIV extends ExpansionSet { } @Override - public List getSpecialLand() { - // ME4 replace all basic lands with special (1 per booster) - // https://mtg.gamepedia.com/Masters_Edition_IV - - if (savedSpecialLand.isEmpty()) { - savedSpecialLand.addAll(CardRepository.instance.findCards(new CardCriteria().setCodes(this.code).name("Urza's Mine"))); - savedSpecialLand.addAll(CardRepository.instance.findCards(new CardCriteria().setCodes(this.code).name("Urza's Power Plant"))); - savedSpecialLand.addAll(CardRepository.instance.findCards(new CardCriteria().setCodes(this.code).name("Urza's Tower"))); + protected List findSpecialCardsByRarity(Rarity rarity) { + List cardInfos = super.findSpecialCardsByRarity(rarity); + if (rarity == Rarity.LAND) { + // Oasis is a normal common + cardInfos.removeIf(cardInfo -> "Oasis".equals(cardInfo.getName())); } - - return new ArrayList<>(savedSpecialLand); + return cardInfos; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/sets/MidnightHuntCommander.java b/Mage.Sets/src/mage/sets/MidnightHuntCommander.java index 6eaeca743dd..b27afb9bfe7 100644 --- a/Mage.Sets/src/mage/sets/MidnightHuntCommander.java +++ b/Mage.Sets/src/mage/sets/MidnightHuntCommander.java @@ -83,6 +83,7 @@ public final class MidnightHuntCommander extends ExpansionSet { cards.add(new SetCardInfo("Gisa and Geralf", 150, Rarity.MYTHIC, mage.cards.g.GisaAndGeralf.class)); cards.add(new SetCardInfo("Gleaming Overseer", 151, Rarity.UNCOMMON, mage.cards.g.GleamingOverseer.class)); cards.add(new SetCardInfo("Go for the Throat", 119, Rarity.UNCOMMON, mage.cards.g.GoForTheThroat.class)); + cards.add(new SetCardInfo("Gorex, the Tombshell", 20, Rarity.RARE, mage.cards.g.GorexTheTombshell.class)); cards.add(new SetCardInfo("Gravespawn Sovereign", 120, Rarity.RARE, mage.cards.g.GravespawnSovereign.class)); cards.add(new SetCardInfo("Growth Spasm", 139, Rarity.COMMON, mage.cards.g.GrowthSpasm.class)); cards.add(new SetCardInfo("Gyre Sage", 140, Rarity.RARE, mage.cards.g.GyreSage.class)); @@ -106,6 +107,7 @@ public final class MidnightHuntCommander extends ExpansionSet { cards.add(new SetCardInfo("Liliana's Mastery", 123, Rarity.RARE, mage.cards.l.LilianasMastery.class)); cards.add(new SetCardInfo("Liliana, Death's Majesty", 121, Rarity.MYTHIC, mage.cards.l.LilianaDeathsMajesty.class)); cards.add(new SetCardInfo("Lord of the Accursed", 124, Rarity.UNCOMMON, mage.cards.l.LordOfTheAccursed.class)); + cards.add(new SetCardInfo("Lynde, Cheerful Tormentor", 38, Rarity.MYTHIC, mage.cards.l.LyndeCheerfulTormentor.class)); cards.add(new SetCardInfo("Midnight Reaper", 125, Rarity.RARE, mage.cards.m.MidnightReaper.class)); cards.add(new SetCardInfo("Mikaeus, the Lunarch", 89, Rarity.MYTHIC, mage.cards.m.MikaeusTheLunarch.class)); cards.add(new SetCardInfo("Moorland Rescuer", 7, Rarity.RARE, mage.cards.m.MoorlandRescuer.class)); diff --git a/Mage.Sets/src/mage/sets/ModernHorizons.java b/Mage.Sets/src/mage/sets/ModernHorizons.java index caebb6050d7..1fd4a56df2a 100644 --- a/Mage.Sets/src/mage/sets/ModernHorizons.java +++ b/Mage.Sets/src/mage/sets/ModernHorizons.java @@ -1,9 +1,16 @@ package mage.sets; import mage.cards.ExpansionSet; +import mage.collation.BoosterCollator; +import mage.collation.BoosterStructure; +import mage.collation.CardRun; +import mage.collation.RarityConfiguration; import mage.constants.Rarity; import mage.constants.SetType; +import java.util.ArrayList; +import java.util.List; + /** * @author TheElk801 */ @@ -283,4 +290,98 @@ public final class ModernHorizons extends ExpansionSet { cards.add(new SetCardInfo("Yawgmoth, Thran Physician", 116, Rarity.MYTHIC, mage.cards.y.YawgmothThranPhysician.class)); cards.add(new SetCardInfo("Zhalfirin Decoy", 39, Rarity.UNCOMMON, mage.cards.z.ZhalfirinDecoy.class)); } + + @Override + public BoosterCollator createCollator() { + return new ModernHorizonsCollator(); + } +} + +// Booster collation info from https://www.lethe.xyz/mtg/collation/mh1.html +// Using "classic" US collation for common/uncommon, rare collation inferred from other sets +class ModernHorizonsCollator implements BoosterCollator { + private final CardRun commonA = new CardRun(true, "30", "43", "142", "33", "51", "119", "7", "70", "153", "22", "67", "123", "6", "59", "120", "32", "61", "144", "36", "49", "127", "23", "44", "147", "14", "62", "131", "2", "43", "134", "30", "69", "123", "19", "72", "126", "22", "51", "127", "33", "70", "142", "32", "67", "153", "6", "61", "120", "7", "44", "119", "23", "59", "147", "14", "62", "144", "2", "72", "134", "19", "49", "131", "36", "69", "126"); + private final CardRun commonB = new CardRun(true, "79", "184", "93", "165", "99", "157", "103", "185", "82", "180", "95", "186", "115", "193", "113", "188", "87", "174", "111", "185", "91", "171", "79", "176", "99", "184", "93", "165", "103", "157", "82", "186", "87", "180", "113", "188", "95", "174", "115", "193", "91", "171", "111", "185", "79", "184", "103", "165", "99", "157", "93", "180", "87", "186", "82", "176", "95", "174", "91", "171", "115", "176", "113", "193", "111", "188"); + private final CardRun commonC1 = new CardRun(true, "17", "63", "96", "141", "77", "18", "104", "169", "54", "97", "220", "223", "172", "25", "55", "237", "121", "107", "27", "71", "163", "136", "114", "15", "125", "181", "220", "63", "102", "18", "54", "169", "104", "77", "141", "25", "97", "235", "27", "125", "172", "96", "55", "17", "121", "163", "71", "114", "136", "223", "107", "15", "237", "181", "102"); + private final CardRun commonC2 = new CardRun(true, "65", "219", "135", "12", "187", "24", "191", "78", "101", "162", "64", "133", "219", "178", "210", "28", "65", "12", "187", "154", "86", "146", "24", "101", "135", "191", "78", "86", "146", "28", "219", "162", "12", "65", "154", "178", "64", "133", "24", "210", "187", "235", "78", "191", "135", "101", "162", "210", "28", "133", "64", "86", "154", "178", "146"); + private final CardRun uncommonA = new CardRun(true, "48", "159", "112", "143", "233", "248", "38", "94", "204", "132", "221", "60", "182", "225", "122", "73", "183", "16", "88", "213", "236", "39", "50", "170", "84", "137", "233", "239", "34", "108", "215", "117", "221", "74", "167", "231", "143", "201", "192", "4", "110", "212", "151", "38", "48", "159", "94", "132", "60", "183", "39", "112", "204", "248", "225", "73", "170", "88", "122", "213", "236", "16", "84", "201", "117", "233", "50", "192", "239", "137", "231", "182", "34", "108", "215", "151", "221", "74", "167", "110", "143", "212", "159", "4", "112", "204", "248", "38", "48", "183", "88", "122", "60", "236", "39", "94", "213", "132", "16", "73", "182", "225", "117", "201", "192", "34", "108", "215", "151", "231", "50", "167", "84", "137", "74", "170", "4", "110", "212", "239"); + private final CardRun uncommonB = new CardRun(true, "234", "11", "129", "81", "47", "209", "190", "8", "232", "66", "150", "214", "5", "152", "106", "129", "194", "173", "240", "222", "42", "130", "109", "31", "139", "90", "242", "207", "175", "245", "230", "45", "150", "240", "179", "198", "68", "242", "152", "11", "177", "209", "105", "234", "245", "173", "214", "76", "224", "9", "130", "190", "207", "106", "194", "8", "175", "105", "42", "230", "129", "5", "179", "66", "81", "139", "35", "177", "106", "76", "222", "150", "31", "173", "47", "198", "234", "11", "190", "90", "45", "232", "245", "8", "175", "68", "109", "224", "9", "240", "105", "42", "230", "209", "35", "242", "47", "81", "214", "31", "139", "90", "66", "194", "179", "5", "232", "45", "152", "222", "9", "130", "109", "68", "207", "177", "35", "224", "76", "198"); + private final CardRun rare = new CardRun(false, "3", "10", "13", "20", "29", "37", "40", "41", "52", "53", "56", "57", "58", "80", "83", "85", "89", "92", "98", "100", "118", "124", "128", "138", "140", "148", "149", "155", "156", "158", "160", "161", "164", "166", "195", "196", "197", "199", "202", "203", "205", "208", "211", "216", "218", "227", "238", "241", "243", "244", "246", "247", "249", "3", "10", "13", "20", "29", "37", "40", "41", "52", "53", "56", "57", "58", "80", "83", "85", "89", "92", "98", "100", "118", "124", "128", "138", "140", "148", "149", "155", "156", "158", "160", "161", "164", "166", "195", "196", "197", "199", "202", "203", "205", "208", "211", "216", "218", "227", "238", "241", "243", "244", "246", "247", "249", "1", "21", "26", "46", "75", "116", "145", "168", "189", "200", "206", "217", "226", "228", "229"); + private final CardRun land = new CardRun(false, "250", "251", "252", "253", "254"); + + private final BoosterStructure AABBC1C1C1C1C1C1 = new BoosterStructure( + commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AAABBC1C1C1C1C1 = new BoosterStructure( + commonA, commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AAAABBC2C2C2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, + commonC2, commonC2, commonC2, commonC2 + ); + private final BoosterStructure AAAABBBC2C2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, commonB, + commonC2, commonC2, commonC2 + ); + private final BoosterStructure AAAABBBBC2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, commonB, commonB, + commonC2, commonC2 + ); + private final BoosterStructure AAB = new BoosterStructure(uncommonA, uncommonA, uncommonB); + private final BoosterStructure ABB = new BoosterStructure(uncommonA, uncommonB, uncommonB); + private final BoosterStructure R1 = new BoosterStructure(rare); + private final BoosterStructure L1 = new BoosterStructure(land); + + // In order for equal numbers of each common to exist, the average booster must contain: + // 3.27 A commons (36 / 11) + // 2.18 B commons (24 / 11) + // 2.73 C1 commons (30 / 11, or 60 / 11 in each C1 booster) + // 1.82 C2 commons (20 / 11, or 40 / 11 in each C2 booster) + // These numbers are the same for all sets with 101 commons in A/B/C1/C2 print runs + // and with 10 common slots per booster + private final RarityConfiguration commonRuns = new RarityConfiguration( + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBBC2C2C2, + AAAABBBC2C2C2, + AAAABBBBC2C2 + ); + private final RarityConfiguration uncommonRuns = new RarityConfiguration(AAB, ABB); + private final RarityConfiguration rareRuns = new RarityConfiguration(R1); + private final RarityConfiguration landRuns = new RarityConfiguration(L1); + + @Override + public List makeBooster() { + List booster = new ArrayList<>(); + booster.addAll(commonRuns.getNext().makeRun()); + booster.addAll(uncommonRuns.getNext().makeRun()); + booster.addAll(rareRuns.getNext().makeRun()); + booster.addAll(landRuns.getNext().makeRun()); + return booster; + } } diff --git a/Mage.Sets/src/mage/sets/ModernHorizons2.java b/Mage.Sets/src/mage/sets/ModernHorizons2.java index f8d7b6843e7..93949352acc 100644 --- a/Mage.Sets/src/mage/sets/ModernHorizons2.java +++ b/Mage.Sets/src/mage/sets/ModernHorizons2.java @@ -2,7 +2,9 @@ package mage.sets; import mage.cards.Card; import mage.cards.ExpansionSet; +import mage.cards.repository.CardCriteria; import mage.cards.repository.CardInfo; +import mage.cards.repository.CardRepository; import mage.collation.BoosterCollator; import mage.collation.BoosterStructure; import mage.collation.CardRun; @@ -34,6 +36,7 @@ public final class ModernHorizons2 extends ExpansionSet { this.numBoosterCommon = 10; this.numBoosterUncommon = 3; this.numBoosterRare = 1; + this.numBoosterSpecial = 1; this.ratioBoosterMythic = 7; this.maxCardNumberInBooster = 303; @@ -532,32 +535,28 @@ public final class ModernHorizons2 extends ExpansionSet { } @Override - public List tryBooster() { - List booster = super.tryBooster(); - addReprints(booster); - return booster; - } - - private void addReprints(List booster) { - final Rarity rarity; - int i = RandomUtil.nextInt(120); - if (i < 4) { + protected void addSpecialCards(List booster, int number) { + // number is here always 1 + Rarity rarity; + int rarityKey = RandomUtil.nextInt(120); + if (rarityKey < 4) { rarity = Rarity.MYTHIC; - } else if (i < 40) { + } else if (rarityKey < 40) { rarity = Rarity.RARE; } else { rarity = Rarity.UNCOMMON; } - List cards = super.getCardsByRarity(rarity); - cards.removeIf(cardInfo -> cardInfo.getCardNumberAsInt() < 262); - addToBooster(booster, cards); + List reprintCards = getSpecialCardsByRarity(rarity); + addToBooster(booster, reprintCards); } @Override - public List getCardsByRarity(Rarity rarity) { - List cards = super.getCardsByRarity(rarity); - cards.removeIf(cardInfo -> cardInfo.getCardNumberAsInt() >= 262); - return cards; + protected List findSpecialCardsByRarity(Rarity rarity) { + return CardRepository.instance.findCards(new CardCriteria() + .setCodes(this.code) + .rarities(rarity) + .minCardNumber(262) + .maxCardNumber(maxCardNumberInBooster)); } @Override diff --git a/Mage.Sets/src/mage/sets/ModernMasters2015.java b/Mage.Sets/src/mage/sets/ModernMasters2015.java index c577a996287..9b2b5f6d312 100644 --- a/Mage.Sets/src/mage/sets/ModernMasters2015.java +++ b/Mage.Sets/src/mage/sets/ModernMasters2015.java @@ -1,9 +1,16 @@ package mage.sets; import mage.cards.ExpansionSet; +import mage.collation.BoosterCollator; +import mage.collation.BoosterStructure; +import mage.collation.CardRun; +import mage.collation.RarityConfiguration; import mage.constants.Rarity; import mage.constants.SetType; +import java.util.ArrayList; +import java.util.List; + /** * @author fireshoes */ @@ -276,4 +283,113 @@ public final class ModernMasters2015 extends ExpansionSet { cards.add(new SetCardInfo("Wrecking Ball", 189, Rarity.UNCOMMON, mage.cards.w.WreckingBall.class)); } + @Override + public BoosterCollator createCollator() { + return new ModernMasters2015Collator(); + } +} + +// Booster collation info from https://www.lethe.xyz/mtg/collation/mm2.html +// Using USA collation for all rarities +// Foil rare sheets used for regular rares as regular rare sheet is not known +class ModernMasters2015Collator implements BoosterCollator { + private final CardRun commonA = new CardRun(true, "109", "150", "205", "50", "159", "132", "34", "160", "57", "229", "125", "148", "202", "9", "150", "109", "50", "216", "116", "147", "65", "106", "87", "157", "205", "132", "100", "159", "65", "87", "106", "147", "202", "9", "148", "109", "229", "150", "216", "132", "160", "34", "100", "125", "50", "205", "159", "116", "57", "157", "229", "125", "100", "160", "9", "132", "147", "205", "159", "65", "109", "202", "57", "34", "148", "106", "50", "87", "150", "116", "216", "157", "50", "9", "147", "87", "205", "109", "148", "202", "106", "150", "57", "229", "157", "116", "34", "100", "159", "65", "125", "216", "160", "132", "87", "34", "157", "106", "229", "160", "9", "57", "116", "202", "65", "147", "100", "125", "148", "216"); + private final CardRun commonB = new CardRun(true, "94", "44", "8", "85", "66", "21", "73", "58", "13", "81", "39", "32", "72", "61", "27", "101", "42", "31", "95", "52", "24", "84", "56", "12", "103", "70", "38", "89", "64", "17", "76", "68", "37", "96", "59", "30", "83", "47", "35", "72", "39", "31", "95", "56", "8", "103", "68", "17", "76", "70", "13", "96", "42", "27", "85", "66", "37", "94", "59", "21", "89", "52", "30", "73", "61", "38", "84", "47", "12", "81", "44", "24", "83", "64", "35", "101", "58", "32", "83", "68", "30", "84", "61", "35", "85", "59", "21", "96", "70", "24", "81", "64", "38", "101", "39", "31", "94", "56", "32", "95", "47", "13", "103", "44", "8", "76", "58", "12", "89", "52", "17", "72", "66", "27", "73", "42", "37"); + // Note that the two copies of Smash to Smithereens (124) in the last row are close enough to appear in the same pack + private final CardRun commonC = new CardRun(true, "164", "113", "140", "115", "146", "105", "167", "112", "142", "136", "162", "126", "155", "108", "166", "114", "144", "133", "163", "120", "168", "105", "167", "136", "146", "126", "140", "112", "163", "133", "168", "115", "144", "114", "166", "120", "162", "113", "164", "124", "142", "108", "155", "113", "146", "133", "166", "108", "155", "126", "142", "105", "164", "124", "167", "136", "162", "114", "140", "120", "163", "112", "168", "115", "144", "114", "140", "126", "164", "124", "167", "115", "144", "108", "162", "120", "155", "133", "168", "113", "142", "105", "166", "136", "146", "112", "163", "115", "166", "105", "142", "120", "163", "136", "168", "112", "146", "126", "140", "113", "164", "133", "155", "108", "144", "114", "167", "124", "162", "124"); + private final CardRun commonD = new CardRun(true, "227", "218", "241", "215", "80", "231", "214", "201", "117", "224", "218", "203", "238", "228", "7", "226", "227", "234", "97", "217", "224", "241", "234", "226", "203", "97", "228", "238", "215", "7", "201", "80", "227", "217", "231", "117", "214", "218", "241", "231", "228", "214", "97", "234", "117", "215", "217", "201", "238", "224", "7", "227", "203", "226", "80", "218", "228", "80", "224", "117", "214", "238", "203", "97", "218", "234", "231", "7", "215", "227", "201", "241", "226", "217", "80", "228", "215", "241", "218", "234", "227", "203", "7", "201", "238", "226", "231", "217", "117", "97", "214", "224", "201", "7", "217", "228", "226", "241", "234", "80", "203", "117", "224", "215", "231", "238", "214", "97"); + private final CardRun uncommonA = new CardRun(true, "18", "74", "63", "123", "244", "158", "77", "22", "135", "54", "243", "78", "110", "152", "240", "45", "11", "92", "2", "239", "149", "69", "245", "93", "213", "36", "107", "145", "237", "55", "98", "15", "119", "143", "235", "130", "102", "53", "28", "137", "93", "245", "107", "15", "243", "92", "152", "239", "135", "53", "78", "18", "213", "137", "54", "74", "11", "2", "119", "145", "22", "235", "63", "123", "98", "244", "158", "45", "36", "237", "110", "102", "143", "240", "69", "28", "77", "130", "149", "55", "11", "92", "119", "36", "45", "244", "107", "18", "78", "54", "145", "240", "123", "74", "22", "158", "237", "63", "135", "93", "245", "15", "152", "2", "69", "235", "102", "137", "98", "53", "130", "213", "239", "149", "55", "28", "77", "143", "243", "110"); + private final CardRun uncommonB = new CardRun(true, "184", "154", "181", "122", "175", "248", "185", "211", "177", "40", "188", "212", "173", "206", "187", "33", "196", "79", "189", "249", "170", "207", "197", "233", "198", "141", "179", "128", "194", "51", "172", "246", "183", "222", "192", "208", "174", "29", "190", "247", "197", "212", "181", "33", "173", "208", "185", "248", "187", "141", "175", "233", "198", "122", "184", "211", "190", "79", "177", "154", "179", "246", "172", "29", "189", "207", "196", "40", "174", "249", "188", "128", "192", "206", "170", "51", "194", "247", "183", "222", "197", "247", "170", "212", "172", "154", "179", "122", "194", "207", "185", "211", "192", "51", "175", "248", "181", "33", "198", "206", "174", "222", "183", "249", "177", "141", "187", "208", "189", "128", "196", "79", "184", "246", "173", "40", "190", "233", "188", "29"); + private final CardRun rare = new CardRun(true, "220", "104", "186", "225", "86", "199", "14", "48", "210", "127", "180", "90", "25", "219", "139", "191", "49", "129", "242", "19", "178", "82", "41", "230", "138", "193", "1", "10", "209", "91", "176", "60", "134", "236", "99", "182", "26", "43", "232", "153", "200", "118", "161", "221", "169", "171", "131", "151", "204", "23", "195", "46", "88", "219", "139", "191", "48", "82", "225", "129", "186", "25", "60", "230", "90", "180", "127", "161", "242", "26", "193", "49", "99", "210", "104", "200", "151", "86", "204", "19", "171", "131", "1", "220", "43", "195", "23", "169", "232", "134", "182", "88", "46", "236", "10", "176", "138", "118", "221", "91", "199", "153", "14", "209", "41", "178"); + private final CardRun mythic = new CardRun(true, "156", "3", "71", "62", "111", "6", "16", "223", "165", "67", "4", "75", "20", "121", "5", "67", "71", "3", "16", "165", "4", "111", "75", "223", "5", "62", "156", "6", "20", "121", "67", "71", "5", "223", "111", "156", "4", "16", "62", "6", "165", "121", "3", "75", "20", "62", "5", "156", "223", "111", "20", "4", "71", "165", "3", "75", "16", "67", "6", "121", "20", "5", "71", "156", "3", "62", "121", "75", "6", "223", "16", "165", "4", "111", "67", "20", "5", "156", "71", "67", "3", "111", "223", "16", "4", "75", "165", "121", "6", "62", "71", "156", "4", "20", "67", "3", "121", "223", "5", "62", "75", "165", "6", "16", "111", "5", "71", "156", "20", "3", "121", "62", "6", "223", "165", "111", "75", "4", "67", "16"); + private final CardRun foilCommon = new CardRun(true, "140", "215", "30", "106", "103", "56", "226", "150", "136", "95", "234", "132", "61", "76", "228", "160", "214", "13", "113", "50", "155", "27", "241", "202", "12", "97", "42", "34", "124", "68", "84", "216", "167", "24", "94", "147", "112", "203", "59", "87", "37", "162", "117", "57", "83", "44", "133", "32", "66", "7", "164", "125", "73", "142", "227", "238", "229", "80", "108", "159", "8", "218", "89", "65", "21", "120", "101", "38", "144", "47", "81", "109", "35", "168", "224", "148", "96", "126", "70", "205", "114", "85", "39", "157", "231", "17", "217", "116", "58", "146", "31", "100", "201", "115", "163", "52", "105", "72", "166", "9", "64"); + + private final BoosterStructure AABBBBCCDD = new BoosterStructure( + commonA, commonA, + commonB, commonB, commonB, commonB, + commonC, commonC, + commonD, commonD + ); + private final BoosterStructure AABBBBCCCD = new BoosterStructure( + commonA, commonA, + commonB, commonB, commonB, commonB, + commonC, commonC, commonC, + commonD + ); + private final BoosterStructure AAABBBCCDD = new BoosterStructure( + commonA, commonA, commonA, + commonB, commonB, commonB, + commonC, commonC, + commonD, commonD + ); + private final BoosterStructure AAABBBBCCD = new BoosterStructure( + commonA, commonA, commonA, + commonB, commonB, commonB, commonB, + commonC, commonC, + commonD + ); + private final BoosterStructure AAB = new BoosterStructure(uncommonA, uncommonA, uncommonB); + private final BoosterStructure ABB = new BoosterStructure(uncommonA, uncommonB, uncommonB); + private final BoosterStructure R1 = new BoosterStructure(rare); + private final BoosterStructure M1 = new BoosterStructure(mythic); + private final BoosterStructure FC = new BoosterStructure(foilCommon); + private final BoosterStructure FUA = new BoosterStructure(uncommonA); + private final BoosterStructure FUB = new BoosterStructure(uncommonB); + + // In order for equal numbers of each common to exist, the average booster must contain: + // 2.18 A commons (220 / 101) + // 3.86 B commons (390 / 101) + // 2.18 C commons (220 / 101) + // 1.78 D commons (180 / 101) + private final RarityConfiguration commonRuns = new RarityConfiguration( + AABBBBCCDD, AABBBBCCDD, AABBBBCCDD, AABBBBCCDD, AABBBBCCDD, + AABBBBCCDD, AABBBBCCDD, AABBBBCCDD, AABBBBCCDD, AABBBBCCDD, + AABBBBCCDD, AABBBBCCDD, AABBBBCCDD, AABBBBCCDD, AABBBBCCDD, + AABBBBCCDD, AABBBBCCDD, AABBBBCCDD, AABBBBCCDD, AABBBBCCDD, + AABBBBCCDD, AABBBBCCDD, AABBBBCCDD, AABBBBCCDD, AABBBBCCDD, + AABBBBCCDD, AABBBBCCDD, AABBBBCCDD, AABBBBCCDD, AABBBBCCDD, + AABBBBCCDD, AABBBBCCDD, AABBBBCCDD, AABBBBCCDD, AABBBBCCDD, + AABBBBCCDD, AABBBBCCDD, AABBBBCCDD, AABBBBCCDD, AABBBBCCDD, + AABBBBCCDD, AABBBBCCDD, AABBBBCCDD, AABBBBCCDD, AABBBBCCDD, + AABBBBCCDD, AABBBBCCDD, AABBBBCCDD, AABBBBCCDD, AABBBBCCDD, + AABBBBCCDD, AABBBBCCDD, AABBBBCCDD, AABBBBCCDD, AABBBBCCDD, + AABBBBCCDD, AABBBBCCDD, AABBBBCCDD, AABBBBCCDD, AABBBBCCDD, + AABBBBCCDD, AABBBBCCDD, AABBBBCCDD, AABBBBCCDD, AABBBBCCDD, + + AABBBBCCCD, AABBBBCCCD, AABBBBCCCD, AABBBBCCCD, AABBBBCCCD, + AABBBBCCCD, AABBBBCCCD, AABBBBCCCD, AABBBBCCCD, AABBBBCCCD, + AABBBBCCCD, AABBBBCCCD, AABBBBCCCD, AABBBBCCCD, AABBBBCCCD, + AABBBBCCCD, AABBBBCCCD, AABBBBCCCD, + + AAABBBCCDD, AAABBBCCDD, AAABBBCCDD, AAABBBCCDD, AAABBBCCDD, + AAABBBCCDD, AAABBBCCDD, AAABBBCCDD, AAABBBCCDD, AAABBBCCDD, + AAABBBCCDD, AAABBBCCDD, AAABBBCCDD, AAABBBCCDD, + + AAABBBBCCD, AAABBBBCCD, AAABBBBCCD, AAABBBBCCD + ); + private final RarityConfiguration uncommonRuns = new RarityConfiguration(AAB, ABB); + private final RarityConfiguration rareRuns = new RarityConfiguration(R1, R1, R1, R1, R1, R1, R1, M1); + private final RarityConfiguration foilRuns = new RarityConfiguration( + FC, FC, FC, FC, FC, FC, FC, FC, FC, FC, + FC, FC, FC, FC, FC, FC, FC, FC, FC, FC, + FC, FC, FC, FC, FC, FC, FC, FC, FC, FC, + FC, FC, FC, FC, FC, FC, FC, FC, FC, FC, + FC, FC, FC, FC, FC, FC, FC, FC, FC, FC, + FC, FC, FC, FC, FC, FC, FC, FC, FC, FC, + FC, FC, FC, FC, FC, FC, FC, FC, FC, FC, + FC, FC, FC, FC, FC, FC, FC, FC, FC, FC, + FUA, FUA, FUA, FUA, FUA, FUA, FUA, FUA, FUA, FUA, FUA, FUA, + FUB, FUB, FUB, FUB, FUB, FUB, FUB, FUB, FUB, FUB, FUB, FUB, + R1, R1, R1, R1, R1, R1, R1, M1 + ); + + @Override + public List makeBooster() { + List booster = new ArrayList<>(); + booster.addAll(commonRuns.getNext().makeRun()); + booster.addAll(uncommonRuns.getNext().makeRun()); + booster.addAll(rareRuns.getNext().makeRun()); + booster.addAll(foilRuns.getNext().makeRun()); + return booster; + } } diff --git a/Mage.Sets/src/mage/sets/NeonDynastyCommander.java b/Mage.Sets/src/mage/sets/NeonDynastyCommander.java new file mode 100644 index 00000000000..55a441a1d25 --- /dev/null +++ b/Mage.Sets/src/mage/sets/NeonDynastyCommander.java @@ -0,0 +1,203 @@ +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * @author TheElk801 + */ +public final class NeonDynastyCommander extends ExpansionSet { + + private static final NeonDynastyCommander instance = new NeonDynastyCommander(); + + public static NeonDynastyCommander getInstance() { + return instance; + } + + private NeonDynastyCommander() { + super("Neon Dynasty Commander", "NEC", ExpansionSet.buildDate(2022, 2, 18), SetType.SUPPLEMENTAL); + this.hasBasicLands = false; + + cards.add(new SetCardInfo("Access Denied", 11, Rarity.RARE, mage.cards.a.AccessDenied.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Access Denied", 47, Rarity.RARE, mage.cards.a.AccessDenied.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Acidic Slime", 112, Rarity.UNCOMMON, mage.cards.a.AcidicSlime.class)); + cards.add(new SetCardInfo("Aerial Surveyor", 39, Rarity.RARE, mage.cards.a.AerialSurveyor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Aerial Surveyor", 5, Rarity.RARE, mage.cards.a.AerialSurveyor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Aeronaut Admiral", 79, Rarity.UNCOMMON, mage.cards.a.AeronautAdmiral.class)); + cards.add(new SetCardInfo("Agitator Ant", 102, Rarity.RARE, mage.cards.a.AgitatorAnt.class)); + cards.add(new SetCardInfo("Akki Battle Squad", 18, Rarity.RARE, mage.cards.a.AkkiBattleSquad.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Akki Battle Squad", 57, Rarity.RARE, mage.cards.a.AkkiBattleSquad.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Arcane Signet", 144, Rarity.COMMON, mage.cards.a.ArcaneSignet.class)); + cards.add(new SetCardInfo("Arcanist's Owl", 135, Rarity.UNCOMMON, mage.cards.a.ArcanistsOwl.class)); + cards.add(new SetCardInfo("Armed and Armored", 80, Rarity.UNCOMMON, mage.cards.a.ArmedAndArmored.class)); + cards.add(new SetCardInfo("Ascendant Acolyte", 24, Rarity.RARE, mage.cards.a.AscendantAcolyte.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ascendant Acolyte", 64, Rarity.RARE, mage.cards.a.AscendantAcolyte.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Azorius Signet", 145, Rarity.UNCOMMON, mage.cards.a.AzoriusSignet.class)); + cards.add(new SetCardInfo("Bear Umbra", 113, Rarity.RARE, mage.cards.b.BearUmbra.class)); + cards.add(new SetCardInfo("Beast Within", 114, Rarity.UNCOMMON, mage.cards.b.BeastWithin.class)); + cards.add(new SetCardInfo("Blackblade Reforged", 146, Rarity.RARE, mage.cards.b.BlackbladeReforged.class)); + cards.add(new SetCardInfo("Bonehoard", 147, Rarity.RARE, mage.cards.b.Bonehoard.class)); + cards.add(new SetCardInfo("Cataclysmic Gearhulk", 81, Rarity.MYTHIC, mage.cards.c.CataclysmicGearhulk.class)); + cards.add(new SetCardInfo("Chain Reaction", 103, Rarity.RARE, mage.cards.c.ChainReaction.class)); + cards.add(new SetCardInfo("Champion of Lambholt", 115, Rarity.RARE, mage.cards.c.ChampionOfLambholt.class)); + cards.add(new SetCardInfo("Chaos Warp", 104, Rarity.RARE, mage.cards.c.ChaosWarp.class)); + cards.add(new SetCardInfo("Chishiro, the Shattered Blade", 1, Rarity.MYTHIC, mage.cards.c.ChishiroTheShatteredBlade.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Chishiro, the Shattered Blade", 73, Rarity.MYTHIC, mage.cards.c.ChishiroTheShatteredBlade.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Chishiro, the Shattered Blade", 77, Rarity.MYTHIC, mage.cards.c.ChishiroTheShatteredBlade.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Cinder Glade", 166, Rarity.RARE, mage.cards.c.CinderGlade.class)); + cards.add(new SetCardInfo("Collision of Realms", 19, Rarity.RARE, mage.cards.c.CollisionOfRealms.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Collision of Realms", 58, Rarity.RARE, mage.cards.c.CollisionOfRealms.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Colossal Plow", 148, Rarity.UNCOMMON, mage.cards.c.ColossalPlow.class)); + cards.add(new SetCardInfo("Command Tower", 167, Rarity.COMMON, mage.cards.c.CommandTower.class)); + cards.add(new SetCardInfo("Concord with the Kami", 25, Rarity.RARE, mage.cards.c.ConcordWithTheKami.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Concord with the Kami", 65, Rarity.RARE, mage.cards.c.ConcordWithTheKami.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Crush Contraband", 82, Rarity.UNCOMMON, mage.cards.c.CrushContraband.class)); + cards.add(new SetCardInfo("Cultivator's Caravan", 149, Rarity.RARE, mage.cards.c.CultivatorsCaravan.class)); + cards.add(new SetCardInfo("Cyberdrive Awakener", 12, Rarity.RARE, mage.cards.c.CyberdriveAwakener.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Cyberdrive Awakener", 48, Rarity.RARE, mage.cards.c.CyberdriveAwakener.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dance of the Manse", 136, Rarity.RARE, mage.cards.d.DanceOfTheManse.class)); + cards.add(new SetCardInfo("Decimate", 137, Rarity.RARE, mage.cards.d.Decimate.class)); + cards.add(new SetCardInfo("Dispatch", 83, Rarity.UNCOMMON, mage.cards.d.Dispatch.class)); + cards.add(new SetCardInfo("Drumbellower", 40, Rarity.RARE, mage.cards.d.Drumbellower.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Drumbellower", 6, Rarity.RARE, mage.cards.d.Drumbellower.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Elemental Mastery", 105, Rarity.RARE, mage.cards.e.ElementalMastery.class)); + cards.add(new SetCardInfo("Emry, Lurker of the Loch", 91, Rarity.RARE, mage.cards.e.EmryLurkerOfTheLoch.class)); + cards.add(new SetCardInfo("Etherium Sculptor", 92, Rarity.COMMON, mage.cards.e.EtheriumSculptor.class)); + cards.add(new SetCardInfo("Exotic Orchard", 168, Rarity.RARE, mage.cards.e.ExoticOrchard.class)); + cards.add(new SetCardInfo("Fellwar Stone", 150, Rarity.UNCOMMON, mage.cards.f.FellwarStone.class)); + cards.add(new SetCardInfo("Fertilid", 116, Rarity.COMMON, mage.cards.f.Fertilid.class)); + cards.add(new SetCardInfo("Fireshrieker", 151, Rarity.UNCOMMON, mage.cards.f.Fireshrieker.class)); + cards.add(new SetCardInfo("Forgotten Ancient", 117, Rarity.RARE, mage.cards.f.ForgottenAncient.class)); + cards.add(new SetCardInfo("Foundry Inspector", 152, Rarity.COMMON, mage.cards.f.FoundryInspector.class)); + cards.add(new SetCardInfo("Game Trail", 169, Rarity.RARE, mage.cards.g.GameTrail.class)); + cards.add(new SetCardInfo("Generous Gift", 84, Rarity.UNCOMMON, mage.cards.g.GenerousGift.class)); + cards.add(new SetCardInfo("Genesis Hydra", 118, Rarity.RARE, mage.cards.g.GenesisHydra.class)); + cards.add(new SetCardInfo("Go-Shintai of Life's Origin", 37, Rarity.MYTHIC, mage.cards.g.GoShintaiOfLifesOrigin.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Go-Shintai of Life's Origin", 66, Rarity.MYTHIC, mage.cards.g.GoShintaiOfLifesOrigin.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Goblin Razerunners", 106, Rarity.RARE, mage.cards.g.GoblinRazerunners.class)); + cards.add(new SetCardInfo("Gold Myr", 153, Rarity.COMMON, mage.cards.g.GoldMyr.class)); + cards.add(new SetCardInfo("Grumgully, the Generous", 138, Rarity.UNCOMMON, mage.cards.g.GrumgullyTheGenerous.class)); + cards.add(new SetCardInfo("Gruul Turf", 170, Rarity.UNCOMMON, mage.cards.g.GruulTurf.class)); + cards.add(new SetCardInfo("Hanna, Ship's Navigator", 139, Rarity.RARE, mage.cards.h.HannaShipsNavigator.class)); + cards.add(new SetCardInfo("Hunter's Insight", 119, Rarity.UNCOMMON, mage.cards.h.HuntersInsight.class)); + cards.add(new SetCardInfo("Imposter Mech", 13, Rarity.RARE, mage.cards.i.ImposterMech.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Imposter Mech", 49, Rarity.RARE, mage.cards.i.ImposterMech.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Indomitable Archangel", 85, Rarity.MYTHIC, mage.cards.i.IndomitableArchangel.class)); + cards.add(new SetCardInfo("Ironsoul Enforcer", 41, Rarity.RARE, mage.cards.i.IronsoulEnforcer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ironsoul Enforcer", 7, Rarity.RARE, mage.cards.i.IronsoulEnforcer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Jace, Architect of Thought", 93, Rarity.MYTHIC, mage.cards.j.JaceArchitectOfThought.class)); + cards.add(new SetCardInfo("Kaima, the Fractured Calm", 3, Rarity.MYTHIC, mage.cards.k.KaimaTheFracturedCalm.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kaima, the Fractured Calm", 74, Rarity.MYTHIC, mage.cards.k.KaimaTheFracturedCalm.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kami of Celebration", 20, Rarity.RARE, mage.cards.k.KamiOfCelebration.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kami of Celebration", 59, Rarity.RARE, mage.cards.k.KamiOfCelebration.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kappa Cannoneer", 14, Rarity.RARE, mage.cards.k.KappaCannoneer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kappa Cannoneer", 50, Rarity.RARE, mage.cards.k.KappaCannoneer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Katsumasa, the Animator", 15, Rarity.RARE, mage.cards.k.KatsumasaTheAnimator.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Katsumasa, the Animator", 51, Rarity.RARE, mage.cards.k.KatsumasaTheAnimator.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kodama's Reach", 120, Rarity.COMMON, mage.cards.k.KodamasReach.class)); + cards.add(new SetCardInfo("Komainu Battle Armor", 21, Rarity.RARE, mage.cards.k.KomainuBattleArmor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Komainu Battle Armor", 60, Rarity.RARE, mage.cards.k.KomainuBattleArmor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kosei, Penitent Warlord", 26, Rarity.RARE, mage.cards.k.KoseiPenitentWarlord.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kosei, Penitent Warlord", 67, Rarity.RARE, mage.cards.k.KoseiPenitentWarlord.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kotori, Pilot Prodigy", 2, Rarity.MYTHIC, mage.cards.k.KotoriPilotProdigy.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kotori, Pilot Prodigy", 75, Rarity.MYTHIC, mage.cards.k.KotoriPilotProdigy.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kotori, Pilot Prodigy", 78, Rarity.MYTHIC, mage.cards.k.KotoriPilotProdigy.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Krenko, Tin Street Kingpin", 107, Rarity.RARE, mage.cards.k.KrenkoTinStreetKingpin.class)); + cards.add(new SetCardInfo("Loyal Guardian", 121, Rarity.UNCOMMON, mage.cards.l.LoyalGuardian.class)); + cards.add(new SetCardInfo("Mage Slayer", 140, Rarity.UNCOMMON, mage.cards.m.MageSlayer.class)); + cards.add(new SetCardInfo("Master of Etherium", 94, Rarity.RARE, mage.cards.m.MasterOfEtherium.class)); + cards.add(new SetCardInfo("Mirage Mirror", 154, Rarity.RARE, mage.cards.m.MirageMirror.class)); + cards.add(new SetCardInfo("Mossfire Valley", 171, Rarity.RARE, mage.cards.m.MossfireValley.class)); + cards.add(new SetCardInfo("Myojin of Blooming Dawn", 31, Rarity.RARE, mage.cards.m.MyojinOfBloomingDawn.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Myojin of Blooming Dawn", 42, Rarity.RARE, mage.cards.m.MyojinOfBloomingDawn.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Myojin of Cryptic Dreams", 33, Rarity.RARE, mage.cards.m.MyojinOfCrypticDreams.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Myojin of Cryptic Dreams", 52, Rarity.RARE, mage.cards.m.MyojinOfCrypticDreams.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Myojin of Grim Betrayal", 34, Rarity.RARE, mage.cards.m.MyojinOfGrimBetrayal.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Myojin of Grim Betrayal", 55, Rarity.RARE, mage.cards.m.MyojinOfGrimBetrayal.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Myojin of Roaring Blades", 36, Rarity.RARE, mage.cards.m.MyojinOfRoaringBlades.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Myojin of Roaring Blades", 61, Rarity.RARE, mage.cards.m.MyojinOfRoaringBlades.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Myojin of Towering Might", 38, Rarity.RARE, mage.cards.m.MyojinOfToweringMight.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Myojin of Towering Might", 68, Rarity.RARE, mage.cards.m.MyojinOfToweringMight.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Myrsmith", 86, Rarity.UNCOMMON, mage.cards.m.Myrsmith.class)); + cards.add(new SetCardInfo("Nissa, Voice of Zendikar", 122, Rarity.MYTHIC, mage.cards.n.NissaVoiceOfZendikar.class)); + cards.add(new SetCardInfo("One with the Kami", 27, Rarity.RARE, mage.cards.o.OneWithTheKami.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("One with the Kami", 69, Rarity.RARE, mage.cards.o.OneWithTheKami.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Opal Palace", 172, Rarity.COMMON, mage.cards.o.OpalPalace.class)); + cards.add(new SetCardInfo("Oran-Rief, the Vastwood", 173, Rarity.RARE, mage.cards.o.OranRiefTheVastwood.class)); + cards.add(new SetCardInfo("Ordeal of Nylea", 123, Rarity.UNCOMMON, mage.cards.o.OrdealOfNylea.class)); + cards.add(new SetCardInfo("Organic Extinction", 43, Rarity.RARE, mage.cards.o.OrganicExtinction.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Organic Extinction", 8, Rarity.RARE, mage.cards.o.OrganicExtinction.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ox of Agonas", 108, Rarity.MYTHIC, mage.cards.o.OxOfAgonas.class)); + cards.add(new SetCardInfo("Parhelion II", 87, Rarity.RARE, mage.cards.p.ParhelionII.class)); + cards.add(new SetCardInfo("Peacewalker Colossus", 155, Rarity.RARE, mage.cards.p.PeacewalkerColossus.class)); + cards.add(new SetCardInfo("Port Town", 174, Rarity.RARE, mage.cards.p.PortTown.class)); + cards.add(new SetCardInfo("Prairie Stream", 175, Rarity.RARE, mage.cards.p.PrairieStream.class)); + cards.add(new SetCardInfo("Primeval Protector", 124, Rarity.RARE, mage.cards.p.PrimevalProtector.class)); + cards.add(new SetCardInfo("Raff Capashen, Ship's Mage", 141, Rarity.UNCOMMON, mage.cards.r.RaffCapashenShipsMage.class)); + cards.add(new SetCardInfo("Raging Ravine", 176, Rarity.RARE, mage.cards.r.RagingRavine.class)); + cards.add(new SetCardInfo("Raiders' Karve", 156, Rarity.COMMON, mage.cards.r.RaidersKarve.class)); + cards.add(new SetCardInfo("Rampant Growth", 125, Rarity.COMMON, mage.cards.r.RampantGrowth.class)); + cards.add(new SetCardInfo("Rampant Rejuvenator", 28, Rarity.RARE, mage.cards.r.RampantRejuvenator.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rampant Rejuvenator", 70, Rarity.RARE, mage.cards.r.RampantRejuvenator.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Reality Shift", 95, Rarity.UNCOMMON, mage.cards.r.RealityShift.class)); + cards.add(new SetCardInfo("Release to Memory", 44, Rarity.RARE, mage.cards.r.ReleaseToMemory.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Release to Memory", 9, Rarity.RARE, mage.cards.r.ReleaseToMemory.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Research Thief", 16, Rarity.RARE, mage.cards.r.ResearchThief.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Research Thief", 53, Rarity.RARE, mage.cards.r.ResearchThief.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rhythm of the Wild", 142, Rarity.UNCOMMON, mage.cards.r.RhythmOfTheWild.class)); + cards.add(new SetCardInfo("Riddlesmith", 96, Rarity.UNCOMMON, mage.cards.r.Riddlesmith.class)); + cards.add(new SetCardInfo("Rishkar's Expertise", 127, Rarity.RARE, mage.cards.r.RishkarsExpertise.class)); + cards.add(new SetCardInfo("Rishkar, Peema Renegade", 126, Rarity.RARE, mage.cards.r.RishkarPeemaRenegade.class)); + cards.add(new SetCardInfo("Ruthless Technomancer", 35, Rarity.RARE, mage.cards.r.RuthlessTechnomancer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ruthless Technomancer", 56, Rarity.RARE, mage.cards.r.RuthlessTechnomancer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sai, Master Thopterist", 97, Rarity.RARE, mage.cards.s.SaiMasterThopterist.class)); + cards.add(new SetCardInfo("Sakura-Tribe Elder", 128, Rarity.COMMON, mage.cards.s.SakuraTribeElder.class)); + cards.add(new SetCardInfo("Shamanic Revelation", 129, Rarity.RARE, mage.cards.s.ShamanicRevelation.class)); + cards.add(new SetCardInfo("Shifting Shadow", 109, Rarity.RARE, mage.cards.s.ShiftingShadow.class)); + cards.add(new SetCardInfo("Shimmer Myr", 157, Rarity.UNCOMMON, mage.cards.s.ShimmerMyr.class)); + cards.add(new SetCardInfo("Shorikai, Genesis Engine", 4, Rarity.MYTHIC, mage.cards.s.ShorikaiGenesisEngine.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shorikai, Genesis Engine", 76, Rarity.MYTHIC, mage.cards.s.ShorikaiGenesisEngine.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Silkguard", 29, Rarity.RARE, mage.cards.s.Silkguard.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Silkguard", 71, Rarity.RARE, mage.cards.s.Silkguard.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Silver Myr", 158, Rarity.COMMON, mage.cards.s.SilverMyr.class)); + cards.add(new SetCardInfo("Skycloud Expanse", 177, Rarity.RARE, mage.cards.s.SkycloudExpanse.class)); + cards.add(new SetCardInfo("Skysovereign, Consul Flagship", 159, Rarity.MYTHIC, mage.cards.s.SkysovereignConsulFlagship.class)); + cards.add(new SetCardInfo("Smoke Spirits' Aid", 22, Rarity.RARE, mage.cards.s.SmokeSpiritsAid.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Smoke Spirits' Aid", 62, Rarity.RARE, mage.cards.s.SmokeSpiritsAid.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Smuggler's Copter", 160, Rarity.RARE, mage.cards.s.SmugglersCopter.class)); + cards.add(new SetCardInfo("Snake Umbra", 130, Rarity.UNCOMMON, mage.cards.s.SnakeUmbra.class)); + cards.add(new SetCardInfo("Sol Ring", 161, Rarity.UNCOMMON, mage.cards.s.SolRing.class)); + cards.add(new SetCardInfo("Solemn Simulacrum", 162, Rarity.RARE, mage.cards.s.SolemnSimulacrum.class)); + cards.add(new SetCardInfo("Soul's Majesty", 131, Rarity.RARE, mage.cards.s.SoulsMajesty.class)); + cards.add(new SetCardInfo("Spearbreaker Behemoth", 132, Rarity.RARE, mage.cards.s.SpearbreakerBehemoth.class)); + cards.add(new SetCardInfo("Spire of Industry", 178, Rarity.RARE, mage.cards.s.SpireOfIndustry.class)); + cards.add(new SetCardInfo("Sram, Senior Edificer", 88, Rarity.RARE, mage.cards.s.SramSeniorEdificer.class)); + cards.add(new SetCardInfo("Starstorm", 110, Rarity.RARE, mage.cards.s.Starstorm.class)); + cards.add(new SetCardInfo("Swift Reconfiguration", 10, Rarity.RARE, mage.cards.s.SwiftReconfiguration.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swift Reconfiguration", 45, Rarity.RARE, mage.cards.s.SwiftReconfiguration.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swiftfoot Boots", 163, Rarity.UNCOMMON, mage.cards.s.SwiftfootBoots.class)); + cards.add(new SetCardInfo("Sword of Vengeance", 164, Rarity.RARE, mage.cards.s.SwordOfVengeance.class)); + cards.add(new SetCardInfo("Swords to Plowshares", 89, Rarity.UNCOMMON, mage.cards.s.SwordsToPlowshares.class)); + cards.add(new SetCardInfo("Tanuki Transplanter", 30, Rarity.RARE, mage.cards.t.TanukiTransplanter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tanuki Transplanter", 72, Rarity.RARE, mage.cards.t.TanukiTransplanter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Taurean Mauler", 111, Rarity.RARE, mage.cards.t.TaureanMauler.class)); + cards.add(new SetCardInfo("Temple of Abandon", 179, Rarity.RARE, mage.cards.t.TempleOfAbandon.class)); + cards.add(new SetCardInfo("Temple of Enlightenment", 180, Rarity.RARE, mage.cards.t.TempleOfEnlightenment.class)); + cards.add(new SetCardInfo("Teshar, Ancestor's Apostle", 90, Rarity.RARE, mage.cards.t.TesharAncestorsApostle.class)); + cards.add(new SetCardInfo("Thopter Spy Network", 98, Rarity.RARE, mage.cards.t.ThopterSpyNetwork.class)); + cards.add(new SetCardInfo("Thoughtcast", 99, Rarity.COMMON, mage.cards.t.Thoughtcast.class)); + cards.add(new SetCardInfo("Ulasht, the Hate Seed", 143, Rarity.RARE, mage.cards.u.UlashtTheHateSeed.class)); + cards.add(new SetCardInfo("Universal Surveillance", 17, Rarity.RARE, mage.cards.u.UniversalSurveillance.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Universal Surveillance", 54, Rarity.RARE, mage.cards.u.UniversalSurveillance.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Unquenchable Fury", 23, Rarity.RARE, mage.cards.u.UnquenchableFury.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Unquenchable Fury", 63, Rarity.RARE, mage.cards.u.UnquenchableFury.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Vastwood Surge", 133, Rarity.UNCOMMON, mage.cards.v.VastwoodSurge.class)); + cards.add(new SetCardInfo("Vedalken Engineer", 100, Rarity.COMMON, mage.cards.v.VedalkenEngineer.class)); + cards.add(new SetCardInfo("Weatherlight", 165, Rarity.MYTHIC, mage.cards.w.Weatherlight.class)); + cards.add(new SetCardInfo("Whiptongue Hydra", 134, Rarity.RARE, mage.cards.w.WhiptongueHydra.class)); + cards.add(new SetCardInfo("Whirler Rogue", 101, Rarity.UNCOMMON, mage.cards.w.WhirlerRogue.class)); + cards.add(new SetCardInfo("Yoshimaru, Ever Faithful", 32, Rarity.MYTHIC, mage.cards.y.YoshimaruEverFaithful.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Yoshimaru, Ever Faithful", 46, Rarity.MYTHIC, mage.cards.y.YoshimaruEverFaithful.class, NON_FULL_USE_VARIOUS)); + } +} diff --git a/Mage.Sets/src/mage/sets/NinthEdition.java b/Mage.Sets/src/mage/sets/NinthEdition.java index 3d8882d91ac..35876d90f22 100644 --- a/Mage.Sets/src/mage/sets/NinthEdition.java +++ b/Mage.Sets/src/mage/sets/NinthEdition.java @@ -1,9 +1,12 @@ package mage.sets; import mage.cards.ExpansionSet; +import mage.cards.repository.CardInfo; import mage.constants.Rarity; import mage.constants.SetType; +import java.util.List; + public final class NinthEdition extends ExpansionSet { private static final NinthEdition instance = new NinthEdition(); @@ -21,9 +24,6 @@ public final class NinthEdition extends ExpansionSet { this.numBoosterRare = 1; this.ratioBoosterMythic = 0; - // scryfall combines Ninth Edition and Ninth Edition Box sets in one, but xmage must split it - // reason: remove box's cards from booster? TODO: implement booster ignore settings for cards instead max card number - cards.add(new SetCardInfo("Adarkar Wastes", 317, Rarity.RARE, mage.cards.a.AdarkarWastes.class)); cards.add(new SetCardInfo("Air Elemental", 58, Rarity.UNCOMMON, mage.cards.a.AirElemental.class)); cards.add(new SetCardInfo("Aladdin's Ring", 286, Rarity.RARE, mage.cards.a.AladdinsRing.class)); @@ -73,6 +73,7 @@ public final class NinthEdition extends ExpansionSet { cards.add(new SetCardInfo("Confiscate", 68, Rarity.UNCOMMON, mage.cards.c.Confiscate.class)); cards.add(new SetCardInfo("Consume Spirit", 119, Rarity.UNCOMMON, mage.cards.c.ConsumeSpirit.class)); cards.add(new SetCardInfo("Contaminated Bond", 120, Rarity.COMMON, mage.cards.c.ContaminatedBond.class)); + cards.add(new SetCardInfo("Coral Eel", "S3", Rarity.COMMON, mage.cards.c.CoralEel.class)); cards.add(new SetCardInfo("Counsel of the Soratami", 69, Rarity.COMMON, mage.cards.c.CounselOfTheSoratami.class)); cards.add(new SetCardInfo("Cowardice", 70, Rarity.RARE, mage.cards.c.Cowardice.class)); cards.add(new SetCardInfo("Crafty Pathmage", 71, Rarity.COMMON, mage.cards.c.CraftyPathmage.class)); @@ -95,6 +96,7 @@ public final class NinthEdition extends ExpansionSet { cards.add(new SetCardInfo("Dragon's Claw", 296, Rarity.UNCOMMON, mage.cards.d.DragonsClaw.class)); cards.add(new SetCardInfo("Dream Prowler", 74, Rarity.UNCOMMON, mage.cards.d.DreamProwler.class)); cards.add(new SetCardInfo("Drudge Skeletons", 126, Rarity.UNCOMMON, mage.cards.d.DrudgeSkeletons.class)); + cards.add(new SetCardInfo("Eager Cadet", "S1", Rarity.COMMON, mage.cards.e.EagerCadet.class)); cards.add(new SetCardInfo("Early Harvest", 235, Rarity.RARE, mage.cards.e.EarlyHarvest.class)); cards.add(new SetCardInfo("Elvish Bard", 236, Rarity.UNCOMMON, mage.cards.e.ElvishBard.class)); cards.add(new SetCardInfo("Elvish Berserker", 237, Rarity.COMMON, mage.cards.e.ElvishBerserker.class)); @@ -103,6 +105,7 @@ public final class NinthEdition extends ExpansionSet { cards.add(new SetCardInfo("Elvish Warrior", 240, Rarity.COMMON, mage.cards.e.ElvishWarrior.class)); cards.add(new SetCardInfo("Emperor Crocodile", 241, Rarity.RARE, mage.cards.e.EmperorCrocodile.class)); cards.add(new SetCardInfo("Enfeeblement", 127, Rarity.COMMON, mage.cards.e.Enfeeblement.class)); + cards.add(new SetCardInfo("Enormous Baloth", "S9", Rarity.UNCOMMON, mage.cards.e.EnormousBaloth.class)); cards.add(new SetCardInfo("Enrage", 180, Rarity.UNCOMMON, mage.cards.e.Enrage.class)); cards.add(new SetCardInfo("Evacuation", 75, Rarity.RARE, mage.cards.e.Evacuation.class)); cards.add(new SetCardInfo("Execute", 128, Rarity.UNCOMMON, mage.cards.e.Execute.class)); @@ -132,6 +135,7 @@ public final class NinthEdition extends ExpansionSet { cards.add(new SetCardInfo("Furnace of Rath", 188, Rarity.RARE, mage.cards.f.FurnaceOfRath.class)); cards.add(new SetCardInfo("Giant Cockroach", 133, Rarity.COMMON, mage.cards.g.GiantCockroach.class)); cards.add(new SetCardInfo("Giant Growth", 243, Rarity.COMMON, mage.cards.g.GiantGrowth.class)); + cards.add(new SetCardInfo("Giant Octopus", "S4", Rarity.COMMON, mage.cards.g.GiantOctopus.class)); cards.add(new SetCardInfo("Giant Spider", 244, Rarity.COMMON, mage.cards.g.GiantSpider.class)); cards.add(new SetCardInfo("Gift of Estates", 15, Rarity.UNCOMMON, mage.cards.g.GiftOfEstates.class)); cards.add(new SetCardInfo("Glorious Anthem", 16, Rarity.RARE, mage.cards.g.GloriousAnthem.class)); @@ -143,6 +147,7 @@ public final class NinthEdition extends ExpansionSet { cards.add(new SetCardInfo("Goblin King", 192, Rarity.RARE, mage.cards.g.GoblinKing.class)); cards.add(new SetCardInfo("Goblin Mountaineer", 193, Rarity.COMMON, mage.cards.g.GoblinMountaineer.class)); cards.add(new SetCardInfo("Goblin Piker", 194, Rarity.COMMON, mage.cards.g.GoblinPiker.class)); + cards.add(new SetCardInfo("Goblin Raider", "S8", Rarity.COMMON, mage.cards.g.GoblinRaider.class)); cards.add(new SetCardInfo("Goblin Sky Raider", 195, Rarity.COMMON, mage.cards.g.GoblinSkyRaider.class)); cards.add(new SetCardInfo("Gravedigger", 136, Rarity.COMMON, mage.cards.g.Gravedigger.class)); cards.add(new SetCardInfo("Grave Pact", 135, Rarity.RARE, mage.cards.g.GravePact.class)); @@ -164,6 +169,7 @@ public final class NinthEdition extends ExpansionSet { cards.add(new SetCardInfo("Hypnotic Specter", 141, Rarity.RARE, mage.cards.h.HypnoticSpecter.class)); cards.add(new SetCardInfo("Icy Manipulator", 299, Rarity.UNCOMMON, mage.cards.i.IcyManipulator.class)); cards.add(new SetCardInfo("Imaginary Pet", 82, Rarity.RARE, mage.cards.i.ImaginaryPet.class)); + cards.add(new SetCardInfo("Index", "S5", Rarity.COMMON, mage.cards.i.Index.class)); cards.add(new SetCardInfo("Infantry Veteran", 21, Rarity.COMMON, mage.cards.i.InfantryVeteran.class)); cards.add(new SetCardInfo("Inspirit", 22, Rarity.UNCOMMON, mage.cards.i.Inspirit.class)); cards.add(new SetCardInfo("Island", 335, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); @@ -301,6 +307,7 @@ public final class NinthEdition extends ExpansionSet { cards.add(new SetCardInfo("Soul Feast", 164, Rarity.UNCOMMON, mage.cards.s.SoulFeast.class)); cards.add(new SetCardInfo("Soul Warden", 46, Rarity.UNCOMMON, mage.cards.s.SoulWarden.class)); cards.add(new SetCardInfo("Spellbook", 309, Rarity.UNCOMMON, mage.cards.s.Spellbook.class)); + cards.add(new SetCardInfo("Spined Wurm", "S10", Rarity.COMMON, mage.cards.s.SpinedWurm.class)); cards.add(new SetCardInfo("Spineless Thug", 165, Rarity.COMMON, mage.cards.s.SpinelessThug.class)); cards.add(new SetCardInfo("Spirit Link", 47, Rarity.UNCOMMON, mage.cards.s.SpiritLink.class)); cards.add(new SetCardInfo("Stone Rain", 221, Rarity.COMMON, mage.cards.s.StoneRain.class)); @@ -345,11 +352,13 @@ public final class NinthEdition extends ExpansionSet { cards.add(new SetCardInfo("Urza's Tower", 329, Rarity.UNCOMMON, mage.cards.u.UrzasTower.class)); cards.add(new SetCardInfo("Utopia Tree", 277, Rarity.RARE, mage.cards.u.UtopiaTree.class)); cards.add(new SetCardInfo("Venerable Monk", 51, Rarity.COMMON, mage.cards.v.VenerableMonk.class)); + cards.add(new SetCardInfo("Vengeance", "S2", Rarity.UNCOMMON, mage.cards.v.Vengeance.class)); cards.add(new SetCardInfo("Verdant Force", 278, Rarity.RARE, mage.cards.v.VerdantForce.class)); cards.add(new SetCardInfo("Verduran Enchantress", 279, Rarity.RARE, mage.cards.v.VerduranEnchantress.class)); cards.add(new SetCardInfo("Veteran Cavalier", 52, Rarity.COMMON, mage.cards.v.VeteranCavalier.class)); cards.add(new SetCardInfo("Viashino Sandstalker", 225, Rarity.UNCOMMON, mage.cards.v.ViashinoSandstalker.class)); cards.add(new SetCardInfo("Viridian Shaman", 280, Rarity.UNCOMMON, mage.cards.v.ViridianShaman.class)); + cards.add(new SetCardInfo("Vizzerdrix", "S7", Rarity.RARE, mage.cards.v.Vizzerdrix.class)); cards.add(new SetCardInfo("Volcanic Hammer", 226, Rarity.COMMON, mage.cards.v.VolcanicHammer.class)); cards.add(new SetCardInfo("Vulshok Morningstar", 315, Rarity.UNCOMMON, mage.cards.v.VulshokMorningstar.class)); cards.add(new SetCardInfo("Wanderguard Sentry", 111, Rarity.COMMON, mage.cards.w.WanderguardSentry.class)); @@ -374,4 +383,12 @@ public final class NinthEdition extends ExpansionSet { cards.add(new SetCardInfo("Zombify", 171, Rarity.UNCOMMON, mage.cards.z.Zombify.class)); cards.add(new SetCardInfo("Zur's Weirding", 114, Rarity.RARE, mage.cards.z.ZursWeirding.class)); } -} \ No newline at end of file + + @Override + protected List findCardsByRarity(Rarity rarity) { + List cardInfos = super.findCardsByRarity(rarity); + // card numbers containing S are Starter Set cards not found in boosters + cardInfos.removeIf(cardInfo -> cardInfo.getCardNumber().contains("S")); + return cardInfos; + } +} diff --git a/Mage.Sets/src/mage/sets/NinthEditionBox.java b/Mage.Sets/src/mage/sets/NinthEditionBox.java deleted file mode 100644 index 9af6401f419..00000000000 --- a/Mage.Sets/src/mage/sets/NinthEditionBox.java +++ /dev/null @@ -1,30 +0,0 @@ -package mage.sets; - -import mage.cards.ExpansionSet; -import mage.constants.Rarity; -import mage.constants.SetType; - -public final class NinthEditionBox extends ExpansionSet { - - private static final NinthEditionBox instance = new NinthEditionBox(); - - public static NinthEditionBox getInstance() { - return instance; - } - - private NinthEditionBox() { - super("Ninth Edition Box", "9EB", ExpansionSet.buildDate(2005, 7, 29), SetType.CORE); - this.hasBoosters = false; - this.hasBasicLands = false; - - cards.add(new SetCardInfo("Coral Eel", "S3", Rarity.COMMON, mage.cards.c.CoralEel.class)); - cards.add(new SetCardInfo("Eager Cadet", "S1", Rarity.COMMON, mage.cards.e.EagerCadet.class)); - cards.add(new SetCardInfo("Enormous Baloth", "S9", Rarity.UNCOMMON, mage.cards.e.EnormousBaloth.class)); - cards.add(new SetCardInfo("Giant Octopus", "S4", Rarity.COMMON, mage.cards.g.GiantOctopus.class)); - cards.add(new SetCardInfo("Goblin Raider", "S8", Rarity.COMMON, mage.cards.g.GoblinRaider.class)); - cards.add(new SetCardInfo("Index", "S5", Rarity.COMMON, mage.cards.i.Index.class)); - cards.add(new SetCardInfo("Spined Wurm", "S10", Rarity.COMMON, mage.cards.s.SpinedWurm.class)); - cards.add(new SetCardInfo("Vengeance", "S2", Rarity.UNCOMMON, mage.cards.v.Vengeance.class)); - cards.add(new SetCardInfo("Vizzerdrix", "S7", Rarity.RARE, mage.cards.v.Vizzerdrix.class)); - } -} diff --git a/Mage.Sets/src/mage/sets/OathOfTheGatewatch.java b/Mage.Sets/src/mage/sets/OathOfTheGatewatch.java index 025e70c4c5b..2549e958bad 100644 --- a/Mage.Sets/src/mage/sets/OathOfTheGatewatch.java +++ b/Mage.Sets/src/mage/sets/OathOfTheGatewatch.java @@ -4,6 +4,10 @@ import mage.cards.ExpansionSet; import mage.cards.repository.CardCriteria; import mage.cards.repository.CardInfo; import mage.cards.repository.CardRepository; +import mage.collation.BoosterCollator; +import mage.collation.BoosterStructure; +import mage.collation.CardRun; +import mage.collation.RarityConfiguration; import mage.constants.Rarity; import mage.constants.SetType; @@ -21,8 +25,6 @@ public final class OathOfTheGatewatch extends ExpansionSet { return instance; } - private final List savedSpecialLand = new ArrayList<>(); - private OathOfTheGatewatch() { super("Oath of the Gatewatch", "OGW", ExpansionSet.buildDate(2016, 1, 22), SetType.EXPANSION); this.blockName = "Battle for Zendikar"; @@ -34,7 +36,7 @@ public final class OathOfTheGatewatch extends ExpansionSet { this.numBoosterUncommon = 3; this.numBoosterRare = 1; this.ratioBoosterMythic = 8; - this.ratioBoosterSpecialLand = 48; + this.ratioBoosterSpecialCommon = 144; cards.add(new SetCardInfo("Abstruse Interference", 40, Rarity.COMMON, mage.cards.a.AbstruseInterference.class)); cards.add(new SetCardInfo("Affa Protector", 14, Rarity.COMMON, mage.cards.a.AffaProtector.class)); @@ -226,25 +228,131 @@ public final class OathOfTheGatewatch extends ExpansionSet { } @Override - public List getCardsByRarity(Rarity rarity) { - List cards = super.getCardsByRarity(rarity); + protected List findCardsByRarity(Rarity rarity) { + List cardInfos = super.findCardsByRarity(rarity); if (rarity == Rarity.COMMON) { // only the full-art versions of Wastes are found in boosters - cards.removeIf(cardInfo -> cardInfo.getCardNumber().contains("a")); + cardInfos.removeIf(cardInfo -> cardInfo.getCardNumber().contains("a")); + } else if (rarity == Rarity.SPECIAL) { + cardInfos.addAll(CardRepository.instance.findCards(new CardCriteria().setCodes("EXP").minCardNumber(26))); } - return cards; + return cardInfos; } @Override - public List getSpecialLand() { - if (savedSpecialLand.isEmpty()) { - CardCriteria criteria = new CardCriteria(); - criteria.setCodes("EXP"); - criteria.minCardNumber(26); - criteria.maxCardNumber(45); - savedSpecialLand.addAll(CardRepository.instance.findCards(criteria)); - } + protected void generateBoosterMap() { + super.generateBoosterMap(); + CardRepository + .instance + .findCards(new CardCriteria().setCodes("EXP").minCardNumber(26)) + .stream() + .forEach(cardInfo -> inBoosterMap.put("EXP_" + cardInfo.getCardNumber(), cardInfo)); + } - return new ArrayList<>(savedSpecialLand); + @Override + public BoosterCollator createCollator() { + return new OathOfTheGatewatchCollator(); + } +} + +// Booster collation info from https://www.lethe.xyz/mtg/collation/ogw.html +// Using USA collation for all rarities +// Foil rare sheet used for regular rares as regular rare sheet is not known +class OathOfTheGatewatchCollator implements BoosterCollator { + private final CardRun commonA = new CardRun(true, "50", "108", "33", "51", "95", "23", "59", "117", "27", "65", "94", "34", "50", "99", "28", "40", "101", "17", "47", "103", "23", "51", "95", "14", "28", "94", "31", "42", "108", "27", "40", "101", "33", "59", "117", "34", "47", "99", "17", "65", "103", "28", "42", "108", "31", "50", "14", "27", "40", "117", "23", "51", "95", "17", "47", "94", "33", "59", "99", "34", "65", "101", "31", "42", "103", "14"); + private final CardRun commonB = new CardRun(true, "129", "74", "134", "83", "136", "79", "135", "80", "141", "91", "146", "74", "142", "83", "129", "78", "130", "93", "136", "82", "137", "73", "135", "79", "134", "91", "141", "80", "146", "93", "142", "83", "136", "74", "130", "78", "135", "82", "137", "79", "129", "73", "134", "91", "146", "80", "141", "93", "130", "78", "142", "73", "137", "82"); + private final CardRun commonC1 = new CardRun(true, "11", "22", "90", "100", "184", "45", "161", "123", "35", "105", "181", "64", "165", "24", "75", "102", "172", "41", "5", "145", "18", "120", "170", "52", "164", "90", "124", "105", "183", "58", "11", "35", "92", "100", "181", "64", "161", "145", "22", "111", "184", "52", "165", "24", "92", "105", "172", "45", "161", "18", "90", "102", "181", "41", "5", "123", "24", "120", "170", "52", "164", "75", "124", "111", "183", "58"); + private final CardRun commonC2 = new CardRun(true, "184", "52", "161", "92", "145", "111", "172", "41", "5", "22", "123", "102", "183", "64", "165", "75", "35", "120", "184", "58", "11", "124", "18", "100", "170", "45", "164", "92", "123", "172", "102", "41", "5", "35", "75", "111", "183", "64", "165", "145", "18", "120", "170", "58", "164", "22", "90", "105", "181", "45", "11", "124", "24", "100"); + private final CardRun uncommonA = new CardRun(true, "85", "173", "62", "112", "133", "10", "20", "69", "178", "62", "167", "107", "121", "153", "26", "85", "179", "56", "107", "131", "8", "32", "88", "168", "54", "163", "116", "121", "10", "20", "76", "178", "153", "115", "143", "56", "15", "88", "179", "54", "167", "112", "131", "8", "32", "69", "168", "46", "116", "133", "180", "26", "85", "173", "56", "163", "115", "143", "10", "15", "76", "178", "62", "107", "121", "180", "32", "69", "173", "46", "167", "112", "131", "8", "20", "88", "168", "54", "116", "133", "153", "26", "85", "179", "62", "163", "115", "143", "180", "15", "76", "173", "54", "112", "131", "153", "26", "69", "178", "46", "167", "107", "133", "10", "20", "88", "168", "56", "116", "121", "8", "32", "76", "179", "46", "163", "115", "143", "180", "15"); + private final CardRun uncommonB = new CardRun(true, "21", "71", "159", "106", "122", "158", "70", "118", "160", "139", "66", "48", "36", "150", "81", "49", "152", "39", "148", "38", "114", "157", "128", "106", "55", "87", "12", "154", "97", "122", "149", "71", "159", "21", "81", "158", "114", "66", "160", "127", "70", "157", "139", "128", "48", "36", "159", "118", "139", "148", "87", "39", "49", "48", "150", "38", "114", "149", "55", "106", "158", "127", "12", "152", "97", "21", "71", "81", "122", "70", "36", "160", "66", "139", "157", "39", "154", "49", "87", "159", "12", "127", "148", "38", "71", "150", "118", "149", "36", "122", "154", "66", "97", "152", "128", "106", "21", "81", "55", "114", "150", "12", "87", "148", "118", "70", "158", "49", "152", "39", "128", "160", "48", "38", "154", "97", "55", "149", "127", "157"); + private final CardRun rare = new CardRun(true, "29", "3", "4", "109", "176", "132", "67", "16", "1", "60", "155", "113", "126", "84", "13", "6", "57", "151", "98", "182", "140", "89", "2", "53", "162", "96", "169", "144", "68", "37", "7", "44", "110", "175", "125", "77", "19", "3", "43", "151", "119", "176", "72", "13", "1", "61", "155", "109", "182", "132", "67", "25", "57", "162", "113", "169", "140", "84", "16", "9", "60", "156", "171", "138", "77", "29", "9", "43", "166", "119", "177", "144", "30", "2", "61", "156", "104", "175", "147", "89", "37", "7", "166", "96", "177", "125", "68"); + private final CardRun expedition = new CardRun(false, "EXP_26", "EXP_27", "EXP_28", "EXP_29", "EXP_30", "EXP_31", "EXP_32", "EXP_33", "EXP_34", "EXP_35", "EXP_36", "EXP_37", "EXP_38", "EXP_39", "EXP_40", "EXP_41", "EXP_42", "EXP_43", "EXP_44", "EXP_45"); + private final CardRun land = new CardRun(false, "BFZ_250", "BFZ_251", "BFZ_252", "BFZ_253", "BFZ_254", "BFZ_255", "BFZ_256", "BFZ_257", "BFZ_258", "BFZ_259", "BFZ_260", "BFZ_261", "BFZ_262", "BFZ_263", "BFZ_264", "BFZ_265", "BFZ_266", "BFZ_267", "BFZ_268", "BFZ_269", "BFZ_270", "BFZ_271", "BFZ_272", "BFZ_273", "BFZ_274"); + + private final BoosterStructure AAABBC1C1C1C1C1 = new BoosterStructure( + commonA, commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AAABBBC1C1C1C1 = new BoosterStructure( + commonA, commonA, commonA, + commonB, commonB, commonB, + commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AAABBC1C1C1C1X = new BoosterStructure( + commonA, commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1, + expedition + ); + private final BoosterStructure AAABBBC2C2C2C2 = new BoosterStructure( + commonA, commonA, commonA, + commonB, commonB, commonB, + commonC2, commonC2, commonC2, commonC2 + ); + private final BoosterStructure AAAABBC2C2C2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, + commonC2, commonC2, commonC2, commonC2 + ); + private final BoosterStructure AAB = new BoosterStructure(uncommonA, uncommonA, uncommonB); + private final BoosterStructure ABB = new BoosterStructure(uncommonA, uncommonB, uncommonB); + private final BoosterStructure R1 = new BoosterStructure(rare); + private final BoosterStructure L1 = new BoosterStructure(land); + + // In order for equal numbers of each common to exist, the average booster must contain: + // 3.14 A commons (44 / 14) (rounded to 455/145) + // 2.57 B commons (36 / 14) (rounded to 373/145) + // 2.36 C1 commons (33 / 14) (rounded to 341/145) + // 1.93 C2 commons (27 / 14) (rounded to 280/145) + // These numbers are the same for all sets with 70 commons in A/B/C1/C2 print runs + // and with 10 common slots per booster + private final RarityConfiguration commonRuns = new RarityConfiguration( + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + + AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, + AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, + AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, + AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, + AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, + AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, + AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBC1C1C1C1X, + + AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, + AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, + AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, + AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, + AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, + AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, + AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, + AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, + AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, + AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, + + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2 + ); + private final RarityConfiguration uncommonRuns = new RarityConfiguration(AAB, ABB); + private final RarityConfiguration rareRuns = new RarityConfiguration(R1); + private final RarityConfiguration landRuns = new RarityConfiguration(L1); + + @Override + public List makeBooster() { + List booster = new ArrayList<>(); + booster.addAll(commonRuns.getNext().makeRun()); + booster.addAll(uncommonRuns.getNext().makeRun()); + booster.addAll(rareRuns.getNext().makeRun()); + booster.addAll(landRuns.getNext().makeRun()); + return booster; } } diff --git a/Mage.Sets/src/mage/sets/Onslaught.java b/Mage.Sets/src/mage/sets/Onslaught.java index cc79d7f83a0..01391ae58c3 100644 --- a/Mage.Sets/src/mage/sets/Onslaught.java +++ b/Mage.Sets/src/mage/sets/Onslaught.java @@ -169,6 +169,7 @@ public final class Onslaught extends ExpansionSet { cards.add(new SetCardInfo("Gratuitous Violence", 212, Rarity.RARE, mage.cards.g.GratuitousViolence.class)); cards.add(new SetCardInfo("Gravel Slinger", 33, Rarity.COMMON, mage.cards.g.GravelSlinger.class)); cards.add(new SetCardInfo("Gravespawn Sovereign", 152, Rarity.RARE, mage.cards.g.GravespawnSovereign.class)); + cards.add(new SetCardInfo("Graxiplon", 86, Rarity.UNCOMMON, mage.cards.g.Graxiplon.class)); cards.add(new SetCardInfo("Grinning Demon", 153, Rarity.RARE, mage.cards.g.GrinningDemon.class)); cards.add(new SetCardInfo("Gustcloak Harrier", 34, Rarity.COMMON, mage.cards.g.GustcloakHarrier.class)); cards.add(new SetCardInfo("Gustcloak Runner", 35, Rarity.COMMON, mage.cards.g.GustcloakRunner.class)); @@ -285,6 +286,7 @@ public final class Onslaught extends ExpansionSet { cards.add(new SetCardInfo("Silent Specter", 169, Rarity.RARE, mage.cards.s.SilentSpecter.class)); cards.add(new SetCardInfo("Silklash Spider", 281, Rarity.RARE, mage.cards.s.SilklashSpider.class)); cards.add(new SetCardInfo("Silvos, Rogue Elemental", 282, Rarity.RARE, mage.cards.s.SilvosRogueElemental.class)); + cards.add(new SetCardInfo("Skirk Commando", 228, Rarity.COMMON, mage.cards.s.SkirkCommando.class)); cards.add(new SetCardInfo("Skirk Fire Marshal", 229, Rarity.RARE, mage.cards.s.SkirkFireMarshal.class)); cards.add(new SetCardInfo("Skirk Prospector", 230, Rarity.COMMON, mage.cards.s.SkirkProspector.class)); cards.add(new SetCardInfo("Skittish Valesk", 231, Rarity.UNCOMMON, mage.cards.s.SkittishValesk.class)); diff --git a/Mage.Sets/src/mage/sets/RavnicaAllegiance.java b/Mage.Sets/src/mage/sets/RavnicaAllegiance.java index 735ad45eb1c..8106856f9f0 100644 --- a/Mage.Sets/src/mage/sets/RavnicaAllegiance.java +++ b/Mage.Sets/src/mage/sets/RavnicaAllegiance.java @@ -1,10 +1,11 @@ package mage.sets; import mage.cards.ExpansionSet; -import mage.cards.repository.CardCriteria; +import mage.collation.BoosterCollator; +import mage.collation.BoosterStructure; +import mage.collation.CardRun; +import mage.collation.RarityConfiguration; import mage.cards.repository.CardInfo; -import mage.cards.repository.CardRepository; -import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.SetType; @@ -23,12 +24,12 @@ public final class RavnicaAllegiance extends ExpansionSet { super("Ravnica Allegiance", "RNA", ExpansionSet.buildDate(2019, 1, 25), SetType.EXPANSION); this.blockName = "Guilds of Ravnica"; this.hasBoosters = true; - this.numBoosterSpecial = 1; - this.numBoosterLands = 0; + this.numBoosterLands = 1; this.numBoosterCommon = 10; this.numBoosterUncommon = 3; this.numBoosterRare = 1; this.ratioBoosterMythic = 8; + this.ratioBoosterSpecialLand = 1; // replace all basic lands this.maxCardNumberInBooster = 259; cards.add(new SetCardInfo("Absorb", 151, Rarity.RARE, mage.cards.a.Absorb.class)); @@ -307,40 +308,107 @@ public final class RavnicaAllegiance extends ExpansionSet { } @Override - public List getCardsByRarity(Rarity rarity) { - if (rarity == Rarity.COMMON) { - List savedCardsInfos = savedCards.get(rarity); - if (savedCardsInfos == null) { - CardCriteria criteria = new CardCriteria(); - criteria.setCodes(this.code).notTypes(CardType.LAND); - criteria.rarities(rarity).doubleFaced(false); - savedCardsInfos = CardRepository.instance.findCards(criteria); - if (maxCardNumberInBooster != Integer.MAX_VALUE) { - savedCardsInfos.removeIf(next -> next.getCardNumberAsInt() > maxCardNumberInBooster); - } - criteria = new CardCriteria(); - // Gateway Plaza is a normal common: https://twitter.com/EliShffrn/status/1043156989218414593s - criteria.setCodes(this.code).nameExact("Gateway Plaza"); - savedCardsInfos.addAll(CardRepository.instance.findCards(criteria)); - savedCards.put(rarity, savedCardsInfos); - } - // Return a copy of the saved cards information, as not to modify the original. - return new ArrayList<>(savedCardsInfos); - } else { - return super.getCardsByRarity(rarity); + protected List findSpecialCardsByRarity(Rarity rarity) { + List cardInfos = super.findSpecialCardsByRarity(rarity); + if (rarity == Rarity.LAND) { + // Gateway Plaza is a normal common + cardInfos.removeIf(cardInfo -> "Gateway Plaza".equals(cardInfo.getName())); } + return cardInfos; } @Override - public List getSpecialCommon() { - List specialCards = getCardsByRarity(Rarity.SPECIAL); - if (specialCards.isEmpty()) { - CardCriteria criteria = new CardCriteria(); - criteria.rarities(Rarity.COMMON).setCodes(this.code).name("Guildgate"); - List specialCardsSave = CardRepository.instance.findCards(criteria); - savedCards.put(Rarity.SPECIAL, specialCardsSave); - specialCards.addAll(specialCardsSave); - } - return specialCards; + public BoosterCollator createCollator() { + return new RavnicaAllegianceCollator(); + } +} + +// Booster collation info from https://www.lethe.xyz/mtg/collation/rna.html +// Using USA collation for all rarities +// Foil rare sheet used for regular rares as regular rare sheet is not known +class RavnicaAllegianceCollator implements BoosterCollator { + private final CardRun commonA = new CardRun(true, "54", "203", "220", "31", "113", "10", "44", "202", "17", "51", "119", "5", "36", "100", "7", "57", "117", "26", "39", "103", "6", "48", "217", "12", "41", "105", "220", "49", "111", "14", "54", "118", "10", "31", "203", "28", "44", "113", "5", "57", "202", "26", "46", "117", "7", "48", "217", "4", "51", "100", "14", "36", "105", "17", "41", "119", "6", "39", "103", "12", "46", "118", "28", "49", "111", "4"); + private final CardRun commonB = new CardRun(true, "127", "87", "145", "84", "133", "66", "135", "71", "140", "88", "138", "78", "142", "198", "143", "89", "145", "82", "121", "84", "127", "216", "135", "69", "133", "87", "132", "78", "143", "71", "140", "88", "138", "198", "144", "66", "142", "89", "127", "216", "133", "69", "145", "82", "121", "71", "135", "84", "132", "88", "144", "87", "143", "89", "140", "198", "142", "66", "138", "78", "144", "82", "121", "216", "132", "69"); + private final CardRun commonC1 = new CardRun(true, "98", "247", "153", "137", "231", "25", "34", "196", "115", "64", "146", "178", "8", "75", "209", "93", "50", "154", "234", "173", "236", "98", "53", "150", "218", "153", "247", "72", "240", "177", "25", "137", "196", "231", "64", "115", "178", "34", "209", "75", "8", "146", "154", "93", "177", "53", "72", "236", "173", "234", "150", "50", "218", "219", "240"); + private final CardRun commonC2 = new CardRun(true, "13", "176", "67", "33", "110", "63", "30", "171", "77", "94", "152", "237", "33", "110", "24", "139", "190", "104", "63", "13", "171", "91", "184", "77", "176", "30", "152", "104", "219", "67", "24", "139", "33", "110", "190", "237", "94", "67", "184", "91", "176", "13", "171", "237", "63", "104", "24", "139", "152", "77", "190", "91", "184", "30", "94"); + private final CardRun uncommonA = new CardRun(true, "62", "194", "52", "201", "125", "204", "3", "141", "159", "107", "180", "80", "192", "60", "181", "123", "175", "239", "129", "197", "97", "215", "90", "206", "40", "182", "148", "224", "2", "225", "141", "96", "174", "62", "125", "52", "201", "16", "194", "232", "68", "208", "107", "180", "90", "210", "38", "159", "148", "156", "18", "181", "129", "175", "80", "182", "123", "40", "215", "3", "192", "239", "60", "206", "97", "174", "68", "197", "38", "204", "2", "208", "232", "201", "141", "96", "225", "90", "210", "40", "180", "125", "16", "224", "148", "159", "107", "156", "62", "194", "52", "181", "18", "208", "239", "175", "3", "97", "182", "80", "123", "60", "174", "16", "192", "129", "204", "206", "96", "224", "68", "197", "38", "225", "18", "210", "232", "215", "2", "156"); + private final CardRun uncommonB = new CardRun(true, "102", "168", "120", "241", "70", "164", "149", "35", "226", "59", "126", "116", "162", "235", "20", "73", "222", "95", "56", "211", "136", "9", "112", "172", "238", "23", "86", "155", "19", "47", "188", "147", "223", "101", "241", "191", "20", "79", "164", "235", "37", "163", "149", "70", "102", "211", "120", "21", "86", "222", "65", "35", "168", "238", "126", "112", "162", "95", "9", "79", "226", "136", "23", "172", "59", "20", "116", "56", "19", "155", "73", "223", "120", "9", "164", "147", "241", "101", "47", "191", "211", "86", "162", "149", "35", "163", "95", "188", "102", "37", "235", "226", "70", "168", "238", "21", "155", "136", "79", "112", "47", "65", "223", "172", "101", "59", "23", "222", "19", "126", "116", "56", "191", "73", "188", "65", "37", "163", "147", "21"); + private final CardRun rare = new CardRun(true, "11", "160", "251", "151", "32", "170", "74", "193", "108", "166", "124", "207", "61", "213", "134", "42", "1", "245", "221", "99", "242", "187", "76", "233", "167", "157", "130", "179", "254", "165", "45", "15", "85", "229", "81", "22", "158", "128", "230", "55", "169", "27", "246", "189", "109", "212", "259", "32", "186", "227", "131", "161", "106", "83", "228", "11", "122", "185", "61", "214", "248", "151", "58", "99", "199", "207", "74", "221", "124", "193", "15", "92", "200", "42", "170", "251", "158", "29", "187", "259", "114", "179", "76", "157", "134", "22", "213", "254", "205", "128", "228", "108", "161", "45", "165", "81", "183", "245", "229", "212", "55", "242", "227", "109", "43", "130", "27", "189", "246", "106", "230", "233", "195", "131", "83", "214", "248", "29", "58", "92", "185"); + private final CardRun land = new CardRun(false, "243", "244", "249", "250", "252", "253", "255", "256", "257", "258"); + + private final BoosterStructure AABBC1C1C1C1C1C1 = new BoosterStructure( + commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AAABBC1C1C1C1C1 = new BoosterStructure( + commonA, commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AAAABBC2C2C2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, + commonC2, commonC2, commonC2, commonC2 + ); + private final BoosterStructure AAAABBBC2C2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, commonB, + commonC2, commonC2, commonC2 + ); + private final BoosterStructure AAAABBBBC2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, commonB, commonB, + commonC2, commonC2 + ); + private final BoosterStructure AAB = new BoosterStructure(uncommonA, uncommonA, uncommonB); + private final BoosterStructure ABB = new BoosterStructure(uncommonA, uncommonB, uncommonB); + private final BoosterStructure R1 = new BoosterStructure(rare); + private final BoosterStructure L1 = new BoosterStructure(land); + + // In order for equal numbers of each common to exist, the average booster must contain: + // 3.27 A commons (36 / 11) + // 2.18 B commons (24 / 11) + // 2.73 C1 commons (30 / 11, or 60 / 11 in each C1 booster) + // 1.82 C2 commons (20 / 11, or 40 / 11 in each C2 booster) + // These numbers are the same for all sets with 101 commons in A/B/C1/C2 print runs + // and with 10 common slots per booster + private final RarityConfiguration commonRuns = new RarityConfiguration( + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBBC2C2C2, + AAAABBBC2C2C2, + AAAABBBBC2C2 + ); + private final RarityConfiguration uncommonRuns = new RarityConfiguration(AAB, ABB); + private final RarityConfiguration rareRuns = new RarityConfiguration(R1); + private final RarityConfiguration landRuns = new RarityConfiguration(L1); + + @Override + public List makeBooster() { + List booster = new ArrayList<>(); + booster.addAll(commonRuns.getNext().makeRun()); + booster.addAll(uncommonRuns.getNext().makeRun()); + booster.addAll(rareRuns.getNext().makeRun()); + booster.addAll(landRuns.getNext().makeRun()); + return booster; } } diff --git a/Mage.Sets/src/mage/sets/RivalsOfIxalan.java b/Mage.Sets/src/mage/sets/RivalsOfIxalan.java index 02af3eb5614..3b956af573e 100644 --- a/Mage.Sets/src/mage/sets/RivalsOfIxalan.java +++ b/Mage.Sets/src/mage/sets/RivalsOfIxalan.java @@ -1,10 +1,16 @@ - package mage.sets; import mage.cards.ExpansionSet; +import mage.collation.BoosterCollator; +import mage.collation.BoosterStructure; +import mage.collation.CardRun; +import mage.collation.RarityConfiguration; import mage.constants.Rarity; import mage.constants.SetType; +import java.util.ArrayList; +import java.util.List; + /** * @author fireshoes */ @@ -243,4 +249,91 @@ public final class RivalsOfIxalan extends ExpansionSet { cards.add(new SetCardInfo("Zacama, Primal Calamity", 174, Rarity.MYTHIC, mage.cards.z.ZacamaPrimalCalamity.class)); cards.add(new SetCardInfo("Zetalpa, Primal Dawn", 30, Rarity.RARE, mage.cards.z.ZetalpaPrimalDawn.class)); } + + @Override + public BoosterCollator createCollator() { + return new RivalsOfIxalanCollator(); + } +} + +// Booster collation info from https://www.lethe.xyz/mtg/collation/rix.html +// Using USA collation for all rarities +// Foil rare sheet used for regular rares as regular rare sheet is not known +class RivalsOfIxalanCollator implements BoosterCollator { + private final CardRun commonA = new CardRun(true, "105", "11", "50", "104", "101", "43", "119", "20", "34", "116", "26", "49", "118", "15", "44", "108", "19", "57", "95", "5", "47", "104", "27", "43", "105", "26", "101", "119", "20", "50", "118", "11", "34", "116", "19", "44", "95", "5", "57", "108", "15", "49", "104", "5", "47", "101", "27", "49", "119", "11", "50", "116", "20", "57", "95", "26", "43", "105", "15", "34", "108", "19", "44", "118", "27", "47"); + private final CardRun commonB = new CardRun(true, "133", "89", "131", "91", "143", "84", "135", "70", "138", "71", "132", "91", "139", "79", "122", "63", "131", "67", "133", "89", "138", "84", "143", "69", "135", "71", "140", "79", "139", "70", "133", "67", "122", "63", "132", "89", "138", "84", "143", "69", "131", "91", "135", "79", "140", "71", "139", "70", "132", "67", "122", "63", "140", "69"); + private final CardRun commonC1 = new CardRun(true, "74", "120", "7", "96", "114", "61", "178", "183", "137", "75", "36", "106", "4", "61", "181", "25", "52", "141", "75", "7", "114", "120", "36", "183", "137", "181", "74", "141", "61", "25", "106", "114", "178", "7", "4", "137", "96", "74", "52", "120", "183", "36", "75", "106", "178", "4", "52", "181", "74", "141", "120", "25", "36", "114", "178", "7", "183", "61", "181", "75", "52", "137", "4", "106", "25", "141"); + private final CardRun commonC2 = new CardRun(true, "41", "184", "186", "125", "134", "96", "14", "58", "125", "12", "65", "56", "76", "23", "184", "186", "41", "93", "134", "12", "125", "65", "56", "14", "58", "76", "186", "96", "93", "23", "125", "41", "134", "184", "14", "65", "56", "186", "93", "12", "58", "184", "41", "23", "14", "76", "134", "56", "65", "12", "58", "76", "93", "23"); + private final CardRun uncommonA = new CardRun(true, "149", "55", "117", "153", "189", "112", "128", "21", "35", "164", "191", "129", "48", "10", "169", "190", "78", "145", "3", "39", "81", "188", "124", "38", "9", "163", "187", "102", "149", "21", "48", "153", "189", "129", "55", "9", "161", "191", "110", "128", "21", "35", "112", "190", "117", "39", "10", "164", "78", "102", "145", "3", "110", "187", "9", "38", "117", "169", "81", "3", "124", "78", "102", "161", "191", "129", "35", "112", "153", "189", "10", "128", "81", "48", "163", "188", "149", "55", "110", "164", "187", "145", "3", "78", "38", "169", "189", "124", "35", "112", "163", "190", "21", "128", "81", "39", "153", "188", "129", "55", "102", "161", "191", "10", "149", "117", "48", "164", "187", "145", "39", "110", "163", "190", "9", "124", "169", "38", "161", "188"); + private final CardRun uncommonB = new CardRun(true, "156", "53", "17", "73", "54", "107", "83", "6", "146", "72", "168", "37", "13", "73", "32", "113", "82", "123", "54", "17", "62", "172", "109", "126", "8", "53", "97", "80", "148", "155", "1", "85", "156", "98", "6", "83", "37", "107", "72", "146", "170", "53", "82", "172", "113", "123", "1", "32", "97", "62", "148", "168", "13", "85", "156", "109", "17", "80", "54", "98", "73", "126", "155", "8", "83", "170", "97", "123", "6", "37", "107", "72", "146", "172", "1", "62", "168", "98", "8", "82", "53", "113", "80", "148", "156", "32", "73", "155", "109", "126", "13", "54", "107", "85", "146", "170", "17", "83", "168", "113", "6", "72", "37", "97", "62", "123", "172", "13", "85", "155", "98", "126", "8", "32", "109", "82", "148", "170", "1", "80"); + private final CardRun rare = new CardRun(true, "175", "22", "167", "29", "2", "86", "16", "147", "162", "103", "45", "177", "24", "171", "59", "45", "87", "22", "150", "182", "115", "46", "86", "28", "103", "88", "46", "90", "24", "151", "185", "121", "51", "87", "30", "115", "111", "51", "92", "28", "167", "174", "127", "60", "90", "31", "121", "144", "60", "94", "18", "171", "175", "130", "64", "92", "33", "127", "152", "64", "99", "31", "147", "177", "136", "66", "94", "40", "130", "154", "66", "100", "33", "150", "180", "142", "68", "99", "42", "136", "157", "68", "182", "40", "151", "2", "30", "77", "100", "16", "142", "159", "77", "185", "42", "18"); + private final CardRun rareDFC = new CardRun(false, "158", "160", "165", "166", "173", "179", "158", "160", "165", "166", "173", "179", "176"); + private final CardRun land = new CardRun(false, "192", "193", "194", "195", "196"); + + private final BoosterStructure AAABBC1C1C1C1C1 = new BoosterStructure( + commonA, commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AAABBBC1C1C1C1 = new BoosterStructure( + commonA, commonA, commonA, + commonB, commonB, commonB, + commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AAABBBC2C2C2C2 = new BoosterStructure( + commonA, commonA, commonA, + commonB, commonB, commonB, + commonC2, commonC2, commonC2, commonC2 + ); + private final BoosterStructure AAAABBC2C2C2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, + commonC2, commonC2, commonC2, commonC2 + ); + private final BoosterStructure AAB = new BoosterStructure(uncommonA, uncommonA, uncommonB); + private final BoosterStructure ABB = new BoosterStructure(uncommonA, uncommonB, uncommonB); + private final BoosterStructure R1 = new BoosterStructure(rare); + private final BoosterStructure R2 = new BoosterStructure(rareDFC); + private final BoosterStructure L1 = new BoosterStructure(land); + + // In order for equal numbers of each common to exist, the average booster must contain: + // 3.14 A commons (44 / 14) + // 2.57 B commons (36 / 14) + // 2.36 C1 commons (33 / 14) + // 1.93 C2 commons (27 / 14) + // These numbers are the same for all sets with 70 commons in A/B/C1/C2 print runs + // and with 10 common slots per booster + private final RarityConfiguration commonRuns = new RarityConfiguration( + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, AAABBC1C1C1C1C1, + + AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, + AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, + AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, AAABBBC1C1C1C1, + AAABBBC1C1C1C1, + + AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, + AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, + AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, + AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, + AAABBBC2C2C2C2, AAABBBC2C2C2C2, AAABBBC2C2C2C2, + + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, + AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2, AAAABBC2C2C2C2 + ); + private final RarityConfiguration uncommonRuns = new RarityConfiguration(AAB, ABB); + private final RarityConfiguration rareRuns = new RarityConfiguration(R1, R1, R1, R1, R1, R1, R1, R2); + private final RarityConfiguration landRuns = new RarityConfiguration(L1); + + @Override + public List makeBooster() { + List booster = new ArrayList<>(); + booster.addAll(commonRuns.getNext().makeRun()); + booster.addAll(uncommonRuns.getNext().makeRun()); + booster.addAll(rareRuns.getNext().makeRun()); + booster.addAll(landRuns.getNext().makeRun()); + return booster; + } } diff --git a/Mage.Sets/src/mage/sets/SecretLairDrop.java b/Mage.Sets/src/mage/sets/SecretLairDrop.java index 402cbe9f7ae..f5f1af0493b 100644 --- a/Mage.Sets/src/mage/sets/SecretLairDrop.java +++ b/Mage.Sets/src/mage/sets/SecretLairDrop.java @@ -20,6 +20,11 @@ public class SecretLairDrop extends ExpansionSet { this.hasBoosters = false; this.hasBasicLands = true; + cards.add(new SetCardInfo("Demonlord Belzenlok", "159*", Rarity.MYTHIC, mage.cards.d.DemonlordBelzenlok.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Griselbrand", "160*", Rarity.MYTHIC, mage.cards.g.Griselbrand.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kothophed, Soul Hoarder", "162*", Rarity.RARE, mage.cards.k.KothophedSoulHoarder.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Liliana's Contract", "161*", Rarity.RARE, mage.cards.l.LilianasContract.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Razaketh, the Foulblooded", "163*", Rarity.MYTHIC, mage.cards.r.RazakethTheFoulblooded.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Snow-Covered Plains", 1, Rarity.LAND, mage.cards.s.SnowCoveredPlains.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Snow-Covered Island", 2, Rarity.LAND, mage.cards.s.SnowCoveredIsland.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Snow-Covered Swamp", 3, Rarity.LAND, mage.cards.s.SnowCoveredSwamp.class, NON_FULL_USE_VARIOUS)); @@ -117,7 +122,7 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Mountain", 107, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Forest", 108, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Forest", 109, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Swords to Plowshares", 110, Rarity.RARE, mage.cards.s.SwordsToPlowshares.class)); + cards.add(new SetCardInfo("Swords to Plowshares", 110, Rarity.RARE, mage.cards.s.SwordsToPlowshares.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Opt", 111, Rarity.RARE, mage.cards.o.Opt.class)); cards.add(new SetCardInfo("Fatal Push", 112, Rarity.RARE, mage.cards.f.FatalPush.class)); cards.add(new SetCardInfo("Anger of the Gods", 113, Rarity.RARE, mage.cards.a.AngerOfTheGods.class)); @@ -125,8 +130,8 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Glen Elendra Archmage", 115, Rarity.RARE, mage.cards.g.GlenElendraArchmage.class)); cards.add(new SetCardInfo("Mistbind Clique", 116, Rarity.RARE, mage.cards.m.MistbindClique.class)); cards.add(new SetCardInfo("Spellstutter Sprite", 117, Rarity.RARE, mage.cards.s.SpellstutterSprite.class)); - cards.add(new SetCardInfo("Vendilion Clique", 118, Rarity.RARE, mage.cards.v.VendilionClique.class)); cards.add(new SetCardInfo("Swamp", 119, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Vendilion Clique", 118, Rarity.MYTHIC, mage.cards.v.VendilionClique.class)); cards.add(new SetCardInfo("Sower of Temptation", 120, Rarity.RARE, mage.cards.s.SowerOfTemptation.class)); cards.add(new SetCardInfo("Damnation", 121, Rarity.RARE, mage.cards.d.Damnation.class)); cards.add(new SetCardInfo("Enchanted Evening", 122, Rarity.RARE, mage.cards.e.EnchantedEvening.class)); @@ -149,7 +154,7 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Assassin's Trophy", 139, Rarity.RARE, mage.cards.a.AssassinsTrophy.class)); cards.add(new SetCardInfo("Decimate", 140, Rarity.RARE, mage.cards.d.Decimate.class)); cards.add(new SetCardInfo("Dreadbore", 141, Rarity.RARE, mage.cards.d.Dreadbore.class)); - cards.add(new SetCardInfo("Thraximundar", 142, Rarity.RARE, mage.cards.t.Thraximundar.class)); + cards.add(new SetCardInfo("Thraximundar", 142, Rarity.MYTHIC, mage.cards.t.Thraximundar.class)); cards.add(new SetCardInfo("Rick, Steadfast Leader", 143, Rarity.MYTHIC, mage.cards.r.RickSteadfastLeader.class)); cards.add(new SetCardInfo("Daryl, Hunter of Walkers", 144, Rarity.MYTHIC, mage.cards.d.DarylHunterOfWalkers.class)); cards.add(new SetCardInfo("Glenn, the Voice of Calm", 145, Rarity.MYTHIC, mage.cards.g.GlennTheVoiceOfCalm.class)); @@ -160,13 +165,18 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Zulaport Cutthroat", 156, Rarity.RARE, mage.cards.z.ZulaportCutthroat.class)); cards.add(new SetCardInfo("Warren Instigator", 157, Rarity.MYTHIC, mage.cards.w.WarrenInstigator.class)); cards.add(new SetCardInfo("Avenger of Zendikar", 158, Rarity.MYTHIC, mage.cards.a.AvengerOfZendikar.class)); + cards.add(new SetCardInfo("Demonlord Belzenlok", 159, Rarity.MYTHIC, mage.cards.d.DemonlordBelzenlok.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Griselbrand", 160, Rarity.MYTHIC, mage.cards.g.Griselbrand.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Liliana's Contract", 161, Rarity.RARE, mage.cards.l.LilianasContract.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kothophed, Soul Hoarder", 162, Rarity.RARE, mage.cards.k.KothophedSoulHoarder.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Razaketh, the Foulblooded", 163, Rarity.MYTHIC, mage.cards.r.RazakethTheFoulblooded.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Teferi's Protection", 164, Rarity.RARE, mage.cards.t.TeferisProtection.class)); cards.add(new SetCardInfo("Consecrated Sphinx", 165, Rarity.RARE, mage.cards.c.ConsecratedSphinx.class)); cards.add(new SetCardInfo("Collected Company", 166, Rarity.RARE, mage.cards.c.CollectedCompany.class)); cards.add(new SetCardInfo("Amulet of Vigor", 167, Rarity.RARE, mage.cards.a.AmuletOfVigor.class)); cards.add(new SetCardInfo("Balance", 173, Rarity.MYTHIC, mage.cards.b.Balance.class)); cards.add(new SetCardInfo("Brainstorm", 174, Rarity.RARE, mage.cards.b.Brainstorm.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Counterspell", 175, Rarity.RARE, mage.cards.c.Counterspell.class)); + cards.add(new SetCardInfo("Counterspell", 175, Rarity.RARE, mage.cards.c.Counterspell.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Birds of Paradise", 176, Rarity.RARE, mage.cards.b.BirdsOfParadise.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Howling Mine", 177, Rarity.RARE, mage.cards.h.HowlingMine.class)); cards.add(new SetCardInfo("Wasteland", 178, Rarity.RARE, mage.cards.w.Wasteland.class)); @@ -190,6 +200,9 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Commander's Sphere", 203, Rarity.RARE, mage.cards.c.CommandersSphere.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Darksteel Ingot", 204, Rarity.RARE, mage.cards.d.DarksteelIngot.class)); cards.add(new SetCardInfo("Gilded Lotus", 205, Rarity.RARE, mage.cards.g.GildedLotus.class)); + cards.add(new SetCardInfo("Exquisite Blood", 206, Rarity.RARE, mage.cards.e.ExquisiteBlood.class)); + cards.add(new SetCardInfo("Night's Whisper", 207, Rarity.RARE, mage.cards.n.NightsWhisper.class)); + cards.add(new SetCardInfo("Phyrexian Tower", 208, Rarity.RARE, mage.cards.p.PhyrexianTower.class)); cards.add(new SetCardInfo("Elesh Norn, Grand Cenobite", 209, Rarity.MYTHIC, mage.cards.e.EleshNornGrandCenobite.class)); cards.add(new SetCardInfo("Jin-Gitaxias, Core Augur", 210, Rarity.MYTHIC, mage.cards.j.JinGitaxiasCoreAugur.class)); cards.add(new SetCardInfo("Sheoldred, Whispering One", 211, Rarity.MYTHIC, mage.cards.s.SheoldredWhisperingOne.class)); @@ -206,6 +219,14 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Grave Titan", 223, Rarity.MYTHIC, mage.cards.g.GraveTitan.class)); cards.add(new SetCardInfo("Inferno Titan", 224, Rarity.MYTHIC, mage.cards.i.InfernoTitan.class)); cards.add(new SetCardInfo("Kroxa, Titan of Death's Hunger", 225, Rarity.MYTHIC, mage.cards.k.KroxaTitanOfDeathsHunger.class)); + cards.add(new SetCardInfo("Path to Exile", 226, Rarity.RARE, mage.cards.p.PathToExile.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Well of Lost Dreams", 227, Rarity.RARE, mage.cards.w.WellOfLostDreams.class)); + cards.add(new SetCardInfo("Frantic Search", 228, Rarity.RARE, mage.cards.f.FranticSearch.class)); + cards.add(new SetCardInfo("Intruder Alarm", 229, Rarity.RARE, mage.cards.i.IntruderAlarm.class)); + cards.add(new SetCardInfo("Shelldock Isle", 230, Rarity.RARE, mage.cards.s.ShelldockIsle.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gravecrawler", 231, Rarity.RARE, mage.cards.g.Gravecrawler.class)); + cards.add(new SetCardInfo("Liliana, Death's Majesty", 232, Rarity.MYTHIC, mage.cards.l.LilianaDeathsMajesty.class)); + cards.add(new SetCardInfo("Rise of the Dark Realms", 233, Rarity.MYTHIC, mage.cards.r.RiseOfTheDarkRealms.class)); cards.add(new SetCardInfo("Brazen Borrower", 234, Rarity.MYTHIC, mage.cards.b.BrazenBorrower.class)); cards.add(new SetCardInfo("Vindictive Lich", 235, Rarity.RARE, mage.cards.v.VindictiveLich.class)); cards.add(new SetCardInfo("Meandering Towershell", 236, Rarity.RARE, mage.cards.m.MeanderingTowershell.class)); @@ -245,10 +266,10 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Ob Nixilis Reignited", 274, Rarity.MYTHIC, mage.cards.o.ObNixilisReignited.class)); cards.add(new SetCardInfo("Sire of Insanity", 275, Rarity.RARE, mage.cards.s.SireOfInsanity.class)); cards.add(new SetCardInfo("Sliver Hivelord", 276, Rarity.MYTHIC, mage.cards.s.SliverHivelord.class)); - cards.add(new SetCardInfo("Spellskite", 277, Rarity.RARE, mage.cards.s.Spellskite.class)); + cards.add(new SetCardInfo("Spellskite", 277, Rarity.RARE, mage.cards.s.Spellskite.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sanctum Prelate", 278, Rarity.MYTHIC, mage.cards.s.SanctumPrelate.class)); cards.add(new SetCardInfo("Carpet of Flowers", 279, Rarity.RARE, mage.cards.c.CarpetOfFlowers.class)); - cards.add(new SetCardInfo("Sphere of Safety", 280, Rarity.RARE, mage.cards.s.SphereOfSafety.class)); + cards.add(new SetCardInfo("Sphere of Safety", 280, Rarity.RARE, mage.cards.s.SphereOfSafety.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Karmic Guide", 281, Rarity.RARE, mage.cards.k.KarmicGuide.class)); cards.add(new SetCardInfo("Mesa Enchantress", 282, Rarity.RARE, mage.cards.m.MesaEnchantress.class)); cards.add(new SetCardInfo("Archaeomancer", 283, Rarity.RARE, mage.cards.a.Archaeomancer.class)); @@ -268,16 +289,139 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Mother of Runes", 297, Rarity.RARE, mage.cards.m.MotherOfRunes.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mother of Runes", 298, Rarity.RARE, mage.cards.m.MotherOfRunes.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mother of Runes", 299, Rarity.RARE, mage.cards.m.MotherOfRunes.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ancient Den", 300, Rarity.RARE, mage.cards.a.AncientDen.class)); + cards.add(new SetCardInfo("Seat of the Synod", 301, Rarity.RARE, mage.cards.s.SeatOfTheSynod.class)); + cards.add(new SetCardInfo("Vault of Whispers", 302, Rarity.RARE, mage.cards.v.VaultOfWhispers.class)); + cards.add(new SetCardInfo("Great Furnace", 303, Rarity.RARE, mage.cards.g.GreatFurnace.class)); + cards.add(new SetCardInfo("Tree of Tales", 304, Rarity.RARE, mage.cards.t.TreeOfTales.class)); + cards.add(new SetCardInfo("Ravenous Chupacabra", 305, Rarity.RARE, mage.cards.r.RavenousChupacabra.class)); + cards.add(new SetCardInfo("Managorger Hydra", 306, Rarity.RARE, mage.cards.m.ManagorgerHydra.class)); + cards.add(new SetCardInfo("Pathbreaker Ibex", 307, Rarity.RARE, mage.cards.p.PathbreakerIbex.class)); + cards.add(new SetCardInfo("Temur Sabertooth", 308, Rarity.RARE, mage.cards.t.TemurSabertooth.class)); + cards.add(new SetCardInfo("Winding Constrictor", 309, Rarity.RARE, mage.cards.w.WindingConstrictor.class)); cards.add(new SetCardInfo("Unbreakable Formation", 310, Rarity.RARE, mage.cards.u.UnbreakableFormation.class)); cards.add(new SetCardInfo("Whir of Invention", 311, Rarity.RARE, mage.cards.w.WhirOfInvention.class)); cards.add(new SetCardInfo("Hero's Downfall", 312, Rarity.RARE, mage.cards.h.HerosDownfall.class)); cards.add(new SetCardInfo("Impact Tremors", 313, Rarity.RARE, mage.cards.i.ImpactTremors.class)); cards.add(new SetCardInfo("Primal Vigor", 314, Rarity.RARE, mage.cards.p.PrimalVigor.class)); cards.add(new SetCardInfo("Commander's Sphere", 315, Rarity.RARE, mage.cards.c.CommandersSphere.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fleet Swallower", 316, Rarity.RARE, mage.cards.f.FleetSwallower.class)); + cards.add(new SetCardInfo("Goblin Trashmaster", 317, Rarity.RARE, mage.cards.g.GoblinTrashmaster.class)); + cards.add(new SetCardInfo("Ilharg, the Raze-Boar", 318, Rarity.MYTHIC, mage.cards.i.IlhargTheRazeBoar.class)); + cards.add(new SetCardInfo("Protean Hulk", 319, Rarity.RARE, mage.cards.p.ProteanHulk.class)); + cards.add(new SetCardInfo("Gishath, Sun's Avatar", 320, Rarity.MYTHIC, mage.cards.g.GishathSunsAvatar.class)); + cards.add(new SetCardInfo("Dismember", 321, Rarity.RARE, mage.cards.d.Dismember.class)); + cards.add(new SetCardInfo("Blasphemous Act", 322, Rarity.RARE, mage.cards.b.BlasphemousAct.class)); + cards.add(new SetCardInfo("Beast Within", 323, Rarity.RARE, mage.cards.b.BeastWithin.class)); + cards.add(new SetCardInfo("Grafdigger's Cage", 324, Rarity.RARE, mage.cards.g.GrafdiggersCage.class)); + cards.add(new SetCardInfo("Snow-Covered Plains", 325, Rarity.LAND, mage.cards.s.SnowCoveredPlains.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Snow-Covered Island", 326, Rarity.LAND, mage.cards.s.SnowCoveredIsland.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Snow-Covered Swamp", 327, Rarity.LAND, mage.cards.s.SnowCoveredSwamp.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Snow-Covered Mountain", 328, Rarity.LAND, mage.cards.s.SnowCoveredMountain.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Snow-Covered Forest", 329, Rarity.LAND, mage.cards.s.SnowCoveredForest.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Aether Gust", 330, Rarity.RARE, mage.cards.a.AetherGust.class)); + cards.add(new SetCardInfo("Counterspell", 331, Rarity.RARE, mage.cards.c.Counterspell.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fabricate", 332, Rarity.RARE, mage.cards.f.Fabricate.class)); + cards.add(new SetCardInfo("Fact or Fiction", 333, Rarity.RARE, mage.cards.f.FactOrFiction.class)); + cards.add(new SetCardInfo("Mystical Tutor", 334, Rarity.RARE, mage.cards.m.MysticalTutor.class)); + cards.add(new SetCardInfo("Arvinox, the Mind Flail", 340, Rarity.MYTHIC, mage.cards.a.ArvinoxTheMindFlail.class)); + cards.add(new SetCardInfo("Sophina, Spearsage Deserter", 341, Rarity.RARE, mage.cards.s.SophinaSpearsageDeserter.class)); + cards.add(new SetCardInfo("Hargilde, Kindly Runechanter", 342, Rarity.RARE, mage.cards.h.HargildeKindlyRunechanter.class)); + cards.add(new SetCardInfo("Cecily, Haunted Mage", 343, Rarity.RARE, mage.cards.c.CecilyHauntedMage.class)); + cards.add(new SetCardInfo("Bjorna, Nightfall Alchemist", 344, Rarity.RARE, mage.cards.b.BjornaNightfallAlchemist.class)); + cards.add(new SetCardInfo("Elmar, Ulvenwald Informant", 345, Rarity.RARE, mage.cards.e.ElmarUlvenwaldInformant.class)); + cards.add(new SetCardInfo("Othelm, Sigardian Outcast", 346, Rarity.RARE, mage.cards.o.OthelmSigardianOutcast.class)); + cards.add(new SetCardInfo("Wernog, Rider's Chaplain", 347, Rarity.RARE, mage.cards.w.WernogRidersChaplain.class)); + cards.add(new SetCardInfo("Moorland Haunt", 349, Rarity.RARE, mage.cards.m.MoorlandHaunt.class)); + cards.add(new SetCardInfo("Vault of the Archangel", 350, Rarity.RARE, mage.cards.v.VaultOfTheArchangel.class)); + cards.add(new SetCardInfo("Nephalia Drownyard", 351, Rarity.RARE, mage.cards.n.NephaliaDrownyard.class)); + cards.add(new SetCardInfo("Desolate Lighthouse", 352, Rarity.RARE, mage.cards.d.DesolateLighthouse.class)); + cards.add(new SetCardInfo("Stensia Bloodhall", 353, Rarity.RARE, mage.cards.s.StensiaBloodhall.class)); + cards.add(new SetCardInfo("Grim Backwoods", 354, Rarity.RARE, mage.cards.g.GrimBackwoods.class)); + cards.add(new SetCardInfo("Kessig Wolf Run", 355, Rarity.RARE, mage.cards.k.KessigWolfRun.class)); + cards.add(new SetCardInfo("Slayers' Stronghold", 356, Rarity.RARE, mage.cards.s.SlayersStronghold.class)); + cards.add(new SetCardInfo("Gavony Township", 357, Rarity.RARE, mage.cards.g.GavonyTownship.class)); + cards.add(new SetCardInfo("Alchemist's Refuge", 358, Rarity.RARE, mage.cards.a.AlchemistsRefuge.class)); + cards.add(new SetCardInfo("Plains", 359, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Island", 360, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 361, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 362, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Forest", 363, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Swords to Plowshares", 364, Rarity.RARE, mage.cards.s.SwordsToPlowshares.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Grim Tutor", 365, Rarity.RARE, mage.cards.g.GrimTutor.class)); + cards.add(new SetCardInfo("Blood Moon", 366, Rarity.RARE, mage.cards.b.BloodMoon.class)); + cards.add(new SetCardInfo("Cut // Ribbons", 367, Rarity.RARE, mage.cards.c.CutRibbons.class)); + cards.add(new SetCardInfo("Teferi's Puzzle Box", 368, Rarity.RARE, mage.cards.t.TeferisPuzzleBox.class)); cards.add(new SetCardInfo("Generous Gift", 369, Rarity.RARE, mage.cards.g.GenerousGift.class)); cards.add(new SetCardInfo("Chain Lightning", 370, Rarity.RARE, mage.cards.c.ChainLightning.class)); cards.add(new SetCardInfo("Kodama's Reach", 371, Rarity.RARE, mage.cards.k.KodamasReach.class)); cards.add(new SetCardInfo("Heirloom Blade", 372, Rarity.RARE, mage.cards.h.HeirloomBlade.class)); + cards.add(new SetCardInfo("Mulldrifter", 373, Rarity.RARE, mage.cards.m.Mulldrifter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mulldrifter", 374, Rarity.RARE, mage.cards.m.Mulldrifter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Craterhoof Behemoth", 375, Rarity.MYTHIC, mage.cards.c.CraterhoofBehemoth.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Craterhoof Behemoth", 376, Rarity.MYTHIC, mage.cards.c.CraterhoofBehemoth.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Metalwork Colossus", 377, Rarity.RARE, mage.cards.m.MetalworkColossus.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Metalwork Colossus", 378, Rarity.RARE, mage.cards.m.MetalworkColossus.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Zndrsplt, Eye of Wisdom", 379, Rarity.RARE, mage.cards.z.ZndrspltEyeOfWisdom.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Zndrsplt, Eye of Wisdom", "379b", Rarity.RARE, mage.cards.z.ZndrspltEyeOfWisdom.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Okaun, Eye of Chaos", 380, Rarity.RARE, mage.cards.o.OkaunEyeOfChaos.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Okaun, Eye of Chaos", "380b", Rarity.RARE, mage.cards.o.OkaunEyeOfChaos.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Propaganda", 381, Rarity.RARE, mage.cards.p.Propaganda.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Propaganda", "381b", Rarity.RARE, mage.cards.p.Propaganda.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Stitch in Time", 382, Rarity.RARE, mage.cards.s.StitchInTime.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Stitch in Time", "382b", Rarity.RARE, mage.cards.s.StitchInTime.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Krark's Thumb", 383, Rarity.RARE, mage.cards.k.KrarksThumb.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Krark's Thumb", "383b", Rarity.RARE, mage.cards.k.KrarksThumb.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 384, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Island", 385, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Tamiyo, the Moon Sage", 396, Rarity.MYTHIC, mage.cards.t.TamiyoTheMoonSage.class)); + cards.add(new SetCardInfo("Ajani, Mentor of Heroes", 397, Rarity.MYTHIC, mage.cards.a.AjaniMentorOfHeroes.class)); + cards.add(new SetCardInfo("Angrath, the Flame-Chained", 398, Rarity.MYTHIC, mage.cards.a.AngrathTheFlameChained.class)); + cards.add(new SetCardInfo("Ashiok, Dream Render", 399, Rarity.RARE, mage.cards.a.AshiokDreamRender.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sorin, Grim Nemesis", 400, Rarity.MYTHIC, mage.cards.s.SorinGrimNemesis.class)); + cards.add(new SetCardInfo("Brain Freeze", 410, Rarity.RARE, mage.cards.b.BrainFreeze.class)); + cards.add(new SetCardInfo("Bribery", 411, Rarity.RARE, mage.cards.b.Bribery.class)); + cards.add(new SetCardInfo("Snap", 412, Rarity.RARE, mage.cards.s.Snap.class)); + cards.add(new SetCardInfo("Unmask", 413, Rarity.RARE, mage.cards.u.Unmask.class)); + cards.add(new SetCardInfo("Shadow of Doubt", 414, Rarity.RARE, mage.cards.s.ShadowOfDoubt.class)); + cards.add(new SetCardInfo("Plains", 415, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Island", 416, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 417, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 418, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Forest", 419, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Hokori, Dust Drinker", 420, Rarity.RARE, mage.cards.h.HokoriDustDrinker.class)); + cards.add(new SetCardInfo("Kira, Great Glass-Spinner", 421, Rarity.RARE, mage.cards.k.KiraGreatGlassSpinner.class)); + cards.add(new SetCardInfo("Eidolon of the Great Revel", 422, Rarity.RARE, mage.cards.e.EidolonOfTheGreatRevel.class)); + cards.add(new SetCardInfo("Elvish Spirit Guide", 423, Rarity.RARE, mage.cards.e.ElvishSpiritGuide.class)); + cards.add(new SetCardInfo("Ghostly Prison", 424, Rarity.RARE, mage.cards.g.GhostlyPrison.class)); + cards.add(new SetCardInfo("Freed from the Real", 425, Rarity.RARE, mage.cards.f.FreedFromTheReal.class)); + cards.add(new SetCardInfo("Boseiju, Who Shelters All", 426, Rarity.RARE, mage.cards.b.BoseijuWhoSheltersAll.class)); + cards.add(new SetCardInfo("Hall of the Bandit Lord", 427, Rarity.RARE, mage.cards.h.HallOfTheBanditLord.class)); + cards.add(new SetCardInfo("E. Honda, Sumo Champion", 428, Rarity.RARE, mage.cards.e.EHondaSumoChampion.class)); + cards.add(new SetCardInfo("Ryu, World Warrior", 429, Rarity.RARE, mage.cards.r.RyuWorldWarrior.class)); + cards.add(new SetCardInfo("Ken, Burning Brawler", 430, Rarity.RARE, mage.cards.k.KenBurningBrawler.class)); + cards.add(new SetCardInfo("Blanka, Ferocious Friend", 431, Rarity.RARE, mage.cards.b.BlankaFerociousFriend.class)); + cards.add(new SetCardInfo("Chun-Li, Countless Kicks", 432, Rarity.RARE, mage.cards.c.ChunLiCountlessKicks.class)); + cards.add(new SetCardInfo("Dhalsim, Pliable Pacifist", 433, Rarity.RARE, mage.cards.d.DhalsimPliablePacifist.class)); + cards.add(new SetCardInfo("Guile, Sonic Soldier", 434, Rarity.RARE, mage.cards.g.GuileSonicSoldier.class)); + cards.add(new SetCardInfo("Zangief, the Red Cyclone", 435, Rarity.RARE, mage.cards.z.ZangiefTheRedCyclone.class)); + cards.add(new SetCardInfo("Windbrisk Heights", 436, Rarity.RARE, mage.cards.w.WindbriskHeights.class)); + cards.add(new SetCardInfo("Shelldock Isle", 437, Rarity.RARE, mage.cards.s.ShelldockIsle.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Howltooth Hollow", 438, Rarity.RARE, mage.cards.h.HowltoothHollow.class)); + cards.add(new SetCardInfo("Spinerock Knoll", 439, Rarity.RARE, mage.cards.s.SpinerockKnoll.class)); + cards.add(new SetCardInfo("Mosswort Bridge", 440, Rarity.RARE, mage.cards.m.MosswortBridge.class)); + cards.add(new SetCardInfo("Path to Exile", 477, Rarity.RARE, mage.cards.p.PathToExile.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rhystic Study", 478, Rarity.RARE, mage.cards.r.RhysticStudy.class)); + cards.add(new SetCardInfo("Duress", 479, Rarity.RARE, mage.cards.d.Duress.class)); + cards.add(new SetCardInfo("Seize the Day", 480, Rarity.RARE, mage.cards.s.SeizeTheDay.class)); + cards.add(new SetCardInfo("Krosan Grip", 481, Rarity.RARE, mage.cards.k.KrosanGrip.class)); + cards.add(new SetCardInfo("Counterflux", 482, Rarity.RARE, mage.cards.c.Counterflux.class)); + cards.add(new SetCardInfo("Thran Dynamo", 483, Rarity.RARE, mage.cards.t.ThranDynamo.class)); + cards.add(new SetCardInfo("Plains", 484, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 485, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 486, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 487, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 488, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Karn, the Great Creator", 501, Rarity.RARE, mage.cards.k.KarnTheGreatCreator.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ugin, the Ineffable", 502, Rarity.RARE, mage.cards.u.UginTheIneffable.class)); cards.add(new SetCardInfo("Gideon Blackblade", 503, Rarity.MYTHIC, mage.cards.g.GideonBlackblade.class)); @@ -305,7 +449,7 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Tamiyo, Collector of Tales", 525, Rarity.RARE, mage.cards.t.TamiyoCollectorOfTales.class)); cards.add(new SetCardInfo("Teferi, Time Raveler", 526, Rarity.RARE, mage.cards.t.TeferiTimeRaveler.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Angrath, Captain of Chaos", 527, Rarity.UNCOMMON, mage.cards.a.AngrathCaptainOfChaos.class)); - cards.add(new SetCardInfo("Ashiok, Dream Render", 528, Rarity.UNCOMMON, mage.cards.a.AshiokDreamRender.class)); + cards.add(new SetCardInfo("Ashiok, Dream Render", 528, Rarity.UNCOMMON, mage.cards.a.AshiokDreamRender.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Dovin, Hand of Control", 529, Rarity.UNCOMMON, mage.cards.d.DovinHandOfControl.class)); cards.add(new SetCardInfo("Huatli, the Sun's Heart", 530, Rarity.UNCOMMON, mage.cards.h.HuatliTheSunsHeart.class)); cards.add(new SetCardInfo("Kaya, Bane of the Dead", 531, Rarity.UNCOMMON, mage.cards.k.KayaBaneOfTheDead.class)); @@ -317,26 +461,59 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Tibalt, the Fiend-Blooded", 537, Rarity.MYTHIC, mage.cards.t.TibaltTheFiendBlooded.class)); cards.add(new SetCardInfo("Evolving Wilds", 538, Rarity.RARE, mage.cards.e.EvolvingWilds.class)); cards.add(new SetCardInfo("Swamp", 539, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Plains", 544, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 548, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 549, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Island", 551, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Island", 552, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 553, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Island", 555, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 556, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Swamp", 557, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Swamp", 558, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 559, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Swamp", 560, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Swamp", 561, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Swamp", 563, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mountain", 566, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 568, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 569, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 570, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 571, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Forest", 572, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Forest", 573, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 576, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 578, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Lucille", 581, Rarity.MYTHIC, mage.cards.l.Lucille.class)); cards.add(new SetCardInfo("Brainstorm", 582, Rarity.RARE, mage.cards.b.Brainstorm.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fblthp, the Lost", 583, Rarity.RARE, mage.cards.f.FblthpTheLost.class)); + cards.add(new SetCardInfo("Wrexial, the Risen Deep", 584, Rarity.MYTHIC, mage.cards.w.WrexialTheRisenDeep.class)); cards.add(new SetCardInfo("Terramorphic Expanse", 585, Rarity.RARE, mage.cards.t.TerramorphicExpanse.class)); + cards.add(new SetCardInfo("Spellskite", 587, Rarity.RARE, mage.cards.s.Spellskite.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sphere of Safety", 588, Rarity.RARE, mage.cards.s.SphereOfSafety.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Arcane Signet", 589, Rarity.RARE, mage.cards.a.ArcaneSignet.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Crash Through", 594, Rarity.RARE, mage.cards.c.CrashThrough.class)); + cards.add(new SetCardInfo("Crash Through", 591, Rarity.RARE, mage.cards.c.CrashThrough.class)); + cards.add(new SetCardInfo("Persistent Petitioners", 596, Rarity.RARE, mage.cards.p.PersistentPetitioners.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Persistent Petitioners", 597, Rarity.RARE, mage.cards.p.PersistentPetitioners.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Persistent Petitioners", 598, Rarity.RARE, mage.cards.p.PersistentPetitioners.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Eldrazi Monument", 603, Rarity.MYTHIC, mage.cards.e.EldraziMonument.class)); cards.add(new SetCardInfo("Ornithopter", 604, Rarity.RARE, mage.cards.o.Ornithopter.class)); cards.add(new SetCardInfo("Panharmonicon", 605, Rarity.RARE, mage.cards.p.Panharmonicon.class)); cards.add(new SetCardInfo("Swiftfoot Boots", 606, Rarity.RARE, mage.cards.s.SwiftfootBoots.class)); cards.add(new SetCardInfo("Rogue's Passage", 607, Rarity.RARE, mage.cards.r.RoguesPassage.class)); + cards.add(new SetCardInfo("Darksteel Citadel", 608, Rarity.RARE, mage.cards.d.DarksteelCitadel.class)); + cards.add(new SetCardInfo("Havengul Laboratory", 609, Rarity.RARE, mage.cards.h.HavengulLaboratory.class)); + cards.add(new SetCardInfo("Havengul Mystery", 609, Rarity.RARE, mage.cards.h.HavengulMystery.class)); + cards.add(new SetCardInfo("Battlefield Forge", 669, Rarity.RARE, mage.cards.b.BattlefieldForge.class)); + cards.add(new SetCardInfo("Plains", 670, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Questing Phelddagrif", 671, Rarity.RARE, mage.cards.q.QuestingPhelddagrif.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Questing Phelddagrif", 672, Rarity.RARE, mage.cards.q.QuestingPhelddagrif.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Spore Frog", 696, Rarity.RARE, mage.cards.s.SporeFrog.class)); + cards.add(new SetCardInfo("Command Tower", 697, Rarity.RARE, mage.cards.c.CommandTower.class)); + cards.add(new SetCardInfo("Idyllic Tutor", 1020, Rarity.RARE, mage.cards.i.IdyllicTutor.class)); + cards.add(new SetCardInfo("Swords to Plowshares", 1021, Rarity.RARE, mage.cards.s.SwordsToPlowshares.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Solve the Equation", 1022, Rarity.RARE, mage.cards.s.SolveTheEquation.class)); + cards.add(new SetCardInfo("Praetor's Grasp", 1023, Rarity.RARE, mage.cards.p.PraetorsGrasp.class)); + cards.add(new SetCardInfo("Veil of Summer", 1024, Rarity.RARE, mage.cards.v.VeilOfSummer.class)); } } diff --git a/Mage.Sets/src/mage/sets/ShadowsOverInnistrad.java b/Mage.Sets/src/mage/sets/ShadowsOverInnistrad.java index 186098b1f5d..bd1e36b3394 100644 --- a/Mage.Sets/src/mage/sets/ShadowsOverInnistrad.java +++ b/Mage.Sets/src/mage/sets/ShadowsOverInnistrad.java @@ -2,15 +2,15 @@ package mage.sets; import mage.cards.Card; import mage.cards.ExpansionSet; -import mage.cards.repository.CardCriteria; -import mage.cards.repository.CardInfo; -import mage.cards.repository.CardRepository; +import mage.collation.BoosterCollator; +import mage.collation.BoosterStructure; +import mage.collation.CardRun; +import mage.collation.RarityConfiguration; import mage.constants.Rarity; import mage.constants.SetType; import mage.util.RandomUtil; import java.util.ArrayList; -import java.util.EnumMap; import java.util.List; /** @@ -24,8 +24,6 @@ public final class ShadowsOverInnistrad extends ExpansionSet { return instance; } - private final EnumMap> savedDoubleFacedCards; - private ShadowsOverInnistrad() { super("Shadows over Innistrad", "SOI", ExpansionSet.buildDate(2016, 4, 8), SetType.EXPANSION); this.blockName = "Shadows over Innistrad"; @@ -36,10 +34,9 @@ public final class ShadowsOverInnistrad extends ExpansionSet { this.numBoosterUncommon = 3; this.numBoosterRare = 1; this.ratioBoosterMythic = 8; + this.ratioBoosterSpecialCommon = 8; this.numBoosterDoubleFaced = 1; - savedDoubleFacedCards = new EnumMap<>(Rarity.class); - cards.add(new SetCardInfo("Aberrant Researcher", 49, Rarity.UNCOMMON, mage.cards.a.AberrantResearcher.class)); cards.add(new SetCardInfo("Accursed Witch", 97, Rarity.UNCOMMON, mage.cards.a.AccursedWitch.class)); cards.add(new SetCardInfo("Aim High", 193, Rarity.COMMON, mage.cards.a.AimHigh.class)); @@ -377,53 +374,149 @@ public final class ShadowsOverInnistrad extends ExpansionSet { cards.add(new SetCardInfo("Woodland Stream", 282, Rarity.UNCOMMON, mage.cards.w.WoodlandStream.class)); } - /* add double faced card for SOI booster - * add only common or uncommon - 80/120 packs contain one of 20 uncommon DFCs and 40/120 packs contain one of 4 common DFCs - */ + // add common or uncommon double faced card to booster + // 40/120 packs contain one of 4 common DFCs and 80/120 packs contain one of 20 uncommon DFCs @Override - public void addDoubleFace(List booster) { + protected void addDoubleFace(List booster) { + Rarity rarity; for (int i = 0; i < numBoosterDoubleFaced; i++) { - List doubleFacedCards; - if (RandomUtil.nextInt(15) < 10) { - doubleFacedCards = getDoubleFacedCardsByRarity(Rarity.UNCOMMON); + if (RandomUtil.nextInt(120) < 40) { + rarity = Rarity.COMMON; } else { - doubleFacedCards = getDoubleFacedCardsByRarity(Rarity.COMMON); + rarity = Rarity.UNCOMMON; } - addToBooster(booster, doubleFacedCards); + addToBooster(booster, getSpecialCardsByRarity(rarity)); } } - private List getDoubleFacedCardsByRarity(Rarity rarity) { - List savedCardsInfos = savedDoubleFacedCards.get(rarity); - if (savedCardsInfos == null) { - CardCriteria criteria = new CardCriteria(); - criteria.setCodes(getCode()); - criteria.rarities(rarity); - criteria.doubleFaced(true); - savedCardsInfos = CardRepository.instance.findCards(criteria); - savedDoubleFacedCards.put(rarity, savedCardsInfos); - } - // Return a copy of the saved cards information, as not to let modify the original. - return new ArrayList<>(savedCardsInfos); - } - + // Then about an eighth of the packs will have a second double-faced card, which will be a rare or mythic rare + // 12/15 of such packs contain one of 6 rare DFCs and 3/15 packs contain one of 3 mythic DFCs @Override - public int getNumberOfSpecialCommons() { - // Then about an eighth of the packs will have a second double-faced card, which will be a rare or mythic rare. - return RandomUtil.nextInt(8) == 0 ? 1 : 0; - } - - @Override - public void addSpecialCommon(List booster, int number) { + protected void addSpecialCards(List booster, int number) { // number is here always 1 - List doubleFacedCards; - if (RandomUtil.nextInt(8) > 0) { - doubleFacedCards = getDoubleFacedCardsByRarity(Rarity.RARE); + Rarity rarity; + if (RandomUtil.nextInt(15) < 12) { + rarity = Rarity.RARE; } else { - doubleFacedCards = getDoubleFacedCardsByRarity(Rarity.MYTHIC); + rarity = Rarity.MYTHIC; } - addToBooster(booster, doubleFacedCards); + addToBooster(booster, getSpecialCardsByRarity(rarity)); } + @Override + public BoosterCollator createCollator() { + return new ShadowsOverInnistradCollator(); + } +} + +// Booster collation info from https://www.lethe.xyz/mtg/collation/soi.html +// Using USA collation for common/uncommon, rare collation inferred from other sets +class ShadowsOverInnistradCollator implements BoosterCollator { + private final CardRun commonA = new CardRun(true, "95", "10", "76", "47", "78", "44", "85", "26", "91", "39", "80", "11", "74", "48", "72", "35", "87", "24", "86", "41", "71", "17", "70", "9", "89", "26", "66", "25", "55", "91", "78", "19", "64", "14", "51", "44", "76", "47", "85", "48", "95", "3", "80", "24", "71", "9", "72", "17", "87", "39", "74", "10", "70", "11", "66", "35", "86", "25", "51", "41", "89", "19", "55", "14", "64", "3"); + private final CardRun commonB = new CardRun(true, "268", "216", "40", "172", "112", "84", "267", "146", "4", "136", "213", "262", "133", "27", "67", "280", "255", "117", "18", "56", "218", "4", "268", "136", "180", "216", "40", "172", "112", "267", "84", "146", "27", "280", "213", "117", "262", "67", "18", "218", "180", "133", "267", "172", "4", "56", "112", "216", "268", "40", "146", "84", "136", "255", "213", "27", "280", "133", "262", "67", "218", "18", "117", "180", "255", "56"); + private final CardRun commonC1 = new CardRun(true, "214", "106", "154", "204", "121", "174", "217", "137", "186", "197", "132", "126", "156", "224", "110", "177", "223", "98", "176", "198", "106", "189", "214", "121", "174", "217", "130", "153", "222", "123", "165", "232", "126", "154", "204", "137", "186", "224", "132", "176", "223", "98", "153", "198", "138", "189", "232", "130", "156", "197", "110", "177", "222", "123", "165"); + private final CardRun commonC2 = new CardRun(true, "205", "134", "164", "237", "142", "185", "231", "105", "188", "239", "142", "168", "193", "143", "191", "205", "144", "178", "196", "115", "143", "164", "231", "134", "188", "196", "115", "185", "193", "105", "168", "237", "144", "191", "239", "143", "164", "205", "138", "178", "231", "142", "185", "193", "134", "191", "237", "144", "188", "196", "115", "168", "239", "105", "178"); + private final CardRun uncommonA = new CardRun(true, "166", "100", "238", "57", "30", "160", "120", "206", "68", "28", "261", "170", "104", "50", "208", "36", "114", "73", "195", "61", "22", "254", "150", "129", "201", "79", "32", "140", "184", "219", "52", "38", "220", "175", "120", "240", "93", "45", "127", "152", "238", "50", "28", "254", "160", "100", "206", "57", "30", "114", "166", "195", "68", "22", "257", "170", "129", "79", "208", "38", "73", "175", "220", "93", "36", "261", "150", "104", "219", "61", "45", "127", "184", "201", "52", "28", "240", "152", "120", "195", "166", "32", "140", "170", "238", "57", "22", "254", "175", "100", "206", "50", "30", "114", "93", "220", "68", "38", "257", "160", "129", "79", "208", "36", "127", "184", "240", "73", "45", "261", "152", "104", "219", "52", "32", "140", "150", "201", "61", "257"); + private final CardRun uncommonB = new CardRun(true, "161", "23", "263", "103", "82", "277", "33", "236", "275", "135", "258", "167", "29", "211", "113", "269", "173", "20", "221", "279", "125", "83", "282", "37", "207", "118", "77", "187", "42", "259", "273", "128", "62", "161", "7", "263", "103", "90", "171", "23", "199", "277", "135", "60", "173", "33", "236", "102", "82", "279", "20", "258", "275", "113", "83", "183", "29", "269", "125", "77", "167", "37", "221", "282", "259", "187", "42", "211", "118", "62", "171", "23", "207", "273", "128", "90", "183", "7", "236", "102", "263", "167", "29", "221", "277", "103", "60", "279", "33", "258", "135", "82", "161", "20", "199", "275", "113", "83", "187", "37", "211", "125", "77", "173", "42", "207", "282", "118", "90", "171", "269", "199", "128", "62", "183", "7", "259", "273", "102", "60"); + private final CardRun commonDFC = new CardRun(true, "149", "116", "203", "229", "119", "49", "158", "260", "54", "149", "194", "6", "229", "169", "158", "147", "256", "210", "97", "209", "34", "182", "94", "229", "215", "210", "116", "46", "149", "266", "190", "158", "54", "203", "149", "260", "210", "147", "194", "158", "6", "256", "229", "49", "119", "210", "169", "149", "209", "34", "229", "215", "94", "158", "182", "97", "46", "149", "116", "203", "210", "190", "49", "158", "54", "266", "229", "194", "210", "6", "119", "149", "97", "169", "229", "260", "94", "158", "147", "256", "46", "209", "149", "116", "182", "229", "190", "34", "158", "215", "210", "169", "266", "229", "260", "54", "210", "119", "203", "149", "97", "158", "256", "6", "149", "34", "194", "210", "215", "49", "158", "266", "229", "94", "182", "46", "209", "147", "210", "190"); + private final CardRun rare = new CardRun(false, "1", "2", "8", "12", "15", "16", "31", "43", "53", "58", "59", "63", "75", "81", "96", "99", "107", "109", "111", "122", "139", "141", "145", "148", "151", "155", "157", "163", "179", "181", "200", "202", "212", "227", "228", "230", "233", "234", "241", "242", "244", "246", "249", "252", "253", "264", "270", "271", "272", "274", "276", "278"); + private final CardRun rareJournal = new CardRun(false, "265", "265+a", "265+b", "265+c", "265+d", "265+e"); + private final CardRun mythic = new CardRun(false, "13", "65", "69", "101", "124", "131", "162", "192", "226", "235", "245", "247", "248", "250", "251"); + private final CardRun rareDFC = new CardRun(false, "21", "92", "108", "159", "225", "281", "21", "92", "108", "159", "225", "281", "5", "88", "243"); + private final CardRun land = new CardRun(false, "283", "284", "285", "286", "287", "288", "289", "290", "291", "292", "293", "294", "295", "296", "297"); + + private final BoosterStructure AAABC1C1C1C1C1 = new BoosterStructure( + commonA, commonA, commonA, + commonB, + commonC1, commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AAABBC1C1C1C1 = new BoosterStructure( + commonA, commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AAABBC2C2C2C2 = new BoosterStructure( + commonA, commonA, commonA, + commonB, commonB, + commonC2, commonC2, commonC2, commonC2 + ); + private final BoosterStructure AAABBBC2C2C2 = new BoosterStructure( + commonA, commonA, commonA, + commonB, commonB, commonB, + commonC2, commonC2, commonC2 + ); + private final BoosterStructure AABBC2C2C2C2D = new BoosterStructure( + commonA, commonA, + commonB, commonB, + commonC2, commonC2, commonC2, commonC2, + rareDFC + ); + private final BoosterStructure AABBBC2C2C2D = new BoosterStructure( + commonA, commonA, + commonB, commonB, commonB, + commonC2, commonC2, commonC2, + rareDFC + ); + private final BoosterStructure AAB = new BoosterStructure(uncommonA, uncommonA, uncommonB); + private final BoosterStructure ABB = new BoosterStructure(uncommonA, uncommonB, uncommonB); + private final BoosterStructure R1 = new BoosterStructure(rare); + private final BoosterStructure RJ = new BoosterStructure(rareJournal); + private final BoosterStructure M1 = new BoosterStructure(mythic); + private final BoosterStructure D1 = new BoosterStructure(commonDFC); + private final BoosterStructure L1 = new BoosterStructure(land); + + // In order for equal numbers of each common to exist, the average booster must contain: + // 2.905 A commons (639 / 220) (rounded to 46/16) + // 1.936 B commons (426 / 220) (rounded to 31/16) + // 2.420 C1 commons (1065 / 440) (rounded to 39/16) + // 1.614 C2 commons (710 / 440) (rounded to 26/16) + private final RarityConfiguration commonRuns = new RarityConfiguration( + AAABC1C1C1C1C1, + AAABC1C1C1C1C1, + AAABC1C1C1C1C1, + AAABC1C1C1C1C1, + AAABC1C1C1C1C1, + AAABC1C1C1C1C1, + AAABC1C1C1C1C1, + AAABBC1C1C1C1, + + AAABBC2C2C2C2, + AAABBBC2C2C2, + AAABBBC2C2C2, + AAABBBC2C2C2, + AAABBBC2C2C2, + AAABBBC2C2C2, + + AABBC2C2C2C2D, + AABBBC2C2C2D + ); + private final RarityConfiguration uncommonRuns = new RarityConfiguration(AAB, ABB); + private final RarityConfiguration rareRuns = new RarityConfiguration( + R1, R1, R1, R1, R1, R1, R1, R1, R1, R1, + R1, R1, R1, R1, R1, R1, R1, R1, R1, R1, + R1, R1, R1, R1, R1, R1, R1, R1, R1, R1, + R1, R1, R1, R1, R1, R1, R1, R1, R1, R1, + R1, R1, R1, R1, R1, R1, R1, R1, R1, R1, + R1, R1, R1, R1, R1, R1, R1, R1, R1, R1, + R1, R1, R1, R1, R1, R1, R1, R1, R1, R1, + R1, R1, R1, R1, R1, R1, R1, R1, R1, R1, + R1, R1, R1, R1, R1, R1, R1, R1, R1, R1, + R1, R1, R1, R1, R1, R1, R1, R1, R1, R1, + R1, R1, R1, R1, RJ, RJ, + M1, M1, M1, M1, M1, M1, M1, M1, M1, M1, + M1, M1, M1, M1, M1 + ); + private final RarityConfiguration dfcRuns = new RarityConfiguration(D1); + private final RarityConfiguration landRuns = new RarityConfiguration(L1); + + @Override + public List makeBooster() { + List booster = new ArrayList<>(); + booster.addAll(commonRuns.getNext().makeRun()); + booster.addAll(uncommonRuns.getNext().makeRun()); + booster.addAll(rareRuns.getNext().makeRun()); + booster.addAll(dfcRuns.getNext().makeRun()); + booster.addAll(landRuns.getNext().makeRun()); + return booster; + } } diff --git a/Mage.Sets/src/mage/sets/Starter1999.java b/Mage.Sets/src/mage/sets/Starter1999.java index f9146ef2ec6..15748eb1cbf 100644 --- a/Mage.Sets/src/mage/sets/Starter1999.java +++ b/Mage.Sets/src/mage/sets/Starter1999.java @@ -85,7 +85,7 @@ public final class Starter1999 extends ExpansionSet { cards.add(new SetCardInfo("Goblin Commando", 100, Rarity.UNCOMMON, mage.cards.g.GoblinCommando.class)); cards.add(new SetCardInfo("Goblin General", 101, Rarity.UNCOMMON, mage.cards.g.GoblinGeneral.class)); cards.add(new SetCardInfo("Goblin Glider", 102, Rarity.UNCOMMON, mage.cards.g.GoblinGlider.class)); - cards.add(new SetCardInfo("Goblin Hero", 103, Rarity.COMMON, mage.cards.g.GoblinHero.class)); + cards.add(new SetCardInfo("Goblin Hero", 103, Rarity.RARE, mage.cards.g.GoblinHero.class)); cards.add(new SetCardInfo("Goblin Lore", 104, Rarity.UNCOMMON, mage.cards.g.GoblinLore.class)); cards.add(new SetCardInfo("Goblin Mountaineer", 105, Rarity.COMMON, mage.cards.g.GoblinMountaineer.class)); cards.add(new SetCardInfo("Goblin Settler", 106, Rarity.UNCOMMON, mage.cards.g.GoblinSettler.class)); @@ -127,6 +127,7 @@ public final class Starter1999 extends ExpansionSet { cards.add(new SetCardInfo("Norwood Archers", 137, Rarity.COMMON, mage.cards.n.NorwoodArchers.class)); cards.add(new SetCardInfo("Norwood Ranger", 138, Rarity.COMMON, mage.cards.n.NorwoodRanger.class)); cards.add(new SetCardInfo("Ogre Warrior", 113, Rarity.COMMON, mage.cards.o.OgreWarrior.class)); + cards.add(new SetCardInfo("Owl Familiar", 43, Rarity.UNCOMMON, mage.cards.o.OwlFamiliar.class)); cards.add(new SetCardInfo("Path of Peace", 21, Rarity.COMMON, mage.cards.p.PathOfPeace.class)); cards.add(new SetCardInfo("Phantom Warrior", 44, Rarity.RARE, mage.cards.p.PhantomWarrior.class)); cards.add(new SetCardInfo("Plains", 154, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); diff --git a/Mage.Sets/src/mage/sets/StreetsOfNewCapenna.java b/Mage.Sets/src/mage/sets/StreetsOfNewCapenna.java new file mode 100644 index 00000000000..0341f0b1474 --- /dev/null +++ b/Mage.Sets/src/mage/sets/StreetsOfNewCapenna.java @@ -0,0 +1,36 @@ +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * @author TheElk801 + */ +public final class StreetsOfNewCapenna extends ExpansionSet { + + private static final StreetsOfNewCapenna instance = new StreetsOfNewCapenna(); + + public static StreetsOfNewCapenna getInstance() { + return instance; + } + + private StreetsOfNewCapenna() { + super("Streets of New Capenna", "SNC", ExpansionSet.buildDate(2022, 4, 29), SetType.EXPANSION); + this.blockName = "Streets of New Capenna"; + this.hasBoosters = true; + this.hasBasicLands = true; + + cards.add(new SetCardInfo("Brokers Ascendancy", 170, Rarity.RARE, mage.cards.b.BrokersAscendancy.class)); + cards.add(new SetCardInfo("Forest", 280, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Island", 274, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Jetmir's Garden", 250, Rarity.RARE, mage.cards.j.JetmirsGarden.class)); + cards.add(new SetCardInfo("Mountain", 278, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Plains", 272, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Raffine's Tower", 254, Rarity.RARE, mage.cards.r.RaffinesTower.class)); + cards.add(new SetCardInfo("Spara's Headquarters", 257, Rarity.RARE, mage.cards.s.SparasHeadquarters.class)); + cards.add(new SetCardInfo("Swamp", 276, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Xander's Lounge", 260, Rarity.RARE, mage.cards.x.XandersLounge.class)); + cards.add(new SetCardInfo("Ziatora's Proving Ground", 261, Rarity.RARE, mage.cards.z.ZiatorasProvingGround.class)); + } +} diff --git a/Mage.Sets/src/mage/sets/StrixhavenSchoolOfMages.java b/Mage.Sets/src/mage/sets/StrixhavenSchoolOfMages.java index d9c1aaa788b..2b1f1d0edc1 100644 --- a/Mage.Sets/src/mage/sets/StrixhavenSchoolOfMages.java +++ b/Mage.Sets/src/mage/sets/StrixhavenSchoolOfMages.java @@ -36,6 +36,7 @@ public final class StrixhavenSchoolOfMages extends ExpansionSet { this.numBoosterCommon = 9; this.numBoosterUncommon = 3; this.numBoosterRare = 1; + this.numBoosterSpecial = 2; this.ratioBoosterMythic = 7.4; this.maxCardNumberInBooster = 275; @@ -424,50 +425,43 @@ public final class StrixhavenSchoolOfMages extends ExpansionSet { } @Override - public List tryBooster() { - List booster = super.tryBooster(); - addArchive(booster); - addLesson(booster); - return booster; - } - - private void addArchive(List booster) { + protected void addSpecialCards(List booster, int number) { + // number is here always 2 // Boosters have one card from STA, odds are 2/3 for uncommon, 4/15 for rare, 1/15 for mythic - final Rarity rarity; - int i = RandomUtil.nextInt(15); - if (i == 14) { + Rarity rarity; + int rarityKey = RandomUtil.nextInt(15); + if (rarityKey == 14) { rarity = Rarity.MYTHIC; - } else if (i >= 10) { + } else if (rarityKey >= 10) { rarity = Rarity.RARE; } else { rarity = Rarity.UNCOMMON; } addToBooster(booster, StrixhavenMysticalArchive.getInstance().getCardsByRarity(rarity)); - } - private void addLesson(List booster) { // Boosters have one Lesson card - final Rarity rarity; - int i = RandomUtil.nextInt(148); - if (i == 0) { + rarityKey = RandomUtil.nextInt(148); + if (rarityKey == 0) { rarity = Rarity.MYTHIC; - } else if (i < 11) { + } else if (rarityKey < 11) { rarity = Rarity.RARE; } else { rarity = Rarity.COMMON; } - List cards = super.getCardsByRarity(rarity); - cards.removeIf(cardInfo -> !cardInfo.getCard().hasSubtype(SubType.LESSON, null)); - addToBooster(booster, cards); + addToBooster(booster, getSpecialCardsByRarity(rarity)); } @Override - public List getCardsByRarity(Rarity rarity) { - List cards = super.getCardsByRarity(rarity); + protected List findSpecialCardsByRarity(Rarity rarity) { + List cardInfos = super.findSpecialCardsByRarity(rarity); if (rarity != Rarity.UNCOMMON) { - cards.removeIf(cardInfo -> cardInfo.getCard().hasSubtype(SubType.LESSON, null)); + cardInfos.addAll(CardRepository.instance.findCards(new CardCriteria() + .setCodes(this.code) + .rarities(rarity) + .subtypes(SubType.LESSON) + .maxCardNumber(maxCardNumberInBooster))); } - return cards; + return cardInfos; } @Override diff --git a/Mage.Sets/src/mage/sets/Theros.java b/Mage.Sets/src/mage/sets/Theros.java index c9ce7d0c80f..d9a39dea171 100644 --- a/Mage.Sets/src/mage/sets/Theros.java +++ b/Mage.Sets/src/mage/sets/Theros.java @@ -1,10 +1,16 @@ - package mage.sets; import mage.cards.ExpansionSet; +import mage.collation.BoosterCollator; +import mage.collation.BoosterStructure; +import mage.collation.CardRun; +import mage.collation.RarityConfiguration; import mage.constants.Rarity; import mage.constants.SetType; +import java.util.ArrayList; +import java.util.List; + /** * * @author LevelX2 @@ -276,4 +282,100 @@ public final class Theros extends ExpansionSet { cards.add(new SetCardInfo("Xenagos, the Reveler", 209, Rarity.MYTHIC, mage.cards.x.XenagosTheReveler.class)); cards.add(new SetCardInfo("Yoked Ox", 37, Rarity.COMMON, mage.cards.y.YokedOx.class)); } + + @Override + public BoosterCollator createCollator() { + return new TherosCollator(); + } +} + +// Booster collation info from https://www.lethe.xyz/mtg/collation/ths.html +// Using USA collation for common/uncommon, rare collation inferred from other sets +class TherosCollator implements BoosterCollator { + private final CardRun commonA = new CardRun(true, "88", "2", "167", "64", "165", "94", "130", "5", "55", "109", "127", "171", "8", "217", "59", "118", "161", "16", "165", "96", "119", "164", "212", "10", "106", "64", "125", "161", "5", "171", "169", "130", "56", "1", "164", "109", "119", "57", "89", "8", "106", "212", "129", "55", "2", "95", "169", "216", "118", "10", "88", "56", "167", "94", "129", "16", "89", "59", "216", "125", "217", "1", "95", "57", "96", "127"); + private final CardRun commonB = new CardRun(true, "178", "80", "72", "29", "221", "115", "176", "84", "27", "74", "218", "179", "114", "28", "70", "81", "177", "87", "73", "29", "117", "176", "30", "84", "74", "221", "178", "115", "29", "70", "73", "179", "80", "111", "30", "72", "87", "178", "117", "218", "27", "74", "81", "115", "177", "28", "84", "73", "111", "176", "218", "27", "72", "117", "114", "80", "177", "30", "70", "221", "111", "81", "179", "28", "87", "114"); + private final CardRun commonC1 = new CardRun(true, "155", "104", "21", "52", "100", "137", "158", "103", "19", "48", "133", "24", "156", "148", "45", "140", "22", "105", "50", "174", "139", "23", "158", "48", "67", "21", "134", "157", "103", "50", "155", "19", "140", "104", "44", "100", "156", "23", "134", "101", "67", "52", "148", "157", "139", "24", "105", "44", "137", "174", "101", "22", "229", "45", "133"); + private final CardRun commonC2 = new CardRun(true, "229", "39", "77", "36", "146", "183", "38", "145", "37", "182", "147", "41", "181", "78", "34", "146", "39", "31", "79", "36", "141", "43", "183", "77", "182", "145", "78", "41", "38", "34", "147", "31", "181", "79", "43", "39", "141", "183", "37", "78", "38", "36", "147", "146", "181", "77", "43", "31", "145", "34", "37", "79", "41", "182", "141"); + private final CardRun uncommonA = new CardRun(true, "97", "194", "25", "154", "142", "61", "108", "189", "35", "160", "136", "99", "195", "47", "213", "25", "159", "93", "189", "49", "142", "15", "192", "151", "97", "51", "211", "35", "185", "131", "58", "163", "99", "194", "18", "61", "128", "211", "154", "93", "190", "47", "132", "15", "163", "213", "185", "102", "136", "159", "192", "26", "131", "108", "51", "190", "151", "18", "128", "102", "49", "195", "160", "26", "132", "58"); + private final CardRun uncommonB = new CardRun(true, "7", "123", "222", "71", "92", "168", "13", "116", "203", "202", "83", "62", "11", "121", "219", "86", "69", "184", "197", "6", "123", "204", "71", "202", "82", "7", "121", "170", "68", "215", "86", "197", "168", "6", "222", "92", "113", "62", "170", "219", "13", "83", "175", "68", "116", "204", "11", "215", "203", "69", "113", "184", "82", "175"); + private final CardRun rare = new CardRun(false, "3", "4", "12", "14", "20", "32", "33", "40", "42", "46", "54", "60", "63", "65", "75", "76", "90", "98", "107", "110", "112", "120", "122", "124", "126", "138", "144", "149", "150", "152", "153", "162", "173", "180", "186", "191", "193", "198", "199", "200", "201", "205", "206", "207", "210", "214", "220", "223", "224", "225", "226", "227", "228", "3", "4", "12", "14", "20", "32", "33", "40", "42", "46", "54", "60", "63", "65", "75", "76", "90", "98", "107", "110", "112", "120", "122", "124", "126", "138", "144", "149", "150", "152", "153", "162", "173", "180", "186", "191", "193", "198", "199", "200", "201", "205", "206", "207", "210", "214", "220", "223", "224", "225", "226", "227", "228", "9", "17", "53", "66", "85", "91", "135", "143", "166", "172", "187", "188", "196", "208", "209"); + private final CardRun land = new CardRun(false, "230", "231", "232", "233", "234", "235", "236", "237", "238", "239", "240", "241", "242", "243", "244", "245", "246", "247", "248", "249"); + + private final BoosterStructure AABBC1C1C1C1C1C1 = new BoosterStructure( + commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AAABBC1C1C1C1C1 = new BoosterStructure( + commonA, commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AAAABBC2C2C2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, + commonC2, commonC2, commonC2, commonC2 + ); + private final BoosterStructure AAAABBBBC2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, commonB, commonB, + commonC2, commonC2 + ); + private final BoosterStructure AAB = new BoosterStructure(uncommonA, uncommonA, uncommonB); + private final BoosterStructure ABB = new BoosterStructure(uncommonA, uncommonB, uncommonB); + private final BoosterStructure R1 = new BoosterStructure(rare); + private final BoosterStructure L1 = new BoosterStructure(land); + + // In order for equal numbers of each common to exist, the average booster must contain: + // 3.27 A commons (36 / 11) + // 2.18 B commons (24 / 11) + // 2.73 C1 commons (30 / 11, or 60 / 11 in each C1 booster) + // 1.82 C2 commons (20 / 11, or 40 / 11 in each C2 booster) + // These numbers are the same for all sets with 101 commons in A/B/C1/C2 print runs + // and with 10 common slots per booster + private final RarityConfiguration commonRuns = new RarityConfiguration( + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBBBC2C2, + AAAABBBBC2C2 + ); + // In order for equal numbers of each uncommon to exist, the average booster must contain: + // 1.65 A uncommons (33 / 20) + // 1.35 B uncommons (27 / 20) + // These numbers are the same for all sets with 60 uncommons in asymmetrical A/B print runs + private final RarityConfiguration uncommonRuns = new RarityConfiguration( + AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, + ABB, ABB, ABB, ABB, ABB, ABB, ABB + ); + private final RarityConfiguration rareRuns = new RarityConfiguration(R1); + private final RarityConfiguration landRuns = new RarityConfiguration(L1); + + @Override + public List makeBooster() { + List booster = new ArrayList<>(); + booster.addAll(commonRuns.getNext().makeRun()); + booster.addAll(uncommonRuns.getNext().makeRun()); + booster.addAll(rareRuns.getNext().makeRun()); + booster.addAll(landRuns.getNext().makeRun()); + return booster; + } } diff --git a/Mage.Sets/src/mage/sets/TherosBeyondDeath.java b/Mage.Sets/src/mage/sets/TherosBeyondDeath.java index fdf322c4a7d..0bd4ff8a964 100644 --- a/Mage.Sets/src/mage/sets/TherosBeyondDeath.java +++ b/Mage.Sets/src/mage/sets/TherosBeyondDeath.java @@ -408,8 +408,12 @@ class TherosBeyondDeathCollator implements BoosterCollator { private final CardRun commonC2 = new CardRun(true, "44", "96", "197", "145", "232", "34", "126", "204", "249", "54", "135", "231", "187", "175", "44", "143", "95", "96", "197", "135", "107", "6", "32", "204", "126", "34", "54", "249", "145", "231", "187", "96", "6", "143", "44", "107", "34", "175", "135", "249", "95", "197", "54", "204", "126", "32", "6", "175", "95", "231", "145", "107", "187", "32", "143"); private final CardRun uncommonA = new CardRun(true, "223", "65", "153", "8", "112", "227", "99", "167", "33", "138", "4", "189", "228", "45", "59", "180", "105", "1", "136", "196", "206", "139", "83", "89", "233", "31", "131", "91", "219", "193", "27", "133", "64", "199", "213", "264", "42", "153", "205", "8", "136", "4", "189", "33", "223", "2", "138", "112", "27", "233", "260", "180", "31", "59", "99", "131", "105", "267", "81", "139", "228", "167", "133", "219", "65", "1", "83", "125", "206", "193", "42", "91", "227", "89", "199", "153", "8", "81", "213", "64", "112", "223", "4", "136", "205", "105", "139", "99", "65", "2", "180", "228", "59", "1", "233", "45", "189", "227", "33", "196", "83", "138", "206", "42", "219", "167", "131", "31", "89", "193", "91", "125", "213", "199", "81", "27", "2", "64", "133", "205"); private final CardRun uncommonB = new CardRun(true, "226", "101", "128", "183", "21", "234", "87", "50", "242", "176", "239", "132", "9", "216", "62", "119", "172", "160", "104", "69", "168", "225", "130", "237", "63", "15", "102", "166", "5", "129", "121", "53", "239", "70", "182", "128", "21", "234", "92", "69", "101", "160", "23", "230", "75", "130", "104", "172", "50", "7", "162", "87", "183", "226", "62", "216", "258", "132", "176", "237", "263", "15", "242", "63", "5", "225", "168", "129", "121", "53", "230", "21", "70", "102", "166", "128", "92", "234", "23", "183", "160", "104", "75", "226", "162", "7", "239", "182", "9", "132", "101", "69", "172", "216", "242", "50", "176", "87", "225", "62", "15", "168", "119", "237", "130", "5", "70", "102", "166", "63", "23", "129", "121", "53", "182", "7", "162", "230", "92", "75"); - private final CardRun rareA = new CardRun(false, "207", "84", "165", "3", "43", "209", "210", "212", "214", "169", "90", "12", "13", "215", "94", "217", "98", "218", "19", "24", "222", "243", "178", "55", "181", "108", "188", "235", "148", "60", "151", "198", "236", "37", "156", "157", "39", "158", "244", "245", "246", "247", "248", "72", "73", "124", "170", "76", "117", "118", "161", "80", "123", "207", "84", "165", "3", "43", "209", "210", "212", "214", "169", "90", "12", "13", "215", "94", "217", "98", "218", "19", "24", "222", "243", "178", "55", "181", "108", "188", "235", "148", "60", "151", "198", "236", "37", "156", "157", "39", "158", "244", "245", "246", "247", "248", "72", "73", "124", "170", "76", "117", "118", "161", "80", "123", "14", "18", "52", "71", "93", "147", "150", "185", "190", "208", "211", "220", "221", "224", "229"); - private final CardRun rareB = new CardRun(false, "207", "84", "165", "3", "43", "209", "210", "212", "214", "169", "90", "12", "13", "215", "94", "217", "98", "218", "19", "24", "222", "243", "178", "55", "181", "108", "188", "235", "148", "60", "151", "198", "236", "37", "156", "157", "39", "158", "244", "245", "246", "247", "248", "72", "73", "124", "170", "76", "117", "118", "161", "80", "123", "207", "84", "165", "3", "43", "209", "210", "212", "214", "169", "90", "12", "13", "215", "94", "217", "98", "218", "19", "24", "222", "243", "178", "55", "181", "108", "188", "235", "148", "60", "151", "198", "236", "37", "156", "157", "39", "158", "244", "245", "246", "247", "248", "72", "73", "124", "170", "76", "117", "118", "161", "80", "123", "255", "259", "52", "261", "262", "147", "265", "266", "190", "256", "257", "268", "221", "224", "229"); + // rares and mythics with no variants that can appear in Draft Bosters + private final CardRun rareNoVariant = new CardRun(false, "3", "12", "13", "19", "24", "37", "39", "43", "55", "60", "72", "73", "76", "80", "84", "90", "94", "98", "108", "117", "118", "123", "124", "148", "151", "156", "157", "158", "161", "165", "169", "170", "178", "181", "188", "198", "207", "209", "210", "212", "214", "215", "217", "218", "222", "235", "236", "243", "244", "245", "246", "247", "248", "3", "12", "13", "19", "24", "37", "39", "43", "55", "60", "72", "73", "76", "80", "84", "90", "94", "98", "108", "117", "118", "123", "124", "148", "151", "156", "157", "158", "161", "165", "169", "170", "178", "181", "188", "198", "207", "209", "210", "212", "214", "215", "217", "218", "222", "235", "236", "243", "244", "245", "246", "247", "248", "52", "147", "190", "221", "224", "229"); + // non-variant versions of mythics with variants + private final CardRun mythicNonVariant = new CardRun(false, "14", "18", "71", "93", "150", "185", "208", "211", "220"); + // variant versions of mythics + private final CardRun mythicVariant = new CardRun(false, "255", "256", "257", "259", "261", "262", "265", "266", "268"); private final CardRun land = new CardRun(false, "250", "251", "252", "253", "254"); private final BoosterStructure AABBC1C1C1C1C1C1 = new BoosterStructure( @@ -439,8 +443,9 @@ class TherosBeyondDeathCollator implements BoosterCollator { ); private final BoosterStructure AAB = new BoosterStructure(uncommonA, uncommonA, uncommonB); private final BoosterStructure ABB = new BoosterStructure(uncommonA, uncommonB, uncommonB); - private final BoosterStructure R1 = new BoosterStructure(rareA); - private final BoosterStructure R2 = new BoosterStructure(rareB); + private final BoosterStructure R1 = new BoosterStructure(rareNoVariant); + private final BoosterStructure R2 = new BoosterStructure(mythicNonVariant); + private final BoosterStructure R3 = new BoosterStructure(mythicVariant); private final BoosterStructure L1 = new BoosterStructure(land); // In order for equal numbers of each common to exist, the average booster must contain: @@ -476,7 +481,12 @@ class TherosBeyondDeathCollator implements BoosterCollator { AAAABBBBC2C2 ); private final RarityConfiguration uncommonRuns = new RarityConfiguration(AAB, ABB); - private final RarityConfiguration rareRuns = new RarityConfiguration(R1, R1, R2); + private final RarityConfiguration rareRuns = new RarityConfiguration( + R1, R1, R1, R1, R1, R1, R1, R1, R1, R1, + R1, R1, R1, R1, R1, R1, R1, R1, R1, R1, + R1, R1, R1, R1, R1, R1, R1, R1, R1, R1, + R1, R1, R1, R1, R1, R1, R1, R2, R2, R3 + ); private final RarityConfiguration landRuns = new RarityConfiguration(L1); @Override diff --git a/Mage.Sets/src/mage/sets/ThroneOfEldraine.java b/Mage.Sets/src/mage/sets/ThroneOfEldraine.java index 22c630f7584..c111e7bbfa9 100644 --- a/Mage.Sets/src/mage/sets/ThroneOfEldraine.java +++ b/Mage.Sets/src/mage/sets/ThroneOfEldraine.java @@ -1,9 +1,16 @@ package mage.sets; import mage.cards.ExpansionSet; +import mage.collation.BoosterCollator; +import mage.collation.BoosterStructure; +import mage.collation.CardRun; +import mage.collation.RarityConfiguration; import mage.constants.Rarity; import mage.constants.SetType; +import java.util.ArrayList; +import java.util.List; + /** * @author TheElk801 */ @@ -424,4 +431,104 @@ public final class ThroneOfEldraine extends ExpansionSet { cards.add(new SetCardInfo("Yorvo, Lord of Garenbrig", 376, Rarity.RARE, mage.cards.y.YorvoLordOfGarenbrig.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Youthful Knight", 37, Rarity.COMMON, mage.cards.y.YouthfulKnight.class)); } + + @Override + public BoosterCollator createCollator() { + return new ThroneOfEldraineCollator(); + } +} + +// Booster collation info from https://www.lethe.xyz/mtg/collation/eld.html +// Using USA collation for common/uncommon, rare collation inferred from other sets +class ThroneOfEldraineCollator implements BoosterCollator { + private final CardRun commonA = new CardRun(true, "141", "23", "59", "113", "29", "62", "131", "37", "61", "132", "13", "68", "119", "6", "57", "112", "4", "56", "146", "31", "64", "137", "23", "42", "148", "24", "52", "134", "21", "74", "141", "11", "62", "121", "29", "68", "132", "34", "73", "113", "13", "57", "131", "37", "59", "146", "6", "64", "112", "4", "56", "148", "11", "61", "119", "31", "52", "134", "21", "42", "137", "34", "74", "121", "24", "73"); + private final CardRun commonB = new CardRun(true, "96", "175", "108", "162", "89", "174", "92", "176", "88", "150", "102", "180", "105", "156", "91", "184", "87", "173", "94", "153", "108", "176", "95", "162", "96", "174", "92", "175", "88", "158", "105", "180", "89", "156", "87", "150", "94", "184", "102", "173", "96", "153", "92", "162", "91", "158", "95", "175", "88", "176", "89", "174", "108", "184", "105", "156", "87", "180", "102", "173", "91", "150", "94", "158", "95", "153"); + private final CardRun commonC1 = new CardRun(true, "228", "41", "12", "217", "219", "93", "249", "166", "30", "140", "86", "40", "225", "33", "126", "229", "77", "41", "157", "243", "236", "116", "220", "178", "72", "246", "225", "103", "126", "12", "249", "166", "221", "140", "7", "40", "228", "243", "93", "219", "33", "220", "246", "157", "86", "229", "116", "217", "178", "77", "236", "7", "72", "221", "103"); + private final CardRun commonC2 = new CardRun(true, "5", "224", "170", "114", "76", "231", "70", "19", "136", "247", "53", "154", "227", "109", "245", "65", "183", "139", "224", "5", "19", "70", "231", "76", "114", "5", "247", "170", "227", "136", "245", "109", "114", "154", "224", "139", "183", "76", "30", "65", "231", "136", "53", "170", "247", "70", "19", "109", "227", "139", "53", "154", "245", "65", "183"); + private final CardRun uncommonA = new CardRun(true, "209", "216", "202", "104", "214", "38", "223", "25", "201", "122", "159", "204", "15", "297", "118", "212", "230", "58", "218", "207", "85", "28", "205", "232", "283", "106", "188", "168", "145", "210", "163", "142", "196", "226", "25", "38", "209", "90", "60", "234", "204", "155", "223", "202", "85", "214", "248", "188", "216", "104", "292", "208", "168", "118", "201", "232", "15", "159", "212", "90", "58", "230", "207", "163", "106", "196", "278", "49", "10", "205", "218", "60", "145", "210", "226", "142", "204", "216", "25", "223", "209", "280", "104", "234", "214", "155", "248", "208", "15", "201", "159", "202", "230", "118", "85", "302", "168", "106", "207", "232", "122", "58", "205", "49", "10", "218", "210", "226", "248", "188", "28", "163", "60", "196", "234", "142", "286", "208", "145", "10"); + private final CardRun uncommonB = new CardRun(true, "45", "213", "123", "149", "83", "237", "2", "78", "129", "194", "63", "32", "151", "193", "81", "67", "130", "3", "211", "69", "22", "107", "143", "50", "206", "179", "27", "222", "99", "47", "200", "177", "192", "80", "35", "117", "149", "237", "215", "45", "83", "32", "130", "193", "167", "50", "107", "2", "123", "194", "69", "3", "135", "164", "211", "47", "81", "213", "22", "222", "143", "63", "206", "179", "27", "78", "200", "45", "129", "151", "288", "35", "192", "177", "117", "67", "3", "167", "215", "80", "50", "237", "135", "164", "83", "193", "2", "211", "130", "194", "179", "107", "63", "123", "213", "177", "32", "69", "22", "206", "78", "129", "295", "222", "67", "81", "151", "215", "143", "27", "135", "200", "99", "167", "117", "47", "192", "35", "80", "164"); + private final CardRun rare = new CardRun(false, "1", "8", "14", "16", "18", "20", "36", "43", "44", "46", "48", "54", "55", "66", "71", "75", "79", "84", "97", "98", "100", "110", "111", "115", "124", "125", "127", "128", "133", "144", "147", "152", "160", "165", "169", "172", "181", "182", "185", "186", "187", "189", "190", "195", "203", "233", "235", "238", "239", "240", "241", "242", "244", "1", "8", "14", "16", "18", "20", "36", "43", "44", "46", "48", "54", "55", "66", "71", "75", "79", "84", "97", "98", "100", "110", "111", "115", "124", "125", "127", "128", "133", "144", "147", "152", "160", "165", "169", "172", "181", "182", "185", "186", "187", "189", "190", "195", "203", "233", "235", "238", "239", "240", "241", "242", "244", "9", "17", "26", "39", "51", "82", "101", "120", "138", "161", "171", "191", "197", "198", "199"); + private final CardRun rareVariant = new CardRun(false, "275", "282", "287", "291", "299", "275", "282", "287", "291", "299", "270", "271", "272", "277", "281"); + private final CardRun land = new CardRun(false, "250", "251", "252", "253", "254", "255", "256", "257", "258", "259", "260", "261", "262", "263", "264", "265", "266", "267", "268", "269"); + + private final BoosterStructure AABBC1C1C1C1C1C1 = new BoosterStructure( + commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AAABBC1C1C1C1C1 = new BoosterStructure( + commonA, commonA, commonA, + commonB, commonB, + commonC1, commonC1, commonC1, commonC1, commonC1 + ); + private final BoosterStructure AAAABBC2C2C2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, + commonC2, commonC2, commonC2, commonC2 + ); + private final BoosterStructure AAAABBBC2C2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, commonB, + commonC2, commonC2, commonC2 + ); + private final BoosterStructure AAAABBBBC2C2 = new BoosterStructure( + commonA, commonA, commonA, commonA, + commonB, commonB, commonB, commonB, + commonC2, commonC2 + ); + private final BoosterStructure AAB = new BoosterStructure(uncommonA, uncommonA, uncommonB); + private final BoosterStructure ABB = new BoosterStructure(uncommonA, uncommonB, uncommonB); + private final BoosterStructure R1 = new BoosterStructure(rare); + private final BoosterStructure R2 = new BoosterStructure(rareVariant); + private final BoosterStructure L1 = new BoosterStructure(land); + + // In order for equal numbers of each common to exist, the average booster must contain: + // 3.27 A commons (36 / 11) + // 2.18 B commons (24 / 11) + // 2.73 C1 commons (30 / 11, or 60 / 11 in each C1 booster) + // 1.82 C2 commons (20 / 11, or 40 / 11 in each C2 booster) + // These numbers are the same for all sets with 101 commons in A/B/C1/C2 print runs + // and with 10 common slots per booster + private final RarityConfiguration commonRuns = new RarityConfiguration( + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AABBC1C1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + AAABBC1C1C1C1C1, + + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBC2C2C2C2, + AAAABBBC2C2C2, + AAAABBBC2C2C2, + AAAABBBBC2C2 + ); + private final RarityConfiguration uncommonRuns = new RarityConfiguration(AAB, ABB); + private final RarityConfiguration rareRuns = new RarityConfiguration( + R1, R1, R1, R1, R1, R1, R1, R1, R1, + R1, R1, R1, R1, R1, R1, R1, R1, R1, + R1, R1, R1, R1, R1, R1, R1, R1, R2 + ); + private final RarityConfiguration landRuns = new RarityConfiguration(L1); + + @Override + public List makeBooster() { + List booster = new ArrayList<>(); + booster.addAll(commonRuns.getNext().makeRun()); + booster.addAll(uncommonRuns.getNext().makeRun()); + booster.addAll(rareRuns.getNext().makeRun()); + booster.addAll(landRuns.getNext().makeRun()); + return booster; + } } diff --git a/Mage.Sets/src/mage/sets/TimeSpiral.java b/Mage.Sets/src/mage/sets/TimeSpiral.java index 1e80b06faad..94047a0d74f 100644 --- a/Mage.Sets/src/mage/sets/TimeSpiral.java +++ b/Mage.Sets/src/mage/sets/TimeSpiral.java @@ -1,8 +1,8 @@ package mage.sets; -import mage.cards.Card; import mage.cards.ExpansionSet; import mage.cards.repository.CardCriteria; +import mage.cards.repository.CardInfo; import mage.cards.repository.CardRepository; import mage.constants.Rarity; import mage.constants.SetType; @@ -25,7 +25,9 @@ public final class TimeSpiral extends ExpansionSet { this.numBoosterCommon = 10; this.numBoosterUncommon = 3; this.numBoosterRare = 1; + this.numBoosterSpecial = 1; this.ratioBoosterMythic = 0; + cards.add(new SetCardInfo("Academy Ruins", 269, Rarity.RARE, mage.cards.a.AcademyRuins.class)); cards.add(new SetCardInfo("Aether Web", 189, Rarity.COMMON, mage.cards.a.AetherWeb.class)); cards.add(new SetCardInfo("Aetherflame Wall", 142, Rarity.COMMON, mage.cards.a.AetherflameWall.class)); @@ -330,11 +332,11 @@ public final class TimeSpiral extends ExpansionSet { } @Override - public List createBooster() { - List booster = super.createBooster(); - CardCriteria criteria = new CardCriteria(); - criteria.rarities(Rarity.SPECIAL).setCodes("TSB"); - addToBooster(booster, CardRepository.instance.findCards(criteria)); - return booster; + protected List findCardsByRarity(Rarity rarity) { + List cardInfos = super.findCardsByRarity(rarity); + if (rarity == Rarity.SPECIAL) { + cardInfos.addAll(CardRepository.instance.findCards(new CardCriteria().setCodes("TSB"))); + } + return cardInfos; } } diff --git a/Mage.Sets/src/mage/sets/TimeSpiralRemastered.java b/Mage.Sets/src/mage/sets/TimeSpiralRemastered.java index 431c3b07311..5fcd4f90407 100644 --- a/Mage.Sets/src/mage/sets/TimeSpiralRemastered.java +++ b/Mage.Sets/src/mage/sets/TimeSpiralRemastered.java @@ -1,9 +1,6 @@ package mage.sets; import mage.cards.ExpansionSet; -import mage.cards.repository.CardCriteria; -import mage.cards.repository.CardInfo; -import mage.cards.repository.CardRepository; import mage.collation.BoosterCollator; import mage.collation.BoosterStructure; import mage.collation.CardRun; @@ -25,8 +22,6 @@ public class TimeSpiralRemastered extends ExpansionSet { return instance; } - private final List savedSpecialBonus = new ArrayList<>(); - private TimeSpiralRemastered() { super("Time Spiral Remastered", "TSR", ExpansionSet.buildDate(2021, 3, 19), SetType.SUPPLEMENTAL); this.hasBoosters = true; @@ -451,17 +446,6 @@ public class TimeSpiralRemastered extends ExpansionSet { cards.add(new SetCardInfo("Zulaport Cutthroat", 337, Rarity.SPECIAL, mage.cards.z.ZulaportCutthroat.class)); } - @Override - public List getSpecialBonus() { - if (savedSpecialBonus.isEmpty()) { - savedSpecialBonus.addAll(CardRepository.instance.findCards(new CardCriteria().setCodes(this.code))); - savedSpecialBonus.removeIf(cardInfo -> cardInfo.getCardNumberAsInt() > 410); - savedSpecialBonus.removeIf(cardInfo -> cardInfo.getCardNumberAsInt() <= 289); - } - - return new ArrayList<>(savedSpecialBonus); - } - @Override public BoosterCollator createCollator() { return new TimeSpiralRemasteredCollator(); diff --git a/Mage.Sets/src/mage/sets/TimeSpiralTimeshifted.java b/Mage.Sets/src/mage/sets/TimeSpiralTimeshifted.java index 350e324ddc7..ed1e4c05655 100644 --- a/Mage.Sets/src/mage/sets/TimeSpiralTimeshifted.java +++ b/Mage.Sets/src/mage/sets/TimeSpiralTimeshifted.java @@ -24,31 +24,31 @@ public final class TimeSpiralTimeshifted extends ExpansionSet { cards.add(new SetCardInfo("Akroma, Angel of Wrath", 1, Rarity.SPECIAL, mage.cards.a.AkromaAngelOfWrath.class)); cards.add(new SetCardInfo("Arena", 117, Rarity.SPECIAL, mage.cards.a.Arena.class)); cards.add(new SetCardInfo("Assault // Battery", 106, Rarity.SPECIAL, mage.cards.a.AssaultBattery.class)); - cards.add(new SetCardInfo("Auratog", 2, Rarity.COMMON, mage.cards.a.Auratog.class)); - cards.add(new SetCardInfo("Avalanche Riders", 55, Rarity.COMMON, mage.cards.a.AvalancheRiders.class)); + cards.add(new SetCardInfo("Auratog", 2, Rarity.SPECIAL, mage.cards.a.Auratog.class)); + cards.add(new SetCardInfo("Avalanche Riders", 55, Rarity.SPECIAL, mage.cards.a.AvalancheRiders.class)); cards.add(new SetCardInfo("Avatar of Woe", 37, Rarity.SPECIAL, mage.cards.a.AvatarOfWoe.class)); cards.add(new SetCardInfo("Avoid Fate", 73, Rarity.SPECIAL, mage.cards.a.AvoidFate.class)); - cards.add(new SetCardInfo("Bad Moon", 38, Rarity.RARE, mage.cards.b.BadMoon.class)); + cards.add(new SetCardInfo("Bad Moon", 38, Rarity.SPECIAL, mage.cards.b.BadMoon.class)); cards.add(new SetCardInfo("Browbeat", 56, Rarity.SPECIAL, mage.cards.b.Browbeat.class)); - cards.add(new SetCardInfo("Call of the Herd", 74, Rarity.COMMON, mage.cards.c.CallOfTheHerd.class)); + cards.add(new SetCardInfo("Call of the Herd", 74, Rarity.SPECIAL, mage.cards.c.CallOfTheHerd.class)); cards.add(new SetCardInfo("Celestial Dawn", 3, Rarity.SPECIAL, mage.cards.c.CelestialDawn.class)); - cards.add(new SetCardInfo("Claws of Gix", 107, Rarity.COMMON, mage.cards.c.ClawsOfGix.class)); - cards.add(new SetCardInfo("Coalition Victory", 91, Rarity.RARE, mage.cards.c.CoalitionVictory.class)); + cards.add(new SetCardInfo("Claws of Gix", 107, Rarity.SPECIAL, mage.cards.c.ClawsOfGix.class)); + cards.add(new SetCardInfo("Coalition Victory", 91, Rarity.SPECIAL, mage.cards.c.CoalitionVictory.class)); cards.add(new SetCardInfo("Cockatrice", 75, Rarity.SPECIAL, mage.cards.c.Cockatrice.class)); cards.add(new SetCardInfo("Consecrate Land", 4, Rarity.SPECIAL, mage.cards.c.ConsecrateLand.class)); cards.add(new SetCardInfo("Conspiracy", 39, Rarity.SPECIAL, mage.cards.c.Conspiracy.class)); cards.add(new SetCardInfo("Craw Giant", 76, Rarity.SPECIAL, mage.cards.c.CrawGiant.class)); cards.add(new SetCardInfo("Dandan", 19, Rarity.SPECIAL, mage.cards.d.Dandan.class)); - cards.add(new SetCardInfo("Darkness", 40, Rarity.COMMON, mage.cards.d.Darkness.class)); - cards.add(new SetCardInfo("Dauthi Slayer", 41, Rarity.COMMON, mage.cards.d.DauthiSlayer.class)); + cards.add(new SetCardInfo("Darkness", 40, Rarity.SPECIAL, mage.cards.d.Darkness.class)); + cards.add(new SetCardInfo("Dauthi Slayer", 41, Rarity.SPECIAL, mage.cards.d.DauthiSlayer.class)); cards.add(new SetCardInfo("Defiant Vanguard", 5, Rarity.SPECIAL, mage.cards.d.DefiantVanguard.class)); cards.add(new SetCardInfo("Desert", 118, Rarity.SPECIAL, mage.cards.d.Desert.class)); cards.add(new SetCardInfo("Desolation Giant", 57, Rarity.SPECIAL, mage.cards.d.DesolationGiant.class)); - cards.add(new SetCardInfo("Disenchant", 6, Rarity.COMMON, mage.cards.d.Disenchant.class)); + cards.add(new SetCardInfo("Disenchant", 6, Rarity.SPECIAL, mage.cards.d.Disenchant.class)); cards.add(new SetCardInfo("Disintegrate", 58, Rarity.SPECIAL, mage.cards.d.Disintegrate.class)); cards.add(new SetCardInfo("Dodecapod", 108, Rarity.SPECIAL, mage.cards.d.Dodecapod.class)); cards.add(new SetCardInfo("Dragonstorm", 60, Rarity.SPECIAL, mage.cards.d.Dragonstorm.class)); - cards.add(new SetCardInfo("Dragon Whelp", 59, Rarity.COMMON, mage.cards.d.DragonWhelp.class)); + cards.add(new SetCardInfo("Dragon Whelp", 59, Rarity.SPECIAL, mage.cards.d.DragonWhelp.class)); cards.add(new SetCardInfo("Enduring Renewal", 7, Rarity.SPECIAL, mage.cards.e.EnduringRenewal.class)); cards.add(new SetCardInfo("Eron the Relentless", 61, Rarity.SPECIAL, mage.cards.e.EronTheRelentless.class)); cards.add(new SetCardInfo("Essence Sliver", 8, Rarity.SPECIAL, mage.cards.e.EssenceSliver.class)); @@ -58,43 +58,43 @@ public final class TimeSpiralTimeshifted extends ExpansionSet { cards.add(new SetCardInfo("Fiery Justice", 92, Rarity.SPECIAL, mage.cards.f.FieryJustice.class)); cards.add(new SetCardInfo("Fiery Temper", 62, Rarity.SPECIAL, mage.cards.f.FieryTemper.class)); cards.add(new SetCardInfo("Fire Whip", 63, Rarity.SPECIAL, mage.cards.f.FireWhip.class)); - cards.add(new SetCardInfo("Flying Men", 20, Rarity.COMMON, mage.cards.f.FlyingMen.class)); - cards.add(new SetCardInfo("Funeral Charm", 44, Rarity.COMMON, mage.cards.f.FuneralCharm.class)); + cards.add(new SetCardInfo("Flying Men", 20, Rarity.SPECIAL, mage.cards.f.FlyingMen.class)); + cards.add(new SetCardInfo("Funeral Charm", 44, Rarity.SPECIAL, mage.cards.f.FuneralCharm.class)); cards.add(new SetCardInfo("Gaea's Blessing", 77, Rarity.SPECIAL, mage.cards.g.GaeasBlessing.class)); cards.add(new SetCardInfo("Gaea's Liege", 78, Rarity.SPECIAL, mage.cards.g.GaeasLiege.class)); - cards.add(new SetCardInfo("Gemstone Mine", 119, Rarity.RARE, mage.cards.g.GemstoneMine.class)); + cards.add(new SetCardInfo("Gemstone Mine", 119, Rarity.SPECIAL, mage.cards.g.GemstoneMine.class)); cards.add(new SetCardInfo("Ghost Ship", 21, Rarity.SPECIAL, mage.cards.g.GhostShip.class)); cards.add(new SetCardInfo("Giant Oyster", 22, Rarity.SPECIAL, mage.cards.g.GiantOyster.class)); - cards.add(new SetCardInfo("Goblin Snowman", 64, Rarity.UNCOMMON, mage.cards.g.GoblinSnowman.class)); + cards.add(new SetCardInfo("Goblin Snowman", 64, Rarity.SPECIAL, mage.cards.g.GoblinSnowman.class)); cards.add(new SetCardInfo("Grinning Totem", 110, Rarity.SPECIAL, mage.cards.g.GrinningTotem.class)); cards.add(new SetCardInfo("Hail Storm", 79, Rarity.SPECIAL, mage.cards.h.HailStorm.class)); cards.add(new SetCardInfo("Honorable Passage", 9, Rarity.SPECIAL, mage.cards.h.HonorablePassage.class)); - cards.add(new SetCardInfo("Hunting Moa", 80, Rarity.COMMON, mage.cards.h.HuntingMoa.class)); + cards.add(new SetCardInfo("Hunting Moa", 80, Rarity.SPECIAL, mage.cards.h.HuntingMoa.class)); cards.add(new SetCardInfo("Icatian Javelineers", 10, Rarity.SPECIAL, mage.cards.i.IcatianJavelineers.class)); - cards.add(new SetCardInfo("Jasmine Boreal", 93, Rarity.COMMON, mage.cards.j.JasmineBoreal.class)); + cards.add(new SetCardInfo("Jasmine Boreal", 93, Rarity.SPECIAL, mage.cards.j.JasmineBoreal.class)); cards.add(new SetCardInfo("Jolrael, Empress of Beasts", 81, Rarity.SPECIAL, mage.cards.j.JolraelEmpressOfBeasts.class)); cards.add(new SetCardInfo("Kobold Taskmaster", 65, Rarity.SPECIAL, mage.cards.k.KoboldTaskmaster.class)); cards.add(new SetCardInfo("Krosan Cloudscraper", 82, Rarity.SPECIAL, mage.cards.k.KrosanCloudscraper.class)); - cards.add(new SetCardInfo("Leviathan", 23, Rarity.RARE, mage.cards.l.Leviathan.class)); - cards.add(new SetCardInfo("Lightning Angel", 94, Rarity.COMMON, mage.cards.l.LightningAngel.class)); - cards.add(new SetCardInfo("Lord of Atlantis", 24, Rarity.RARE, mage.cards.l.LordOfAtlantis.class)); + cards.add(new SetCardInfo("Leviathan", 23, Rarity.SPECIAL, mage.cards.l.Leviathan.class)); + cards.add(new SetCardInfo("Lightning Angel", 94, Rarity.SPECIAL, mage.cards.l.LightningAngel.class)); + cards.add(new SetCardInfo("Lord of Atlantis", 24, Rarity.SPECIAL, mage.cards.l.LordOfAtlantis.class)); cards.add(new SetCardInfo("Merfolk Assassin", 25, Rarity.SPECIAL, mage.cards.m.MerfolkAssassin.class)); cards.add(new SetCardInfo("Merieke Ri Berit", 95, Rarity.SPECIAL, mage.cards.m.MeriekeRiBerit.class)); cards.add(new SetCardInfo("Mindless Automaton", 111, Rarity.SPECIAL, mage.cards.m.MindlessAutomaton.class)); cards.add(new SetCardInfo("Mirari", 112, Rarity.SPECIAL, mage.cards.m.Mirari.class)); cards.add(new SetCardInfo("Mistform Ultimus", 26, Rarity.SPECIAL, mage.cards.m.MistformUltimus.class)); - cards.add(new SetCardInfo("Moorish Cavalry", 11, Rarity.COMMON, mage.cards.m.MoorishCavalry.class)); + cards.add(new SetCardInfo("Moorish Cavalry", 11, Rarity.SPECIAL, mage.cards.m.MoorishCavalry.class)); cards.add(new SetCardInfo("Mystic Enforcer", 96, Rarity.SPECIAL, mage.cards.m.MysticEnforcer.class)); - cards.add(new SetCardInfo("Mystic Snake", 97, Rarity.COMMON, mage.cards.m.MysticSnake.class)); + cards.add(new SetCardInfo("Mystic Snake", 97, Rarity.SPECIAL, mage.cards.m.MysticSnake.class)); cards.add(new SetCardInfo("Nicol Bolas", 98, Rarity.SPECIAL, mage.cards.n.NicolBolas.class)); cards.add(new SetCardInfo("Orcish Librarian", 66, Rarity.SPECIAL, mage.cards.o.OrcishLibrarian.class)); cards.add(new SetCardInfo("Orgg", 67, Rarity.SPECIAL, mage.cards.o.Orgg.class)); cards.add(new SetCardInfo("Ovinomancer", 27, Rarity.SPECIAL, mage.cards.o.Ovinomancer.class)); cards.add(new SetCardInfo("Pandemonium", 68, Rarity.SPECIAL, mage.cards.p.Pandemonium.class)); - cards.add(new SetCardInfo("Pendelhaven", 120, Rarity.COMMON, mage.cards.p.Pendelhaven.class)); + cards.add(new SetCardInfo("Pendelhaven", 120, Rarity.SPECIAL, mage.cards.p.Pendelhaven.class)); cards.add(new SetCardInfo("Pirate Ship", 28, Rarity.SPECIAL, mage.cards.p.PirateShip.class)); - cards.add(new SetCardInfo("Prodigal Sorcerer", 29, Rarity.COMMON, mage.cards.p.ProdigalSorcerer.class)); - cards.add(new SetCardInfo("Psionic Blast", 30, Rarity.COMMON, mage.cards.p.PsionicBlast.class)); + cards.add(new SetCardInfo("Prodigal Sorcerer", 29, Rarity.SPECIAL, mage.cards.p.ProdigalSorcerer.class)); + cards.add(new SetCardInfo("Psionic Blast", 30, Rarity.SPECIAL, mage.cards.p.PsionicBlast.class)); cards.add(new SetCardInfo("Resurrection", 12, Rarity.SPECIAL, mage.cards.r.Resurrection.class)); cards.add(new SetCardInfo("Sacred Mesa", 13, Rarity.SPECIAL, mage.cards.s.SacredMesa.class)); cards.add(new SetCardInfo("Safe Haven", 121, Rarity.SPECIAL, mage.cards.s.SafeHaven.class)); @@ -102,29 +102,29 @@ public final class TimeSpiralTimeshifted extends ExpansionSet { cards.add(new SetCardInfo("Sengir Autocrat", 45, Rarity.SPECIAL, mage.cards.s.SengirAutocrat.class)); cards.add(new SetCardInfo("Serrated Arrows", 114, Rarity.SPECIAL, mage.cards.s.SerratedArrows.class)); cards.add(new SetCardInfo("Shadow Guildmage", 46, Rarity.SPECIAL, mage.cards.s.ShadowGuildmage.class)); - cards.add(new SetCardInfo("Shadowmage Infiltrator", 99, Rarity.COMMON, mage.cards.s.ShadowmageInfiltrator.class)); + cards.add(new SetCardInfo("Shadowmage Infiltrator", 99, Rarity.SPECIAL, mage.cards.s.ShadowmageInfiltrator.class)); cards.add(new SetCardInfo("Sindbad", 31, Rarity.SPECIAL, mage.cards.s.Sindbad.class)); - cards.add(new SetCardInfo("Sol'kanar the Swamp King", 100, Rarity.COMMON, mage.cards.s.SolkanarTheSwampKing.class)); - cards.add(new SetCardInfo("Soltari Priest", 14, Rarity.COMMON, mage.cards.s.SoltariPriest.class)); + cards.add(new SetCardInfo("Sol'kanar the Swamp King", 100, Rarity.SPECIAL, mage.cards.s.SolkanarTheSwampKing.class)); + cards.add(new SetCardInfo("Soltari Priest", 14, Rarity.SPECIAL, mage.cards.s.SoltariPriest.class)); cards.add(new SetCardInfo("Soul Collector", 47, Rarity.SPECIAL, mage.cards.s.SoulCollector.class)); - cards.add(new SetCardInfo("Spike Feeder", 84, Rarity.COMMON, mage.cards.s.SpikeFeeder.class)); + cards.add(new SetCardInfo("Spike Feeder", 84, Rarity.SPECIAL, mage.cards.s.SpikeFeeder.class)); cards.add(new SetCardInfo("Spined Sliver", 101, Rarity.SPECIAL, mage.cards.s.SpinedSliver.class)); cards.add(new SetCardInfo("Spitting Slug", 85, Rarity.SPECIAL, mage.cards.s.SpittingSlug.class)); - cards.add(new SetCardInfo("Squire", 15, Rarity.COMMON, mage.cards.s.Squire.class)); + cards.add(new SetCardInfo("Squire", 15, Rarity.SPECIAL, mage.cards.s.Squire.class)); cards.add(new SetCardInfo("Stormbind", 102, Rarity.SPECIAL, mage.cards.s.Stormbind.class)); - cards.add(new SetCardInfo("Stormscape Familiar", 32, Rarity.COMMON, mage.cards.s.StormscapeFamiliar.class)); - cards.add(new SetCardInfo("Stupor", 48, Rarity.COMMON, mage.cards.s.Stupor.class)); - cards.add(new SetCardInfo("Suq'Ata Lancer", 69, Rarity.COMMON, mage.cards.s.SuqAtaLancer.class)); + cards.add(new SetCardInfo("Stormscape Familiar", 32, Rarity.SPECIAL, mage.cards.s.StormscapeFamiliar.class)); + cards.add(new SetCardInfo("Stupor", 48, Rarity.SPECIAL, mage.cards.s.Stupor.class)); + cards.add(new SetCardInfo("Suq'Ata Lancer", 69, Rarity.SPECIAL, mage.cards.s.SuqAtaLancer.class)); cards.add(new SetCardInfo("Swamp Mosquito", 49, Rarity.SPECIAL, mage.cards.s.SwampMosquito.class)); cards.add(new SetCardInfo("Teferi's Moat", 103, Rarity.SPECIAL, mage.cards.t.TeferisMoat.class)); - cards.add(new SetCardInfo("Thallid", 86, Rarity.COMMON, mage.cards.t.Thallid.class)); + cards.add(new SetCardInfo("Thallid", 86, Rarity.SPECIAL, mage.cards.t.Thallid.class)); cards.add(new SetCardInfo("The Rack", 113, Rarity.SPECIAL, mage.cards.t.TheRack.class)); cards.add(new SetCardInfo("Thornscape Battlemage", 87, Rarity.SPECIAL, mage.cards.t.ThornscapeBattlemage.class)); - cards.add(new SetCardInfo("Tormod's Crypt", 115, Rarity.COMMON, mage.cards.t.TormodsCrypt.class)); - cards.add(new SetCardInfo("Tribal Flames", 70, Rarity.COMMON, mage.cards.t.TribalFlames.class)); - cards.add(new SetCardInfo("Twisted Abomination", 50, Rarity.COMMON, mage.cards.t.TwistedAbomination.class)); + cards.add(new SetCardInfo("Tormod's Crypt", 115, Rarity.SPECIAL, mage.cards.t.TormodsCrypt.class)); + cards.add(new SetCardInfo("Tribal Flames", 70, Rarity.SPECIAL, mage.cards.t.TribalFlames.class)); + cards.add(new SetCardInfo("Twisted Abomination", 50, Rarity.SPECIAL, mage.cards.t.TwistedAbomination.class)); cards.add(new SetCardInfo("Uncle Istvan", 51, Rarity.SPECIAL, mage.cards.u.UncleIstvan.class)); - cards.add(new SetCardInfo("Undead Warchief", 52, Rarity.UNCOMMON, mage.cards.u.UndeadWarchief.class)); + cards.add(new SetCardInfo("Undead Warchief", 52, Rarity.SPECIAL, mage.cards.u.UndeadWarchief.class)); cards.add(new SetCardInfo("Undertaker", 53, Rarity.SPECIAL, mage.cards.u.Undertaker.class)); cards.add(new SetCardInfo("Unstable Mutation", 33, Rarity.SPECIAL, mage.cards.u.UnstableMutation.class)); cards.add(new SetCardInfo("Uthden Troll", 71, Rarity.SPECIAL, mage.cards.u.UthdenTroll.class)); @@ -133,14 +133,14 @@ public final class TimeSpiralTimeshifted extends ExpansionSet { cards.add(new SetCardInfo("Vhati il-Dal", 104, Rarity.SPECIAL, mage.cards.v.VhatiIlDal.class)); cards.add(new SetCardInfo("Void", 105, Rarity.SPECIAL, mage.cards.v.Void.class)); cards.add(new SetCardInfo("Voidmage Prodigy", 34, Rarity.SPECIAL, mage.cards.v.VoidmageProdigy.class)); - cards.add(new SetCardInfo("Wall of Roots", 89, Rarity.COMMON, mage.cards.w.WallOfRoots.class)); + cards.add(new SetCardInfo("Wall of Roots", 89, Rarity.SPECIAL, mage.cards.w.WallOfRoots.class)); cards.add(new SetCardInfo("War Barge", 116, Rarity.SPECIAL, mage.cards.w.WarBarge.class)); - cards.add(new SetCardInfo("Whirling Dervish", 90, Rarity.COMMON, mage.cards.w.WhirlingDervish.class)); + cards.add(new SetCardInfo("Whirling Dervish", 90, Rarity.SPECIAL, mage.cards.w.WhirlingDervish.class)); cards.add(new SetCardInfo("Whispers of the Muse", 35, Rarity.SPECIAL, mage.cards.w.WhispersOfTheMuse.class)); cards.add(new SetCardInfo("Wildfire Emissary", 72, Rarity.SPECIAL, mage.cards.w.WildfireEmissary.class)); cards.add(new SetCardInfo("Willbender", 36, Rarity.SPECIAL, mage.cards.w.Willbender.class)); cards.add(new SetCardInfo("Witch Hunter", 17, Rarity.SPECIAL, mage.cards.w.WitchHunter.class)); - cards.add(new SetCardInfo("Withered Wretch", 54, Rarity.RARE, mage.cards.w.WitheredWretch.class)); - cards.add(new SetCardInfo("Zhalfirin Commander", 18, Rarity.COMMON, mage.cards.z.ZhalfirinCommander.class)); + cards.add(new SetCardInfo("Withered Wretch", 54, Rarity.SPECIAL, mage.cards.w.WitheredWretch.class)); + cards.add(new SetCardInfo("Zhalfirin Commander", 18, Rarity.SPECIAL, mage.cards.z.ZhalfirinCommander.class)); } } diff --git a/Mage.Sets/src/mage/sets/Torment.java b/Mage.Sets/src/mage/sets/Torment.java index 6ede6aff0a3..e9eb791d210 100644 --- a/Mage.Sets/src/mage/sets/Torment.java +++ b/Mage.Sets/src/mage/sets/Torment.java @@ -28,6 +28,7 @@ public final class Torment extends ExpansionSet { this.numBoosterUncommon = 3; this.numBoosterRare = 1; this.ratioBoosterMythic = 0; + this.hasUnbalancedColors = true; cards.add(new SetCardInfo("Accelerate", 90, Rarity.COMMON, mage.cards.a.Accelerate.class)); cards.add(new SetCardInfo("Acorn Harvest", 118, Rarity.COMMON, mage.cards.a.AcornHarvest.class)); cards.add(new SetCardInfo("Ambassador Laquatus", 23, Rarity.RARE, mage.cards.a.AmbassadorLaquatus.class)); diff --git a/Mage.Sets/src/mage/sets/Unfinity.java b/Mage.Sets/src/mage/sets/Unfinity.java new file mode 100644 index 00000000000..214d1c67b95 --- /dev/null +++ b/Mage.Sets/src/mage/sets/Unfinity.java @@ -0,0 +1,40 @@ +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * @author TheElk801 + */ +public final class Unfinity extends ExpansionSet { + + private static final Unfinity instance = new Unfinity(); + + public static Unfinity getInstance() { + return instance; + } + + private Unfinity() { + super("Unfinity", "UNF", ExpansionSet.buildDate(2022, 4, 1), SetType.JOKESET); // TODO: some of these cards are legacy legal + this.hasBasicLands = true; + + cards.add(new SetCardInfo("Blood Crypt", 279, Rarity.COMMON, mage.cards.b.BloodCrypt.class)); + cards.add(new SetCardInfo("Breeding Pool", 286, Rarity.RARE, mage.cards.b.BreedingPool.class)); + cards.add(new SetCardInfo("Forest", 239, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Godless Shrine", 282, Rarity.RARE, mage.cards.g.GodlessShrine.class)); + cards.add(new SetCardInfo("Hallowed Fountain", 277, Rarity.RARE, mage.cards.h.HallowedFountain.class)); + cards.add(new SetCardInfo("Island", 236, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 238, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Overgrown Tomb", 284, Rarity.RARE, mage.cards.o.OvergrownTomb.class)); + cards.add(new SetCardInfo("Plains", 240, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Sacred Foundry", 285, Rarity.RARE, mage.cards.s.SacredFoundry.class)); + cards.add(new SetCardInfo("Saw in Half", 88, Rarity.RARE, mage.cards.s.SawInHalf.class)); + cards.add(new SetCardInfo("Steam Vents", 283, Rarity.RARE, mage.cards.s.SteamVents.class)); + cards.add(new SetCardInfo("Stomping Ground", 280, Rarity.RARE, mage.cards.s.StompingGround.class)); + cards.add(new SetCardInfo("Swamp", 237, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Temple Garden", 281, Rarity.RARE, mage.cards.t.TempleGarden.class)); + cards.add(new SetCardInfo("The Space Family Goblinson", 179, Rarity.UNCOMMON, mage.cards.t.TheSpaceFamilyGoblinson.class)); + cards.add(new SetCardInfo("Watery Grave", 278, Rarity.RARE, mage.cards.w.WateryGrave.class)); + } +} diff --git a/Mage.Sets/src/mage/sets/UniversesWithin.java b/Mage.Sets/src/mage/sets/UniversesWithin.java new file mode 100644 index 00000000000..7d919c69dfb --- /dev/null +++ b/Mage.Sets/src/mage/sets/UniversesWithin.java @@ -0,0 +1,35 @@ +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * @author TheElk801 + */ +public final class UniversesWithin extends ExpansionSet { + + private static final UniversesWithin instance = new UniversesWithin(); + + public static UniversesWithin getInstance() { + return instance; + } + + private UniversesWithin() { + // The set name is a placeholder and will likely change + super("Universes Within", "SLX", ExpansionSet.buildDate(2022, 3, 3), SetType.SUPPLEMENTAL); + this.hasBasicLands = false; + this.hasBoosters = false; + + cards.add(new SetCardInfo("Arvinox, the Mind Flail", 1, Rarity.MYTHIC, mage.cards.a.ArvinoxTheMindFlail.class)); + cards.add(new SetCardInfo("Bjorna, Nightfall Alchemist", 2, Rarity.RARE, mage.cards.b.BjornaNightfallAlchemist.class)); + cards.add(new SetCardInfo("Cecily, Haunted Mage", 3, Rarity.RARE, mage.cards.c.CecilyHauntedMage.class)); + cards.add(new SetCardInfo("Elmar, Ulvenwald Informant", 4, Rarity.RARE, mage.cards.e.ElmarUlvenwaldInformant.class)); + cards.add(new SetCardInfo("Hargilde, Kindly Runechanter", 5, Rarity.RARE, mage.cards.h.HargildeKindlyRunechanter.class)); + cards.add(new SetCardInfo("Havengul Laboratory", 9, Rarity.RARE, mage.cards.h.HavengulLaboratory.class)); + cards.add(new SetCardInfo("Havengul Mystery", 9, Rarity.RARE, mage.cards.h.HavengulMystery.class)); + cards.add(new SetCardInfo("Othelm, Sigardian Outcast", 6, Rarity.RARE, mage.cards.o.OthelmSigardianOutcast.class)); + cards.add(new SetCardInfo("Sophina, Spearsage Deserter", 7, Rarity.RARE, mage.cards.s.SophinaSpearsageDeserter.class)); + cards.add(new SetCardInfo("Wernog, Rider's Chaplain", 8, Rarity.RARE, mage.cards.w.WernogRidersChaplain.class)); + } +} diff --git a/Mage.Sets/src/mage/sets/VintageMasters.java b/Mage.Sets/src/mage/sets/VintageMasters.java index 3347805d4fd..6ddaf2d0abb 100644 --- a/Mage.Sets/src/mage/sets/VintageMasters.java +++ b/Mage.Sets/src/mage/sets/VintageMasters.java @@ -1,11 +1,11 @@ package mage.sets; +import mage.cards.Card; import mage.cards.ExpansionSet; -import mage.cards.repository.CardCriteria; import mage.cards.repository.CardInfo; -import mage.cards.repository.CardRepository; import mage.constants.Rarity; import mage.constants.SetType; +import mage.util.RandomUtil; import java.util.List; @@ -30,6 +30,7 @@ public final class VintageMasters extends ExpansionSet { this.numBoosterUncommon = 3; this.numBoosterRare = 1; this.ratioBoosterMythic = 8; + cards.add(new SetCardInfo("Academy Elite", 55, Rarity.RARE, mage.cards.a.AcademyElite.class)); cards.add(new SetCardInfo("Addle", 103, Rarity.COMMON, mage.cards.a.Addle.class)); cards.add(new SetCardInfo("Aether Mutation", 241, Rarity.UNCOMMON, mage.cards.a.AetherMutation.class)); cards.add(new SetCardInfo("Afterlife", 10, Rarity.COMMON, mage.cards.a.Afterlife.class)); @@ -92,6 +93,7 @@ public final class VintageMasters extends ExpansionSet { cards.add(new SetCardInfo("Control Magic", 63, Rarity.RARE, mage.cards.c.ControlMagic.class)); cards.add(new SetCardInfo("Council's Judgment", 20, Rarity.RARE, mage.cards.c.CouncilsJudgment.class)); cards.add(new SetCardInfo("Counterspell", 64, Rarity.COMMON, mage.cards.c.Counterspell.class)); + cards.add(new SetCardInfo("Crater Hellion", 157, Rarity.RARE, mage.cards.c.CraterHellion.class)); cards.add(new SetCardInfo("Crescendo of War", 21, Rarity.RARE, mage.cards.c.CrescendoOfWar.class)); cards.add(new SetCardInfo("Crovax the Cursed", 110, Rarity.RARE, mage.cards.c.CrovaxTheCursed.class)); cards.add(new SetCardInfo("Cruel Bargain", 111, Rarity.RARE, mage.cards.c.CruelBargain.class)); @@ -154,6 +156,7 @@ public final class VintageMasters extends ExpansionSet { cards.add(new SetCardInfo("Goblin Goon", 166, Rarity.UNCOMMON, mage.cards.g.GoblinGoon.class)); cards.add(new SetCardInfo("Goblin Lackey", 167, Rarity.RARE, mage.cards.g.GoblinLackey.class)); cards.add(new SetCardInfo("Goblin Matron", 168, Rarity.COMMON, mage.cards.g.GoblinMatron.class)); + cards.add(new SetCardInfo("Goblin Patrol", 169, Rarity.COMMON, mage.cards.g.GoblinPatrol.class)); cards.add(new SetCardInfo("Goblin Piledriver", 170, Rarity.RARE, mage.cards.g.GoblinPiledriver.class)); cards.add(new SetCardInfo("Goblin Ringleader", 171, Rarity.UNCOMMON, mage.cards.g.GoblinRingleader.class)); cards.add(new SetCardInfo("Goblin Settler", 172, Rarity.UNCOMMON, mage.cards.g.GoblinSettler.class)); @@ -190,6 +193,7 @@ public final class VintageMasters extends ExpansionSet { cards.add(new SetCardInfo("Lake of the Dead", 302, Rarity.RARE, mage.cards.l.LakeOfTheDead.class)); cards.add(new SetCardInfo("Laquatus's Champion", 125, Rarity.RARE, mage.cards.l.LaquatussChampion.class)); cards.add(new SetCardInfo("Library of Alexandria", 303, Rarity.MYTHIC, mage.cards.l.LibraryOfAlexandria.class)); + cards.add(new SetCardInfo("Lightning Dragon", 177, Rarity.RARE, mage.cards.l.LightningDragon.class)); cards.add(new SetCardInfo("Lightning Rift", 178, Rarity.UNCOMMON, mage.cards.l.LightningRift.class)); cards.add(new SetCardInfo("Lion's Eye Diamond", 271, Rarity.MYTHIC, mage.cards.l.LionsEyeDiamond.class)); cards.add(new SetCardInfo("Living Death", 126, Rarity.RARE, mage.cards.l.LivingDeath.class)); @@ -230,6 +234,7 @@ public final class VintageMasters extends ExpansionSet { cards.add(new SetCardInfo("Obsessive Search", 83, Rarity.COMMON, mage.cards.o.ObsessiveSearch.class)); cards.add(new SetCardInfo("Ophidian", 84, Rarity.COMMON, mage.cards.o.Ophidian.class)); cards.add(new SetCardInfo("Orcish Lumberjack", 179, Rarity.COMMON, mage.cards.o.OrcishLumberjack.class)); + cards.add(new SetCardInfo("Owl Familiar", 85, Rarity.COMMON, mage.cards.o.OwlFamiliar.class)); cards.add(new SetCardInfo("Palinchron", 86, Rarity.RARE, mage.cards.p.Palinchron.class)); cards.add(new SetCardInfo("Parallax Wave", 37, Rarity.RARE, mage.cards.p.ParallaxWave.class)); cards.add(new SetCardInfo("Paralyze", 132, Rarity.COMMON, mage.cards.p.Paralyze.class)); @@ -353,38 +358,25 @@ public final class VintageMasters extends ExpansionSet { } @Override - public List getSpecialCommon() { - CardCriteria criteria = new CardCriteria(); - criteria.rarities(Rarity.COMMON).setCodes(this.code); - return CardRepository.instance.findCards(criteria); + protected void addSpecialCards(List booster, int number) { + // number is here always 1 + Rarity rarity; + // You will open a Power Nine card about once in 53 packs + if (RandomUtil.nextInt(53) == 0) { + rarity = Rarity.BONUS; + } else { + // assuming same distribution as foils in paper Masters sets, 10:3:1 C:U:R + int rarityKey = RandomUtil.nextInt(112); + if (rarityKey < 80) { + rarity = Rarity.COMMON; + } else if (rarityKey < 104) { + rarity = Rarity.UNCOMMON; + } else if (rarityKey < 111) { + rarity = Rarity.RARE; + } else { + rarity = Rarity.MYTHIC; + } + } + addToBooster(booster, getCardsByRarity(rarity)); } - - @Override - public List getSpecialUncommon() { - CardCriteria criteria = new CardCriteria(); - criteria.rarities(Rarity.UNCOMMON).setCodes(this.code); - return CardRepository.instance.findCards(criteria); - } - - @Override - public List getSpecialRare() { - CardCriteria criteria = new CardCriteria(); - criteria.rarities(Rarity.RARE).setCodes(this.code); - return CardRepository.instance.findCards(criteria); - } - - @Override - public List getSpecialMythic() { - CardCriteria criteria = new CardCriteria(); - criteria.rarities(Rarity.MYTHIC).setCodes(this.code); - return CardRepository.instance.findCards(criteria); - } - - @Override - public List getSpecialBonus() { - CardCriteria criteria = new CardCriteria(); - criteria.rarities(Rarity.BONUS).setCodes(this.code); - return CardRepository.instance.findCards(criteria); - } - } diff --git a/Mage.Sets/src/mage/sets/WarOfTheSpark.java b/Mage.Sets/src/mage/sets/WarOfTheSpark.java index e86c4dbfe29..39683297740 100644 --- a/Mage.Sets/src/mage/sets/WarOfTheSpark.java +++ b/Mage.Sets/src/mage/sets/WarOfTheSpark.java @@ -1,10 +1,14 @@ package mage.sets; import mage.cards.ExpansionSet; +import mage.cards.repository.CardCriteria; +import mage.cards.repository.CardInfo; +import mage.cards.repository.CardRepository; import mage.collation.BoosterCollator; import mage.collation.BoosterStructure; import mage.collation.CardRun; import mage.collation.RarityConfiguration; +import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.SetType; @@ -27,8 +31,9 @@ public final class WarOfTheSpark extends ExpansionSet { this.numBoosterCommon = 10; this.numBoosterUncommon = 3; this.numBoosterRare = 1; - this.ratioBoosterMythic = 8; - this.needsPlaneswalker = true; + this.ratioBoosterMythic = (40 + 40 + 12) / 12.0; // non-planeswalkers: 40 rares, 12 mythics + this.ratioBoosterSpecialRare = 4; + this.ratioBoosterSpecialMythic = (13 + 13 + 3) / 3.0; // planeswalkers: 13 rares, 3 mythics this.maxCardNumberInBooster = 264; cards.add(new SetCardInfo("Ahn-Crop Invader", 113, Rarity.COMMON, mage.cards.a.AhnCropInvader.class)); @@ -344,6 +349,17 @@ public final class WarOfTheSpark extends ExpansionSet { cards.add(new SetCardInfo("Widespread Brutality", 226, Rarity.RARE, mage.cards.w.WidespreadBrutality.class)); } + @Override + protected List findSpecialCardsByRarity(Rarity rarity) { + List cardInfos = super.findSpecialCardsByRarity(rarity); + cardInfos.addAll(CardRepository.instance.findCards(new CardCriteria() + .setCodes(this.code) + .rarities(rarity) + .types(CardType.PLANESWALKER) + .maxCardNumber(maxCardNumberInBooster))); + return cardInfos; + } + @Override public BoosterCollator createCollator() { return new WarOfTheSparkCollator(); diff --git a/Mage.Sets/src/mage/sets/WizardsPlayNetwork2009.java b/Mage.Sets/src/mage/sets/WizardsPlayNetwork2009.java index 52ca5d02dd0..bb18f9122f0 100644 --- a/Mage.Sets/src/mage/sets/WizardsPlayNetwork2009.java +++ b/Mage.Sets/src/mage/sets/WizardsPlayNetwork2009.java @@ -16,7 +16,7 @@ public class WizardsPlayNetwork2009 extends ExpansionSet { } private WizardsPlayNetwork2009() { - super("Wizards Play Network 2009", "PWP09", ExpansionSet.buildDate(2009, 1, 1), SetType.PROMOTIONAL); + super("Wizards Play Network 2009", "PW09", ExpansionSet.buildDate(2009, 1, 1), SetType.PROMOTIONAL); this.hasBoosters = false; this.hasBasicLands = false; diff --git a/Mage.Sets/src/mage/sets/WizardsPlayNetwork2010.java b/Mage.Sets/src/mage/sets/WizardsPlayNetwork2010.java index e76290fd14c..b29b28d0543 100644 --- a/Mage.Sets/src/mage/sets/WizardsPlayNetwork2010.java +++ b/Mage.Sets/src/mage/sets/WizardsPlayNetwork2010.java @@ -16,7 +16,7 @@ public class WizardsPlayNetwork2010 extends ExpansionSet { } private WizardsPlayNetwork2010() { - super("Wizards Play Network 2010", "PWP10", ExpansionSet.buildDate(2010, 1, 1), SetType.PROMOTIONAL); + super("Wizards Play Network 2010", "PW10", ExpansionSet.buildDate(2010, 1, 1), SetType.PROMOTIONAL); this.hasBoosters = false; this.hasBasicLands = false; diff --git a/Mage.Sets/src/mage/sets/WizardsPlayNetwork2011.java b/Mage.Sets/src/mage/sets/WizardsPlayNetwork2011.java index e628de4527e..2dab797f192 100644 --- a/Mage.Sets/src/mage/sets/WizardsPlayNetwork2011.java +++ b/Mage.Sets/src/mage/sets/WizardsPlayNetwork2011.java @@ -16,7 +16,7 @@ public class WizardsPlayNetwork2011 extends ExpansionSet { } private WizardsPlayNetwork2011() { - super("Wizards Play Network 2011", "PWP11", ExpansionSet.buildDate(2011, 1, 1), SetType.PROMOTIONAL); + super("Wizards Play Network 2011", "PW11", ExpansionSet.buildDate(2011, 1, 1), SetType.PROMOTIONAL); this.hasBoosters = false; this.hasBasicLands = false; diff --git a/Mage.Sets/src/mage/sets/WizardsPlayNetwork2012.java b/Mage.Sets/src/mage/sets/WizardsPlayNetwork2012.java index 2c732534d8d..3c6b3dab84d 100644 --- a/Mage.Sets/src/mage/sets/WizardsPlayNetwork2012.java +++ b/Mage.Sets/src/mage/sets/WizardsPlayNetwork2012.java @@ -16,7 +16,7 @@ public class WizardsPlayNetwork2012 extends ExpansionSet { } private WizardsPlayNetwork2012() { - super("Wizards Play Network 2012", "PWP12", ExpansionSet.buildDate(2012, 1, 1), SetType.PROMOTIONAL); + super("Wizards Play Network 2012", "PW12", ExpansionSet.buildDate(2012, 1, 1), SetType.PROMOTIONAL); this.hasBoosters = false; this.hasBasicLands = false; diff --git a/Mage.Sets/src/mage/sets/Worldwake.java b/Mage.Sets/src/mage/sets/Worldwake.java index 590a8c01aa5..743901f1d72 100644 --- a/Mage.Sets/src/mage/sets/Worldwake.java +++ b/Mage.Sets/src/mage/sets/Worldwake.java @@ -1,10 +1,16 @@ - package mage.sets; import mage.cards.ExpansionSet; +import mage.collation.BoosterCollator; +import mage.collation.BoosterStructure; +import mage.collation.CardRun; +import mage.collation.RarityConfiguration; import mage.constants.Rarity; import mage.constants.SetType; +import java.util.ArrayList; +import java.util.List; + /** * * @author BetaSteward_at_googlemail.com @@ -175,4 +181,56 @@ public final class Worldwake extends ExpansionSet { cards.add(new SetCardInfo("Wrexial, the Risen Deep", 120, Rarity.MYTHIC, mage.cards.w.WrexialTheRisenDeep.class)); } + @Override + public BoosterCollator createCollator() { + return new WorldwakeCollator(); + } +} + +// Booster collation info from https://www.lethe.xyz/mtg/collation/wwk.html +// Using USA collation for common/uncommon, rare collation inferred from other sets +class WorldwakeCollator implements BoosterCollator { + private final CardRun commonA = new CardRun(true, "69", "14", "46", "140", "91", "132", "103", "51", "8", "27", "125", "90", "137", "56", "95", "2", "24", "138", "91", "25", "128", "63", "101", "5", "140", "78", "27", "143", "14", "95", "90", "63", "117", "39", "143", "9", "58", "78", "142", "103", "46", "56", "9", "82", "125", "142", "117", "69", "25", "2", "83", "101", "132", "128", "58", "24", "82", "8", "104", "137", "51", "39", "138", "5", "83", "104"); + private final CardRun commonB = new CardRun(true, "55", "75", "112", "23", "29", "62", "88", "108", "6", "33", "126", "65", "73", "102", "4", "43", "60", "71", "99", "23", "42", "131", "62", "73", "100", "6", "29", "52", "71", "112", "18", "26", "126", "55", "77", "108", "10", "43", "52", "75", "99", "4", "26", "131", "65", "88", "100", "18", "42", "60", "77", "102", "10", "33"); + private final CardRun uncommonA = new CardRun(true, "13", "30", "50", "80", "54", "44", "116", "7", "79", "114", "41", "17", "50", "44", "92", "70", "116", "123", "13", "79", "30", "68", "7", "97", "123", "70", "80", "41", "13", "124", "114", "94", "68", "12", "41", "97", "45", "54", "79", "17", "114", "30", "80", "107", "7", "50", "45", "92", "124", "70", "107", "17", "94", "45", "12", "97", "124", "54", "92", "123", "44", "12", "116", "68", "107", "94"); + private final CardRun uncommonB = new CardRun(true, "129", "87", "61", "110", "145", "38", "16", "135", "89", "61", "111", "34", "19", "145", "87", "67", "38", "111", "89", "129", "36", "16", "110", "67", "86", "135", "61", "34", "16", "98", "66", "38", "11", "87", "135", "110", "36", "66", "129", "19", "86", "34", "98", "145", "11", "89", "67", "36", "111", "66", "19", "98", "11", "86"); + private final CardRun rare = new CardRun(false, "3", "15", "20", "21", "22", "28", "32", "35", "37", "40", "48", "49", "53", "57", "59", "64", "72", "74", "84", "85", "93", "105", "106", "113", "115", "118", "121", "122", "127", "130", "133", "134", "139", "141", "144"); + private final CardRun mythic = new CardRun(false, "1", "31", "47", "76", "81", "96", "109", "119", "120", "136"); + private final CardRun land = new CardRun(false, "ZEN_230", "ZEN_231", "ZEN_232", "ZEN_233", "ZEN_234", "ZEN_235", "ZEN_236", "ZEN_237", "ZEN_238", "ZEN_239", "ZEN_240", "ZEN_241", "ZEN_242", "ZEN_243", "ZEN_244", "ZEN_245", "ZEN_246", "ZEN_247", "ZEN_248", "ZEN_249"); + + private final BoosterStructure AAAAABBBBB = new BoosterStructure( + commonA, commonA, commonA, commonA, commonA, + commonB, commonB, commonB, commonB, commonB + ); + private final BoosterStructure AAAAAABBBB = new BoosterStructure( + commonA, commonA, commonA, commonA, commonA, commonA, + commonB, commonB, commonB, commonB + ); + private final BoosterStructure AAB = new BoosterStructure(uncommonA, uncommonA, uncommonB); + private final BoosterStructure ABB = new BoosterStructure(uncommonA, uncommonB, uncommonB); + private final BoosterStructure R1 = new BoosterStructure(rare); + private final BoosterStructure M1 = new BoosterStructure(mythic); + private final BoosterStructure L1 = new BoosterStructure(land); + + private final RarityConfiguration commonRuns = new RarityConfiguration(AAAAABBBBB, AAAAAABBBB); + // In order for equal numbers of each uncommon to exist, the average booster must contain: + // 1.65 A uncommons (33 / 20) + // 1.35 B uncommons (27 / 20) + // These numbers are the same for all sets with 40 uncommons in asymmetrical A/B print runs + private final RarityConfiguration uncommonRuns = new RarityConfiguration( + AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, + ABB, ABB, ABB, ABB, ABB, ABB, ABB + ); + private final RarityConfiguration rareRuns = new RarityConfiguration(R1, R1, R1, R1, R1, R1, R1, M1); + private final RarityConfiguration landRuns = new RarityConfiguration(L1); + + @Override + public List makeBooster() { + List booster = new ArrayList<>(); + booster.addAll(commonRuns.getNext().makeRun()); + booster.addAll(uncommonRuns.getNext().makeRun()); + booster.addAll(rareRuns.getNext().makeRun()); + booster.addAll(landRuns.getNext().makeRun()); + return booster; + } } diff --git a/Mage.Sets/src/mage/sets/Zendikar.java b/Mage.Sets/src/mage/sets/Zendikar.java index 9521768efba..368d6796210 100644 --- a/Mage.Sets/src/mage/sets/Zendikar.java +++ b/Mage.Sets/src/mage/sets/Zendikar.java @@ -3,6 +3,7 @@ package mage.sets; import mage.ObjectColor; import mage.cards.CardGraphicInfo; import mage.cards.ExpansionSet; +import mage.cards.repository.CardInfo; import mage.collation.BoosterCollator; import mage.collation.BoosterStructure; import mage.collation.CardRun; @@ -305,6 +306,16 @@ public final class Zendikar extends ExpansionSet { cards.add(new SetCardInfo("Zendikar Farguide", 194, Rarity.COMMON, mage.cards.z.ZendikarFarguide.class)); } + @Override + protected List findCardsByRarity(Rarity rarity) { + List cardInfos = super.findCardsByRarity(rarity); + if (rarity == Rarity.LAND) { + // only the full-art basic lands are found in boosters + cardInfos.removeIf(cardInfo -> cardInfo.getCardNumber().contains("a")); + } + return cardInfos; + } + @Override public BoosterCollator createCollator() { return new ZendikarCollator(); diff --git a/Mage.Sets/src/mage/sets/ZendikarRising.java b/Mage.Sets/src/mage/sets/ZendikarRising.java index 6ae423eff9e..44e7be65b73 100644 --- a/Mage.Sets/src/mage/sets/ZendikarRising.java +++ b/Mage.Sets/src/mage/sets/ZendikarRising.java @@ -1,8 +1,9 @@ package mage.sets; -import mage.cards.Card; import mage.cards.ExpansionSet; -import mage.cards.ModalDoubleFacesCard; +import mage.cards.repository.CardCriteria; +import mage.cards.repository.CardInfo; +import mage.cards.repository.CardRepository; import mage.constants.Rarity; import mage.constants.SetType; @@ -28,7 +29,9 @@ public final class ZendikarRising extends ExpansionSet { this.numBoosterCommon = 10; this.numBoosterUncommon = 3; this.numBoosterRare = 1; - this.ratioBoosterMythic = 7.4; + this.ratioBoosterMythic = 8; + this.ratioBoosterSpecialRare = 5.5; + this.ratioBoosterSpecialMythic = 5.4; // 5 mythic MDFCs, 11 rare MDFCs this.maxCardNumberInBooster = 280; cards.add(new SetCardInfo("Acquisitions Expert", 89, Rarity.UNCOMMON, mage.cards.a.AcquisitionsExpert.class)); @@ -425,8 +428,13 @@ public final class ZendikarRising extends ExpansionSet { } @Override - protected boolean boosterIsValid(List booster) { - return super.boosterIsValid(booster) - && booster.stream().filter(ModalDoubleFacesCard.class::isInstance).count() == 1; + protected List findSpecialCardsByRarity(Rarity rarity) { + List cardInfos = super.findSpecialCardsByRarity(rarity); + cardInfos.addAll(CardRepository.instance.findCards(new CardCriteria() + .setCodes(this.code) + .rarities(rarity) + .modalDoubleFaced(true) + .maxCardNumber(maxCardNumberInBooster))); + return cardInfos; } } diff --git a/Mage.Sets/src/mage/sets/ZendikarRisingCommander.java b/Mage.Sets/src/mage/sets/ZendikarRisingCommander.java index 804fc57587e..0973d121a8b 100644 --- a/Mage.Sets/src/mage/sets/ZendikarRisingCommander.java +++ b/Mage.Sets/src/mage/sets/ZendikarRisingCommander.java @@ -24,7 +24,8 @@ public final class ZendikarRisingCommander extends ExpansionSet { cards.add(new SetCardInfo("Acidic Slime", 59, Rarity.UNCOMMON, mage.cards.a.AcidicSlime.class)); cards.add(new SetCardInfo("Admonition Angel", 10, Rarity.MYTHIC, mage.cards.a.AdmonitionAngel.class)); cards.add(new SetCardInfo("Aetherize", 23, Rarity.UNCOMMON, mage.cards.a.Aetherize.class)); - cards.add(new SetCardInfo("Anowon, the Ruin Thief", 1, Rarity.MYTHIC, mage.cards.a.AnowonTheRuinThief.class)); + cards.add(new SetCardInfo("Anowon, the Ruin Thief", 1, Rarity.MYTHIC, mage.cards.a.AnowonTheRuinThief.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Anowon, the Ruin Thief", 7, Rarity.MYTHIC, mage.cards.a.AnowonTheRuinThief.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Arcane Signet", 106, Rarity.COMMON, mage.cards.a.ArcaneSignet.class)); cards.add(new SetCardInfo("Armorcraft Judge", 60, Rarity.UNCOMMON, mage.cards.a.ArmorcraftJudge.class)); cards.add(new SetCardInfo("Banishing Light", 11, Rarity.UNCOMMON, mage.cards.b.BanishingLight.class)); @@ -46,7 +47,7 @@ public final class ZendikarRisingCommander extends ExpansionSet { cards.add(new SetCardInfo("Dimir Guildgate", 127, Rarity.COMMON, mage.cards.d.DimirGuildgate.class)); cards.add(new SetCardInfo("Dimir Keyrune", 110, Rarity.UNCOMMON, mage.cards.d.DimirKeyrune.class)); cards.add(new SetCardInfo("Dimir Locket", 111, Rarity.COMMON, mage.cards.d.DimirLocket.class)); - cards.add(new SetCardInfo("Dimir Signet", 112, Rarity.UNCOMMON, mage.cards.d.DimirSignet.class)); + cards.add(new SetCardInfo("Dimir Signet", 112, Rarity.COMMON, mage.cards.d.DimirSignet.class)); cards.add(new SetCardInfo("Dismal Backwater", 128, Rarity.COMMON, mage.cards.d.DismalBackwater.class)); cards.add(new SetCardInfo("Distant Melody", 24, Rarity.COMMON, mage.cards.d.DistantMelody.class)); cards.add(new SetCardInfo("Elite Scaleguard", 14, Rarity.UNCOMMON, mage.cards.e.EliteScaleguard.class)); @@ -92,7 +93,7 @@ public final class ZendikarRisingCommander extends ExpansionSet { cards.add(new SetCardInfo("Master Thief", 30, Rarity.UNCOMMON, mage.cards.m.MasterThief.class)); cards.add(new SetCardInfo("Military Intelligence", 31, Rarity.UNCOMMON, mage.cards.m.MilitaryIntelligence.class)); cards.add(new SetCardInfo("Mina and Denn, Wildborn", 94, Rarity.RARE, mage.cards.m.MinaAndDennWildborn.class)); - cards.add(new SetCardInfo("Mind Stone", 114, Rarity.COMMON, mage.cards.m.MindStone.class)); + cards.add(new SetCardInfo("Mind Stone", 114, Rarity.UNCOMMON, mage.cards.m.MindStone.class)); cards.add(new SetCardInfo("Multani, Yavimaya's Avatar", 75, Rarity.MYTHIC, mage.cards.m.MultaniYavimayasAvatar.class)); cards.add(new SetCardInfo("Murder", 47, Rarity.COMMON, mage.cards.m.Murder.class)); cards.add(new SetCardInfo("Myriad Landscape", 135, Rarity.UNCOMMON, mage.cards.m.MyriadLandscape.class)); @@ -106,7 +107,8 @@ public final class ZendikarRisingCommander extends ExpansionSet { cards.add(new SetCardInfo("Notion Thief", 96, Rarity.RARE, mage.cards.n.NotionThief.class)); cards.add(new SetCardInfo("Notorious Throng", 33, Rarity.RARE, mage.cards.n.NotoriousThrong.class)); cards.add(new SetCardInfo("Obelisk of Urd", 115, Rarity.RARE, mage.cards.o.ObeliskOfUrd.class)); - cards.add(new SetCardInfo("Obuun, Mul Daya Ancestor", 2, Rarity.MYTHIC, mage.cards.o.ObuunMulDayaAncestor.class)); + cards.add(new SetCardInfo("Obuun, Mul Daya Ancestor", 2, Rarity.MYTHIC, mage.cards.o.ObuunMulDayaAncestor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Obuun, Mul Daya Ancestor", 8, Rarity.MYTHIC, mage.cards.o.ObuunMulDayaAncestor.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ogre Slumlord", 50, Rarity.RARE, mage.cards.o.OgreSlumlord.class)); cards.add(new SetCardInfo("Omnath, Locus of Rage", 97, Rarity.MYTHIC, mage.cards.o.OmnathLocusOfRage.class)); cards.add(new SetCardInfo("Oona's Blackguard", 51, Rarity.UNCOMMON, mage.cards.o.OonasBlackguard.class)); @@ -139,6 +141,7 @@ public final class ZendikarRisingCommander extends ExpansionSet { cards.add(new SetCardInfo("Springbloom Druid", 83, Rarity.COMMON, mage.cards.s.SpringbloomDruid.class)); cards.add(new SetCardInfo("Stinkdrinker Bandit", 55, Rarity.UNCOMMON, mage.cards.s.StinkdrinkerBandit.class)); cards.add(new SetCardInfo("Stolen Identity", 37, Rarity.RARE, mage.cards.s.StolenIdentity.class)); + cards.add(new SetCardInfo("Struggle // Survive", 102, Rarity.UNCOMMON, mage.cards.s.StruggleSurvive.class)); cards.add(new SetCardInfo("Submerged Boneyard", 141, Rarity.UNCOMMON, mage.cards.s.SubmergedBoneyard.class)); cards.add(new SetCardInfo("Sun Titan", 21, Rarity.MYTHIC, mage.cards.s.SunTitan.class)); cards.add(new SetCardInfo("Sygg, River Cutthroat", 103, Rarity.RARE, mage.cards.s.SyggRiverCutthroat.class)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/activated/ReconfigureTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/activated/ReconfigureTest.java new file mode 100644 index 00000000000..1b078a7378c --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/activated/ReconfigureTest.java @@ -0,0 +1,105 @@ +package org.mage.test.cards.abilities.activated; + +import mage.abilities.keyword.TrampleAbility; +import mage.constants.CardType; +import mage.constants.PhaseStep; +import mage.constants.SubType; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author TheElk801 + */ +public class ReconfigureTest extends CardTestPlayerBase { + + private static final String lion = "Silvercoat Lion"; + private static final String boar = "Bronzeplate Boar"; + private static final String aid = "Sigarda's Aid"; + private static final String paladin = "Puresteel Paladin"; + private static final String relic = "Darksteel Relic"; + + @Test + public void testAttach() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); + addCard(Zone.BATTLEFIELD, playerA, lion); + addCard(Zone.BATTLEFIELD, playerA, boar); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Reconfigure", lion); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertType(boar, CardType.CREATURE, false); + assertSubtype(boar, SubType.EQUIPMENT); + assertIsAttachedTo(playerA, boar, lion); + assertPowerToughness(playerA, lion, 2 + 3, 2 + 2); + assertAbility(playerA, lion, TrampleAbility.getInstance(), true); + } + + @Test + public void testAttachDetach() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 10); + addCard(Zone.BATTLEFIELD, playerA, lion); + addCard(Zone.BATTLEFIELD, playerA, boar); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Reconfigure", lion); + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{5}:"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertType(boar, CardType.CREATURE, true); + assertSubtype(boar, SubType.EQUIPMENT); + assertPowerToughness(playerA, lion, 2, 2); + assertAbility(playerA, lion, TrampleAbility.getInstance(), false); + } + + @Test + public void testSigardasAid() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + addCard(Zone.BATTLEFIELD, playerA, lion); + addCard(Zone.BATTLEFIELD, playerA, aid); + addCard(Zone.HAND, playerA, boar); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, boar); + addTarget(playerA, lion); + setChoice(playerA, true); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertType(boar, CardType.CREATURE, false); + assertSubtype(boar, SubType.EQUIPMENT); + assertIsAttachedTo(playerA, boar, lion); + assertPowerToughness(playerA, lion, 2 + 3, 2 + 2); + assertAbility(playerA, lion, TrampleAbility.getInstance(), true); + } + + @Test + public void testPuresteelPaladin() { + addCard(Zone.BATTLEFIELD, playerA, lion); + addCard(Zone.BATTLEFIELD, playerA, boar); + addCard(Zone.BATTLEFIELD, playerA, paladin); + addCard(Zone.BATTLEFIELD, playerA, relic, 2); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip", lion); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertType(boar, CardType.CREATURE, false); + assertSubtype(boar, SubType.EQUIPMENT); + assertIsAttachedTo(playerA, boar, lion); + assertPowerToughness(playerA, lion, 2 + 3, 2 + 2); + assertAbility(playerA, lion, TrampleAbility.getInstance(), true); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/CompleatedTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/CompleatedTest.java new file mode 100644 index 00000000000..1bd21b656aa --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/CompleatedTest.java @@ -0,0 +1,47 @@ +package org.mage.test.cards.abilities.keywords; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author TheElk801 + */ +public class CompleatedTest extends CardTestPlayerBase { + + private static final String tamiyo = "Tamiyo, Compleated Sage"; + + @Test + public void testLifePaid() { + addCard(Zone.BATTLEFIELD, playerA, "Tropical Island", 4); + addCard(Zone.HAND, playerA, tamiyo); + + setChoice(playerA, true); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, tamiyo); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertCounterCount(playerA, tamiyo, CounterType.LOYALTY, 3); + } + + @Test + public void testManaPaid() { + addCard(Zone.BATTLEFIELD, playerA, "Tropical Island", 5); + addCard(Zone.HAND, playerA, tamiyo); + + setChoice(playerA, false); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, tamiyo); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertCounterCount(playerA, tamiyo, CounterType.LOYALTY, 5); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ConspireTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ConspireTest.java index e9d95cdb6cb..f386b7a2fa5 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ConspireTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ConspireTest.java @@ -48,7 +48,7 @@ public class ConspireTest extends CardTestPlayerBase { setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); - assertLife(playerB, 14); + assertLife(playerB, 20 - 3 - 3); assertGraveyardCount(playerA, "Burn Trail", 1); assertTapped("Goblin Roughrider", true); assertTapped("Raging Goblin", true); @@ -68,7 +68,7 @@ public class ConspireTest extends CardTestPlayerBase { setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); - assertLife(playerB, 17); + assertLife(playerB, 20 - 3); assertGraveyardCount(playerA, "Burn Trail", 1); assertTapped("Goblin Roughrider", false); assertTapped("Raging Goblin", false); @@ -101,12 +101,70 @@ public class ConspireTest extends CardTestPlayerBase { assertPermanentCount(playerA, "Wort, the Raidmother", 1); assertGraveyardCount(playerA, "Lightning Bolt", 1); - assertLife(playerB, 14); + assertLife(playerB, 20 - 3 - 3); } @Test - public void testWortTheRaidmotherWithConspireSpell() { + public void testWortTheRaidmotherTwoSpells() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 8); + addCard(Zone.BATTLEFIELD, playerA, "Raging Goblin", 2); + // When Wort, the Raidmother enters the battlefield, put two 1/1 red and green Goblin Warrior creature tokens onto the battlefield. + // Each red or green instant or sorcery spell you cast has conspire. + // (As you cast the spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it and you may choose new targets for the copy.) + addCard(Zone.HAND, playerA, "Wort, the Raidmother"); + addCard(Zone.HAND, playerA, "Lightning Bolt"); + addCard(Zone.HAND, playerA, "Shock"); + + // prepare goblins + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wort, the Raidmother");// {4}{R/G}{R/G} + + // cast with conspire + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + setChoice(playerA, true); // use conspire + setChoice(playerA, "Goblin Warrior"); + setChoice(playerA, "Goblin Warrior"); + setChoice(playerA, false); // keep targets + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Shock", playerB); + setChoice(playerA, false); // don't use conspire + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Wort, the Raidmother", 1); + assertGraveyardCount(playerA, "Lightning Bolt", 1); + assertTapped("Raging Goblin", false); + assertLife(playerB, 20 - 3 - 3 - 2); + + } + + @Test + public void testWortTheRaidmotherWithConspireSpellOnce() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 10); + // When Wort, the Raidmother enters the battlefield, put two 1/1 red and green Goblin Warrior creature tokens onto the battlefield. + // Each red or green instant or sorcery spell you cast has conspire. + // (As you cast the spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it and you may choose new targets for the copy.) + addCard(Zone.HAND, playerA, "Wort, the Raidmother"); + addCard(Zone.HAND, playerA, "Burn Trail"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wort, the Raidmother"); // {4}{R/G}{R/G} + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Burn Trail", playerB); + setChoice(playerA, true); // use Conspire from Burn Trail itself + setChoice(playerA, false); // don't use Conspire gained from Wort, the Raidmother + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertPermanentCount(playerA, "Wort, the Raidmother", 1); + assertLife(playerB, 20 - 3 - 3); + assertLife(playerA, 20); + assertGraveyardCount(playerA, "Burn Trail", 1); + } + + @Test + public void testWortTheRaidmotherWithConspireSpellTwice() { addCard(Zone.BATTLEFIELD, playerA, "Mountain", 10); addCard(Zone.BATTLEFIELD, playerA, "Raging Goblin", 2); // When Wort, the Raidmother enters the battlefield, put two 1/1 red and green Goblin Warrior creature tokens onto the battlefield. @@ -124,12 +182,66 @@ public class ConspireTest extends CardTestPlayerBase { setStopAt(1, PhaseStep.END_TURN); execute(); assertPermanentCount(playerA, "Wort, the Raidmother", 1); - assertLife(playerB, 11); + assertLife(playerB, 20 - 3 - 3 - 3); assertLife(playerA, 20); assertGraveyardCount(playerA, "Burn Trail", 1); } + @Test + public void testWortTheRaidmotherWithSakashimaOnce() { + addCard(Zone.BATTLEFIELD, playerA, "Volcanic Island", 11); + // When Wort, the Raidmother enters the battlefield, put two 1/1 red and green Goblin Warrior creature tokens onto the battlefield. + // Each red or green instant or sorcery spell you cast has conspire. + // (As you cast the spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it and you may choose new targets for the copy.) + addCard(Zone.HAND, playerA, "Wort, the Raidmother"); + addCard(Zone.HAND, playerA, "Sakashima the Impostor"); + addCard(Zone.HAND, playerA, "Lightning Bolt"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wort, the Raidmother"); // {4}{R/G}{R/G} + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Sakashima the Impostor"); // {2}{U}{U} + setChoice(playerA, "Wort, the Raidmother"); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + setChoice(playerA, true); // use Conspire gained from Wort, the Raidmother + setChoice(playerA, false); // don't use Conspire gained from Sakashima the Imposter + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertPermanentCount(playerA, "Wort, the Raidmother", 1); + assertPermanentCount(playerA, "Sakashima the Impostor", 1); + assertLife(playerB, 20 - 3 - 3); + assertLife(playerA, 20); + assertGraveyardCount(playerA, "Lightning Bolt", 1); + } + + @Test + public void testWortTheRaidmotherWithSakashimaTwice() { + addCard(Zone.BATTLEFIELD, playerA, "Volcanic Island", 11); + // When Wort, the Raidmother enters the battlefield, put two 1/1 red and green Goblin Warrior creature tokens onto the battlefield. + // Each red or green instant or sorcery spell you cast has conspire. + // (As you cast the spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it and you may choose new targets for the copy.) + addCard(Zone.HAND, playerA, "Wort, the Raidmother"); + addCard(Zone.HAND, playerA, "Sakashima the Impostor"); + addCard(Zone.HAND, playerA, "Lightning Bolt"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wort, the Raidmother"); // {4}{R/G}{R/G} + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Sakashima the Impostor"); // {2}{U}{U} + setChoice(playerA, "Wort, the Raidmother"); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + setChoice(playerA, true); // use Conspire gained from Wort, the Raidmother + setChoice(playerA, true); // use Conspire gained from Sakashima the Imposter + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertPermanentCount(playerA, "Wort, the Raidmother", 1); + assertPermanentCount(playerA, "Sakashima the Impostor", 1); + assertLife(playerB, 20 - 3 - 3 - 3); + assertLife(playerA, 20); + assertGraveyardCount(playerA, "Lightning Bolt", 1); + } + @Test public void testConspire_User() { // Burn Trail deals 3 damage to any target. @@ -151,7 +263,7 @@ public class ConspireTest extends CardTestPlayerBase { assertAllCommandsUsed(); assertGraveyardCount(playerA, "Burn Trail", 1); - assertLife(playerB, 20 - 3 * 2); + assertLife(playerB, 20 - 3 - 3); assertTapped("Goblin Assailant", true); } @@ -176,7 +288,7 @@ public class ConspireTest extends CardTestPlayerBase { assertAllCommandsUsed(); assertGraveyardCount(playerA, "Burn Trail", 1); - assertLife(playerB, 20 - 3 * 2); + assertLife(playerB, 20 - 3 - 3); assertTapped("Goblin Assailant", true); } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/CrewTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/CrewTest.java index dfd0f46cba9..31ac01d196a 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/CrewTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/CrewTest.java @@ -6,6 +6,7 @@ import mage.constants.CardType; import mage.constants.PhaseStep; import mage.constants.SubType; import mage.constants.Zone; +import mage.counters.CounterType; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; @@ -15,23 +16,38 @@ import org.mage.test.serverside.base.CardTestPlayerBase; // http://magic.wizards.com/en/articles/archive/feature/kaladesh-mechanics-2016-09-02 public class CrewTest extends CardTestPlayerBase { + private static final String lion = "Silvercoat Lion"; + private static final String caravan = "Cultivator's Caravan"; + private static final String fanatic = "Speedway Fanatic"; + private static final String copter = "Smuggler's Copter"; + private static final String evacuation = "Evacuation"; + private static final String ox = "Giant Ox"; + private static final String plow = "Colossal Plow"; + private static final String kotori = "Kotori, Pilot Prodigy"; + private static final String crusher = "Irontread Crusher"; + private static final String mechanic = "Hotshot Mechanic"; + private static final String express = "Aradara Express"; + private static final String heart = "Heart of Kiran"; + private static final String jace = "Jace Beleren"; + @Test public void crewBasicTest() { // {T}: Add one mana of any color. // Crew 3 (Tap any number of creatures you control with total power 3 or more: This Vehicle becomes an artifact creature until end of turn.)"; - addCard(Zone.BATTLEFIELD, playerA, "Cultivator's Caravan", 1); + addCard(Zone.BATTLEFIELD, playerA, caravan, 1); - addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 2); + addCard(Zone.BATTLEFIELD, playerA, lion, 2); activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Crew 3"); - setChoice(playerA, "Silvercoat Lion^Silvercoat Lion"); + setChoice(playerA, lion + "^" + lion); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); + assertAllCommandsUsed(); - assertTappedCount("Silvercoat Lion", true, 2); - assertPowerToughness(playerA, "Cultivator's Caravan", 5, 5); - assertType("Cultivator's Caravan", CardType.CREATURE, SubType.VEHICLE); + assertTappedCount(lion, true, 2); + assertPowerToughness(playerA, caravan, 5, 5); + assertType(caravan, CardType.CREATURE, SubType.VEHICLE); } @Test @@ -39,56 +55,107 @@ public class CrewTest extends CardTestPlayerBase { // Flying // Whenever Smuggler's Copter attacks or blocks, you may draw a card. If you do, discard a card. // Crew 1 (Tap any number of creatures you control with total power 3 or more: This Vehicle becomes an artifact creature until end of turn.)"; - addCard(Zone.BATTLEFIELD, playerA, "Smuggler's Copter", 1); + addCard(Zone.BATTLEFIELD, playerA, copter, 1); - addCard(Zone.BATTLEFIELD, playerA, "Speedway Fanatic", 1); + addCard(Zone.BATTLEFIELD, playerA, fanatic, 1); activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Crew 1"); - setChoice(playerA, "Speedway Fanatic"); + setChoice(playerA, fanatic); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); + assertAllCommandsUsed(); - assertTappedCount("Speedway Fanatic", true, 1); - assertPowerToughness(playerA, "Smuggler's Copter", 3, 3); - assertAbility(playerA, "Smuggler's Copter", HasteAbility.getInstance(), true); - assertType("Smuggler's Copter", CardType.CREATURE, SubType.VEHICLE); + assertTappedCount(fanatic, true, 1); + assertPowerToughness(playerA, copter, 3, 3); + assertAbility(playerA, copter, HasteAbility.getInstance(), true); + assertType(copter, CardType.CREATURE, SubType.VEHICLE); } @Test public void testThatBouncingACrewedVehicleWillUncrewIt() { - addCard(Zone.BATTLEFIELD, playerA, "Smuggler's Copter", 1); - addCard(Zone.BATTLEFIELD, playerA, "Speedway Fanatic", 1); + addCard(Zone.BATTLEFIELD, playerA, copter, 1); + addCard(Zone.BATTLEFIELD, playerA, fanatic, 1); addCard(Zone.BATTLEFIELD, playerA, "Island", 7); - addCard(Zone.HAND, playerA, "Evacuation", 1); + addCard(Zone.HAND, playerA, evacuation, 1); activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Crew 1"); - setChoice(playerA, "Speedway Fanatic"); + setChoice(playerA, fanatic); // Return all creatures to there owners hands - castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Evacuation"); + castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, evacuation); // (Re)Cast Smugglers Copter - castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Smuggler's Copter"); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, copter); setStopAt(1, PhaseStep.END_TURN); execute(); + assertAllCommandsUsed(); // Only crewed vehicles have card type creature - assertNotType("Smuggler's Copter", CardType.CREATURE); + assertNotType(copter, CardType.CREATURE); } @Test public void testGiantOx() { - addCard(Zone.BATTLEFIELD, playerA, "Giant Ox"); - addCard(Zone.BATTLEFIELD, playerA, "Colossal Plow"); + addCard(Zone.BATTLEFIELD, playerA, ox); + addCard(Zone.BATTLEFIELD, playerA, plow); activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Crew"); setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); execute(); + assertAllCommandsUsed(); - assertTapped("Giant Ox", true); - assertType("Colossal Plow", CardType.CREATURE, true); + assertTapped(ox, true); + assertType(plow, CardType.CREATURE, true); + } + + @Test + public void testGrantedAbility() { + addCard(Zone.BATTLEFIELD, playerA, kotori); + addCard(Zone.BATTLEFIELD, playerA, crusher); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Crew 2"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertTapped(kotori, true); + assertType(crusher, CardType.ARTIFACT, true); + assertType(crusher, CardType.CREATURE, SubType.VEHICLE); + } + + @Test + public void testHotshotMechanic() { + addCard(Zone.BATTLEFIELD, playerA, mechanic); + addCard(Zone.BATTLEFIELD, playerA, express); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Crew 4"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertTapped(mechanic, true); + assertType(express, CardType.ARTIFACT, true); + assertType(express, CardType.CREATURE, SubType.VEHICLE); + } + + @Test + public void testHeartOfKiran() { + addCard(Zone.BATTLEFIELD, playerA, jace); + addCard(Zone.BATTLEFIELD, playerA, heart); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Crew 3"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertCounterCount(playerA, jace, CounterType.LOYALTY, 2); + assertType(heart, CardType.ARTIFACT, true); + assertType(heart, CardType.CREATURE, SubType.VEHICLE); } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DayNightTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DayNightTest.java new file mode 100644 index 00000000000..6525e7c0005 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DayNightTest.java @@ -0,0 +1,349 @@ +package org.mage.test.cards.abilities.keywords; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.game.permanent.Permanent; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author TheElk801 + */ +public class DayNightTest extends CardTestPlayerBase { + + private static final String ruffian = "Tavern Ruffian"; + private static final String smasher = "Tavern Smasher"; + private static final String moonmist = "Moonmist"; + private static final String outcasts = "Grizzled Outcasts"; + private static final String wantons = "Krallenhorde Wantons"; + private static final String immerwolf = "Immerwolf"; + private static final String bolt = "Lightning Bolt"; + private static final String curse = "Curse of Leeches"; + private static final String lurker = "Leeching Lurker"; + private static final String vandal = "Brimstone Vandal"; + + private void assertDayNight(boolean daytime) { + Assert.assertTrue("It should not be neither day nor night", currentGame.hasDayNight()); + Assert.assertTrue("It should be " + (daytime ? "day" : "night"), currentGame.checkDayNight(daytime)); + Assert.assertFalse("It should not be " + (daytime ? "night" : "day"), currentGame.checkDayNight(!daytime)); + } + + private void assertRuffianSmasher(boolean daytime) { + assertDayNight(daytime); + if (daytime) { + assertPowerToughness(playerA, ruffian, 2, 5); + assertPermanentCount(playerA, smasher, 0); + } else { + assertPermanentCount(playerA, ruffian, 0); + assertPowerToughness(playerA, smasher, 6, 5); + } + } + + private void setDayNight(int turn, PhaseStep phaseStep, boolean daytime) { + runCode("set game to " + (daytime ? "day" : "night"), turn, phaseStep, playerA, (i, p, game) -> game.setDaytime(daytime)); + } + + @Test + public void testRegularDay() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + addCard(Zone.HAND, playerA, ruffian); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, ruffian); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertRuffianSmasher(true); + } + + @Test + public void testNightbound() { + currentGame.setDaytime(false); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + addCard(Zone.HAND, playerA, ruffian); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, ruffian); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertRuffianSmasher(false); + } + + @Test + public void testDayToNightTransform() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + addCard(Zone.HAND, playerA, ruffian); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, ruffian); + setDayNight(1, PhaseStep.POSTCOMBAT_MAIN, false); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertRuffianSmasher(false); + } + + @Test + public void testNightToDayTransform() { + currentGame.setDaytime(false); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + addCard(Zone.HAND, playerA, ruffian); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, ruffian); + setDayNight(1, PhaseStep.POSTCOMBAT_MAIN, true); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertRuffianSmasher(true); + } + + @Test + public void testMoonmistFails() { + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + addCard(Zone.BATTLEFIELD, playerA, ruffian); + addCard(Zone.BATTLEFIELD, playerA, outcasts); + addCard(Zone.HAND, playerA, moonmist); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, moonmist); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertRuffianSmasher(true); + assertPermanentCount(playerA, outcasts, 0); + assertPowerToughness(playerA, wantons, 7, 7); + } + + @Test + public void testImmerwolfPreventsTransformation() { + currentGame.setDaytime(false); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + addCard(Zone.BATTLEFIELD, playerA, immerwolf); + addCard(Zone.HAND, playerA, ruffian); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, ruffian); + setDayNight(1, PhaseStep.POSTCOMBAT_MAIN, true); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertDayNight(true); + assertPowerToughness(playerA, smasher, 6 + 1, 5 + 1); + assertPermanentCount(playerA, ruffian, 0); + } + + @Test + public void testImmerwolfRemoved() { + currentGame.setDaytime(false); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); + addCard(Zone.BATTLEFIELD, playerA, immerwolf); + addCard(Zone.HAND, playerA, bolt); + addCard(Zone.HAND, playerA, ruffian); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, ruffian); + setDayNight(1, PhaseStep.BEGIN_COMBAT, true); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_COMBAT); + execute(); + + assertDayNight(true); + assertPowerToughness(playerA, smasher, 6 + 1, 5 + 1); + assertPermanentCount(playerA, ruffian, 0); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, bolt, immerwolf); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertRuffianSmasher(true); + } + + @Test + public void testNoSpellsBecomesNight() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + addCard(Zone.HAND, playerA, ruffian); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, ruffian); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertRuffianSmasher(true); + + setStopAt(2, PhaseStep.END_TURN); + execute(); + + assertRuffianSmasher(true); + + setStopAt(3, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertRuffianSmasher(false); + } + + @Test + public void testTwoSpellsBecomesDay() { + currentGame.setDaytime(false); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); + addCard(Zone.HAND, playerA, ruffian); + addCard(Zone.HAND, playerA, bolt); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, ruffian); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, playerB); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertLife(playerB, 20 - 3); + assertGraveyardCount(playerA, bolt, 1); + assertRuffianSmasher(false); + + setStopAt(2, PhaseStep.END_TURN); + execute(); + + assertRuffianSmasher(true); + + setStopAt(3, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertRuffianSmasher(false); + } + + @Test + public void testCurseOfLeechesRegular() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); + addCard(Zone.HAND, playerA, curse); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, curse, playerB); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertDayNight(true); + Permanent permanent = getPermanent(curse); + Assert.assertTrue("Curse is attached to playerB", permanent.isAttachedTo(playerB.getId())); + assertPermanentCount(playerA, lurker, 0); + } + + @Test + public void testCurseOfLeechesNightbound() { + currentGame.setDaytime(false); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); + addCard(Zone.HAND, playerA, curse); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, curse, playerB); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertDayNight(false); + assertPermanentCount(playerA, curse, 0); + assertPermanentCount(playerA, lurker, 1); + } + + @Test + public void testCurseOfLeechesDayToNight() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); + addCard(Zone.HAND, playerA, curse); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, curse, playerB); + setDayNight(1, PhaseStep.POSTCOMBAT_MAIN, false); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertDayNight(false); + assertPermanentCount(playerA, curse, 0); + assertPermanentCount(playerA, lurker, 1); + } + + @Test + public void testCurseOfLeechesNightToDay() { + currentGame.setDaytime(false); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); + addCard(Zone.HAND, playerA, curse); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, curse, playerB); + setChoice(playerA, playerB.getName()); + setDayNight(1, PhaseStep.POSTCOMBAT_MAIN, true); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertDayNight(true); + Permanent permanent = getPermanent(curse); + Assert.assertTrue("Curse is attached to playerB", permanent.isAttachedTo(playerB.getId())); + assertPermanentCount(playerA, lurker, 0); + } + + @Test + public void testBrimstoneVandalBecomeDay() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + addCard(Zone.HAND, playerA, vandal); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, vandal); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertDayNight(true); + assertLife(playerB, 20); + } + + @Test + public void testBrimstoneVandalTrigger() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + addCard(Zone.HAND, playerA, bolt, 2); + addCard(Zone.HAND, playerA, vandal); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, vandal); + + setStrictChooseMode(true); + setStopAt(3, PhaseStep.UPKEEP); + execute(); + + assertDayNight(false); + assertLife(playerB, 20 - 1); + + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, playerB); + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, playerB); + + setStopAt(4, PhaseStep.UPKEEP); + execute(); + assertAllCommandsUsed(); + + assertDayNight(true); + assertLife(playerB, 20 - 1 - 3 - 3 - 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DecayedTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DecayedTest.java new file mode 100644 index 00000000000..63f082fac31 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DecayedTest.java @@ -0,0 +1,51 @@ +package org.mage.test.cards.abilities.keywords; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class DecayedTest extends CardTestPlayerBase { + + @Test + public void decayedToken() { + addCard(Zone.HAND, playerA, "Falcon Abomination", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); + setStrictChooseMode(true); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Falcon Abomination"); + attack(3, playerA, "Zombie"); + setStopAt(3, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertAllCommandsUsed(); + assertPermanentCount(playerA, "Falcon Abomination", 1); + assertPermanentCount(playerA, "Zombie", 0); + } + + @Test + public void decayedPermanent() { + addCard(Zone.BATTLEFIELD, playerA, "Gisa, Glorious Resurrector", 1); + addCard(Zone.BATTLEFIELD, playerB, "Grizzly Bears", 1); + addCard(Zone.HAND, playerA, "Doom Blade", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + setStrictChooseMode(true); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Doom Blade"); + addTarget(playerA, "Grizzly Bears"); + // Gisa - "If a creature an opponent controls would die, exile it instead." + checkExileCount("Gisa Exile Ability", 1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Grizzly Bears", 1); + + attack(5, playerA, "Grizzly Bears"); + setStopAt(5, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertAllCommandsUsed(); + assertPermanentCount(playerA, "Gisa, Glorious Resurrector", 1); + assertPermanentCount(playerA, "Grizzly Bears", 0); + assertPermanentCount(playerB, "Grizzly Bears", 0); + assertExileCount("Grizzly Bears", 0); + // Grizzly Bears should sacrifice after combat and go to playerB's graveyard + assertGraveyardCount(playerB, "Grizzly Bears", 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DisturbTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DisturbTest.java new file mode 100644 index 00000000000..ca16f998219 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DisturbTest.java @@ -0,0 +1,194 @@ +package org.mage.test.cards.abilities.keywords; + +import mage.constants.CardType; +import mage.constants.PhaseStep; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.stack.Spell; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author JayDi85 + */ +public class DisturbTest extends CardTestPlayerBase { + + @Test + public void test_SpellAttributesOnStack() { + // Disturb {1}{U} + // Hook-Haunt Drifter + addCard(Zone.GRAVEYARD, playerA, "Baithook Angler", 1); // {1}{U} + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + // + addCard(Zone.HAND, playerA, "Lightning Bolt", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + + checkPlayableAbility("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Hook-Haunt Drifter with Disturb", true); + + // cast with disturb + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U}", 2); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Hook-Haunt Drifter with Disturb"); + checkStackObject("on stack", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Hook-Haunt Drifter with Disturb", 1); + runCode("check stack", 1, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> { + // Stack must contain another card side, so spell/card characteristics must be diff from main side (only mana value is same) + // rules: + // When you cast a spell using a card's disturb ability, the card is put onto the stack with its + // back face up. The resulting spell has all the characteristics of that face. + Spell spell = (Spell) game.getStack().getFirst(); + Assert.assertEquals("Hook-Haunt Drifter", spell.getName()); + Assert.assertEquals(1, spell.getCardType(game).size()); + Assert.assertEquals(CardType.CREATURE, spell.getCardType(game).get(0)); + Assert.assertEquals(1, spell.getSubtype(game).size()); + Assert.assertEquals(SubType.SPIRIT, spell.getSubtype(game).get(0)); + Assert.assertEquals(1, spell.getPower().getValue()); + Assert.assertEquals(2, spell.getToughness().getValue()); + Assert.assertEquals("U", spell.getColor(game).toString()); + + // rules: + // The mana value of a spell cast using disturb is determined by the mana cost on the + // front face of the card, no matter what the total cost to cast the spell was. + Assert.assertEquals(2, spell.getManaValue()); // {1}{U} + + Assert.assertEquals("{1}{U}", spell.getSpellAbility().getManaCosts().getText()); + }); + + // must be transformed + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Hook-Haunt Drifter", 1); + checkPT("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Hook-Haunt Drifter", 1, 2); + checkSubType("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Hook-Haunt Drifter", SubType.SPIRIT, true); + + // must be exiled on die + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Hook-Haunt Drifter"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkExileCount("after die", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Hook-Haunt Drifter", 0); + checkExileCount("after die", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Baithook Angler", 1); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_CostModification_CanPlay() { + // rules: + // To determine the total cost of a spell, start with the mana cost or alternative cost + // (such as a disturb cost) you're paying, add any cost increases, then apply any cost + // reductions. The mana value of a spell cast using disturb is determined by the mana cost on + // the front face of the card, no matter what the total cost to cast the spell was. (This is + // a special rule that applies only to transforming double faced-cards, including ones with + // disturb.) + + // Disturb {1}{U} + // Hook-Haunt Drifter + addCard(Zone.GRAVEYARD, playerA, "Baithook Angler", 1); // {1}{U} + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + // + addCustomEffect_SpellCostModification(playerA, -1); + + checkPlayableAbility("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Hook-Haunt Drifter with Disturb", true); + + // cast with disturb + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Hook-Haunt Drifter with Disturb"); + runCode("check stack", 1, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> { + Spell spell = (Spell) game.getStack().getFirst(); + Assert.assertEquals("mana value must be from main side", 2, spell.getManaValue()); + Assert.assertEquals("mana cost to pay must be modified", "{U}", spell.getSpellAbility().getManaCostsToPay().getText()); + }); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Hook-Haunt Drifter", 1); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_CostModification_CanNotPlay() { + // Disturb {1}{U} + // Hook-Haunt Drifter + addCard(Zone.GRAVEYARD, playerA, "Baithook Angler", 1); // {1}{U} + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + // + addCustomEffect_SpellCostModification(playerA, 1); + + checkPlayableAbility("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Hook-Haunt Drifter with Disturb", false); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_CopySpell() { + // rules: + // If you copy a permanent spell cast this way (perhaps with a card like Double Major), the copy becomes + // a token that's a copy of the card's back face, even though it isn't itself a double-faced card. + + // Disturb {1}{U} + // Hook-Haunt Drifter + addCard(Zone.GRAVEYARD, playerA, "Baithook Angler", 1); // {1}{U} + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + // + // Copy target creature spell you control, except it isn't legendary if the spell is legendary. + addCard(Zone.HAND, playerA, "Double Major", 1); // {G}{U} + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + + checkPlayableAbility("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Hook-Haunt Drifter with Disturb", true); + + // cast with disturb + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U}", 2); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Hook-Haunt Drifter with Disturb"); + // prepare copy of spell + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Double Major", "Hook-Haunt Drifter", "Hook-Haunt Drifter"); + checkStackSize("before copy spell", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 2); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA, true); + checkStackSize("after copy spell", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 2); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Hook-Haunt Drifter", 2); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_Counter() { + // rules: + // The back face of each card with disturb has an ability that instructs its controller to exile + // if it would be put into a graveyard from anywhere. This includes going to the graveyard from the + // stack, so if the spell is countered after you cast it using the disturb ability, it will + // be put into exile. + + // Disturb {1}{U} + // Hook-Haunt Drifter + addCard(Zone.GRAVEYARD, playerA, "Baithook Angler", 1); // {1}{U} + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + // + // Counter target spell. + addCard(Zone.HAND, playerA, "Counterspell", 1); // {U}{U} + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + + checkPlayableAbility("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Hook-Haunt Drifter with Disturb", true); + + // cast with disturb + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Hook-Haunt Drifter with Disturb"); + // counter it + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Counterspell", "Hook-Haunt Drifter", "Hook-Haunt Drifter"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + checkPermanentCount("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Hook-Haunt Drifter", 0); + checkExileCount("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Baithook Angler", 1); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/GoadTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/GoadTest.java index e0729305b41..ed3f413b762 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/GoadTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/GoadTest.java @@ -1,6 +1,5 @@ package org.mage.test.cards.abilities.keywords; -import java.io.FileNotFoundException; import mage.constants.MultiplayerAttackOption; import mage.constants.PhaseStep; import mage.constants.RangeOfInfluence; @@ -10,16 +9,29 @@ import mage.game.Game; import mage.game.GameException; import mage.game.mulligan.MulliganType; import mage.game.permanent.Permanent; +import mage.players.Player; import org.junit.Assert; import org.junit.Test; +import org.mage.test.player.TestPlayer; import org.mage.test.serverside.base.CardTestMultiPlayerBase; +import java.io.FileNotFoundException; +import java.util.Arrays; +import java.util.UUID; +import java.util.stream.Collectors; + /** - * * @author LevelX2 */ public class GoadTest extends CardTestMultiPlayerBase { + private static final String marisi = "Marisi, Breaker of the Coil"; + private static final String griffin = "Abbey Griffin"; + private static final String ray = "Ray of Command"; + private static final String homunculus = "Jeering Homunculus"; + private static final String lion = "Silvercoat Lion"; + private static final String archon = "Blazing Archon"; + @Override protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException { Game game = new FreeForAll(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ALL, MulliganType.GAME_DEFAULT.getMulligan(0), 40); @@ -31,85 +43,155 @@ public class GoadTest extends CardTestMultiPlayerBase { return game; } + private void assertAttacking(String attacker, TestPlayer... players) { + Assert.assertTrue("At least one player should be provided", players.length > 0); + Permanent permanent = getPermanent(attacker); + Assert.assertTrue("Creature should be tapped", permanent.isTapped()); + Assert.assertTrue("Creature should be attacking", permanent.isAttacking()); + UUID defenderId = currentGame.getCombat().getDefenderId(permanent.getId()); + Assert.assertTrue( + "Creature should be attacking one the following players: " + + Arrays + .stream(players) + .map(Player::getName) + .reduce((a, b) -> a + ", " + b) + .orElse(""), + Arrays.stream(players) + .map(TestPlayer::getId) + .anyMatch(defenderId::equals) + ); + } + + private void assertGoaded(String attacker, TestPlayer... players) { + Assert.assertTrue("At least one player should be provided", players.length > 0); + Permanent permanent = getPermanent(attacker); + Assert.assertEquals( + "Creature should be goaded by " + + Arrays + .stream(players) + .map(Player::getName) + .reduce((a, b) -> a + ", " + b).orElse(""), + permanent.getGoadingPlayers(), + Arrays.stream(players) + .map(TestPlayer::getId) + .collect(Collectors.toSet()) + ); + } + @Test - public void goadWithOwnedCreatureTest() { - // Your opponents can't cast spells during combat. - // Whenever a creature you control deals combat damage to a player, goad each creature that player controls - // (Until your next turn, that creature attacks each combat if able and attacks a player other than you if able.) - addCard(Zone.BATTLEFIELD, playerD, "Marisi, Breaker of the Coil", 1); // Creature 5/4 + public void testCantAttackGoadingPlayer() { + addCard(Zone.HAND, playerA, homunculus); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + addCard(Zone.BATTLEFIELD, playerD, lion); - addCard(Zone.BATTLEFIELD, playerC, "Abbey Griffin", 3); // Creature 2/2 + addTarget(playerA, lion); + setChoice(playerA, "Yes"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, homunculus); - attack(2, playerD, "Marisi, Breaker of the Coil", playerC); - - setStopAt(3, PhaseStep.BEGIN_COMBAT); - - setStrictChooseMode(true); + setStopAt(2, PhaseStep.DECLARE_BLOCKERS); execute(); assertAllCommandsUsed(); - Permanent griffinPermanent = getPermanent("Abbey Griffin"); - - Assert.assertFalse("Griffin can attack playerD but should not be able", - griffinPermanent.canAttack(playerD.getId(), currentGame)); - Assert.assertTrue("Griffin can't attack playerA but should be able", - griffinPermanent.canAttack(playerA.getId(), currentGame)); - Assert.assertTrue("Griffin can't attack playerB but should be able", - griffinPermanent.canAttack(playerB.getId(), currentGame)); - - assertLife(playerC, 35); - assertLife(playerD, 40); // player D can not be attacked from C because the creatures are goaded - assertLife(playerA, 40); - assertLife(playerB, 40); - + assertGoaded(lion, playerA); + assertAttacking(lion, playerB, playerC); } - /** - * In a game of commander, my opponent gained control of Marisi, Breaker of - * Coils (until end of turn) and did combat damage to another player. This - * caused the creatures damaged by Marisi's controller to be goaded. - * However, when the goaded creatures went to attack, they could not attack - * me but could attack the (former) controller of Marisi. - */ @Test - public void goadWithNotOwnedCreatureTest() { - // Your opponents can't cast spells during combat. - // Whenever a creature you control deals combat damage to a player, goad each creature that player controls - // (Until your next turn, that creature attacks each combat if able and attacks a player other than you if able.) - addCard(Zone.BATTLEFIELD, playerA, "Marisi, Breaker of the Coil", 1); // Creature 5/4 + public void testCanOnlyAttackOnePlayer() { + addCard(Zone.HAND, playerA, homunculus); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + addCard(Zone.BATTLEFIELD, playerD, lion); + addCard(Zone.BATTLEFIELD, playerB, archon); - // Untap target creature an opponent controls and gain control of it until end of turn. - // That creature gains haste until end of turn. - // When you lose control of the creature, tap it. - addCard(Zone.HAND, playerD, "Ray of Command"); // Instant {3}{U} - addCard(Zone.BATTLEFIELD, playerD, "Island", 4); + addTarget(playerA, lion); + setChoice(playerA, "Yes"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, homunculus); - addCard(Zone.BATTLEFIELD, playerC, "Silvercoat Lion", 1); // Creature 2/2 - - castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerD, "Ray of Command", "Marisi, Breaker of the Coil"); - - attack(2, playerD, "Marisi, Breaker of the Coil", playerC); - - setStopAt(3, PhaseStep.BEGIN_COMBAT); - setStrictChooseMode(true); + setStopAt(2, PhaseStep.DECLARE_BLOCKERS); execute(); - assertAllCommandsUsed(); - assertGraveyardCount(playerD, "Ray of Command", 1); - assertPermanentCount(playerA, "Marisi, Breaker of the Coil", 1); - - Permanent lion = getPermanent("Silvercoat Lion", playerC); - - Assert.assertFalse("Silvercoat lion shouldn't be able to attack player D but can", lion.canAttack(playerD.getId(), currentGame)); - Assert.assertTrue("Silvercoat lion should be able to attack player A but can't", lion.canAttack(playerA.getId(), currentGame)); - Assert.assertTrue("Silvercoat lion should be able to attack player B but can't", lion.canAttack(playerB.getId(), currentGame)); - - assertLife(playerD, 40); - assertLife(playerC, 35); - assertLife(playerA, 40); - assertLife(playerB, 40); - + assertGoaded(lion, playerA); + assertAttacking(lion, playerC); } + @Test + public void testMustAttackGoader() { + addCard(Zone.HAND, playerA, homunculus); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + addCard(Zone.BATTLEFIELD, playerD, lion); + addCard(Zone.BATTLEFIELD, playerB, archon); + addCard(Zone.BATTLEFIELD, playerC, archon); + + addTarget(playerA, lion); + setChoice(playerA, "Yes"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, homunculus); + + setStopAt(2, PhaseStep.DECLARE_BLOCKERS); + execute(); + assertAllCommandsUsed(); + + assertGoaded(lion, playerA); + assertAttacking(lion, playerA); + } + + @Test + public void testMultipleGoad() { + addCard(Zone.HAND, playerA, homunculus); + addCard(Zone.HAND, playerD, homunculus); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + addCard(Zone.BATTLEFIELD, playerD, "Island", 2); + addCard(Zone.BATTLEFIELD, playerC, lion); + + addTarget(playerA, lion); + setChoice(playerA, "Yes"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, homunculus); + + addTarget(playerD, lion); + setChoice(playerD, "Yes"); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerD, homunculus); + + setStopAt(3, PhaseStep.DECLARE_BLOCKERS); + execute(); + assertAllCommandsUsed(); + + assertGoaded(lion, playerA, playerD); + assertAttacking(lion, playerB); + } + + @Test + public void testMultipleGoadRestriction() { + addCard(Zone.HAND, playerA, homunculus); + addCard(Zone.HAND, playerD, homunculus); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + addCard(Zone.BATTLEFIELD, playerD, "Island", 2); + addCard(Zone.BATTLEFIELD, playerB, archon); + addCard(Zone.BATTLEFIELD, playerC, lion); + + addTarget(playerA, lion); + setChoice(playerA, "Yes"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, homunculus); + + addTarget(playerD, lion); + setChoice(playerD, "Yes"); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerD, homunculus); + + setStopAt(3, PhaseStep.DECLARE_BLOCKERS); + execute(); + assertAllCommandsUsed(); + + assertGoaded(lion, playerA, playerD); + assertAttacking(lion, playerA, playerD); + } + + @Test + public void testRegularCombatRequirement() { + addCard(Zone.BATTLEFIELD, playerA, "Berserkers of Blood Ridge"); + + setStopAt(1, PhaseStep.DECLARE_BLOCKERS); + execute(); + assertAllCommandsUsed(); + + assertAttacking("Berserkers of Blood Ridge", playerB, playerC, playerD); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/TransformTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/TransformTest.java index 5329f99df1a..9ef18543e73 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/TransformTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/TransformTest.java @@ -1,15 +1,13 @@ package org.mage.test.cards.abilities.keywords; +import mage.constants.CardType; import mage.constants.PhaseStep; import mage.constants.Zone; import mage.counters.CounterType; -import mage.game.permanent.Permanent; -import org.junit.Assert; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; /** - * * @author LevelX2 */ public class TransformTest extends CardTestPlayerBase { @@ -133,7 +131,7 @@ public class TransformTest extends CardTestPlayerBase { * 4G Creature - Human Shaman Whenever a permanent you control transforms * into a non-Human creature, put a 2/2 green Wolf creature token onto the * battlefield. - * + *

* Reported bug: "It appears to trigger either when a non-human creature * transforms OR when a creature transforms from a non-human into a human * (as in when a werewolf flips back to the sun side), rather than when a @@ -171,24 +169,51 @@ public class TransformTest extends CardTestPlayerBase { addCard(Zone.HAND, playerA, "Startled Awake"); // SORCERY {2}{U}{U}" addCard(Zone.BATTLEFIELD, playerA, "Island", 9); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Startled Awake"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Startled Awake", playerB); activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}{U}{U}"); + + setStrictChooseMode(true); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); + assertAllCommandsUsed(); assertGraveyardCount(playerB, 13); assertGraveyardCount(playerA, "Startled Awake", 0); assertPermanentCount(playerA, "Persistent Nightmare", 1); // Night-side card of Startled Awake - Permanent nightmare = getPermanent("Persistent Nightmare", playerA); - Assert.assertTrue("Has to have creature card type", nightmare.isCreature(currentGame)); - Assert.assertFalse("Has not to have sorcery card type", nightmare.isSorcery(currentGame)); + assertType("Persistent Nightmare", CardType.CREATURE, true); + assertType("Persistent Nightmare", CardType.SORCERY, false); + } + + @Test + public void testStartledAwakeMoonmist() { + addCard(Zone.HAND, playerA, "Startled Awake"); + addCard(Zone.HAND, playerA, "Moonmist"); + addCard(Zone.BATTLEFIELD, playerA, "Tropical Island", 11); + addCard(Zone.BATTLEFIELD, playerA, "Maskwood Nexus"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Startled Awake", playerB); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}{U}{U}"); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Moonmist"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerB, 13); + assertGraveyardCount(playerA, "Startled Awake", 0); + assertPermanentCount(playerA, "Persistent Nightmare", 1); // Night-side card of Startled Awake + assertType("Persistent Nightmare", CardType.CREATURE, true); + assertType("Persistent Nightmare", CardType.SORCERY, false); } /** * When copy token of Lambholt Pacifist transforms with "its transform * ability", I see below error. Then rollback. - * + *

* 701.25a Only permanents represented by double-faced cards can transform. * (See rule 711, “Double-Faced Cards.”) If a spell or ability instructs a * player to transform any permanent that isn‘t represented by a @@ -221,7 +246,7 @@ public class TransformTest extends CardTestPlayerBase { /** * Mirror Mockery copies the front face of a Transformed card rather than * the current face. - * + *

* It's worth pointing out that my opponent cast Mirror Mockery the previous * turn - after it had transformed. I should have included the part of the * log that showed that Mirror Mockery was applied to the Unimpeded @@ -280,11 +305,13 @@ public class TransformTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerB, "Wastes", 3); castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Silvercoat Lion"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, true); - activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{2}{C}", "Archangel Avacyn", "Whenever a non-Angel creature you control dies"); + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{2}{C}", "Archangel Avacyn"); setStopAt(3, PhaseStep.PRECOMBAT_MAIN); execute(); + assertAllCommandsUsed(); assertGraveyardCount(playerA, "Lightning Bolt", 1); assertGraveyardCount(playerA, "Silvercoat Lion", 1); @@ -317,9 +344,9 @@ public class TransformTest extends CardTestPlayerBase { * was on stack, my opponent used Displacer's ability targeting Huntmaster. * That ability resolved and Huntmaster still transformed like it never left * the battlefield. - * + *

* http://www.slightlymagic.net/forum/viewtopic.php?f=70&t=20014&p=210533#p210513 - * + *

* The transform effect on the stack should fizzle. The card brought back * from Exile should be a new object unless I am interpreting the rules * incorrectly. The returned permanent uses the same GUID. @@ -356,12 +383,13 @@ public class TransformTest extends CardTestPlayerBase { assertPermanentCount(playerA, "Ravager of the Fells", 0); assertPermanentCount(playerA, "Huntmaster of the Fells", 1); - assertPowerToughness(playerA, "Huntmaster of the Fells", 2, 2); + assertPowerToughness(playerA, "Huntmaster of the Fells", 2, 2); assertTappedCount("Plains", true, 2); assertTappedCount("Wastes", true, 1); } - @Test + + @Test public void testHuntmasterTransformed() { // Whenever this creature enters the battlefield or transforms into Huntmaster of the Fells, create a 2/2 green Wolf creature token and you gain 2 life. // At the beginning of each upkeep, if no spells were cast last turn, transform Huntmaster of the Fells. @@ -387,15 +415,16 @@ public class TransformTest extends CardTestPlayerBase { assertPermanentCount(playerA, "Ravager of the Fells", 0); assertPermanentCount(playerA, "Huntmaster of the Fells", 1); assertPowerToughness(playerA, "Huntmaster of the Fells", 2, 2); - + } + /** * Having cast Phantasmal Image copying my opponent's flipped Thing in the * Ice, I was left with a 0/4 Awoken Horror. - * + *

* https://github.com/magefree/mage/issues/5893 - * + *

* The transform effect on the stack should fizzle. The card brought back * from Exile should be a new object unless I am interpreting the rules * incorrectly. The returned permanent uses the same GUID. @@ -440,4 +469,32 @@ public class TransformTest extends CardTestPlayerBase { } + @Test + public void testMoonmistDelver() { + addCard(Zone.BATTLEFIELD, playerA, "Island"); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); + addCard(Zone.HAND, playerA, "Delver of Secrets"); + addCard(Zone.HAND, playerA, "Moonmist", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Delver of Secrets"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Moonmist"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Delver of Secrets", 0); + assertPermanentCount(playerA, "Insectile Aberration", 1); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Moonmist"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Delver of Secrets", 1); + assertPermanentCount(playerA, "Insectile Aberration", 0); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/WardTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/WardTest.java new file mode 100644 index 00000000000..b21b86b41e2 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/WardTest.java @@ -0,0 +1,35 @@ +package org.mage.test.cards.abilities.keywords; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author weirddan455 + */ +public class WardTest extends CardTestPlayerBase { + + @Test + public void wardMultipleAbilities() { + addCard(Zone.HAND, playerA, "Solitude"); // https://github.com/magefree/mage/issues/8378 Test that ward counters correct ability + addCard(Zone.HAND, playerA, "Healer's Hawk"); // Card to pitch to Solitude + addCard(Zone.BATTLEFIELD, playerB, "Waterfall Aerialist"); // Flying, Ward 2 + setStrictChooseMode(true); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Solitude"); + setChoice(playerA, "Yes"); // Use alternate casting cost + setChoice(playerA, "Healer's Hawk"); + setChoice(playerA, "When {this} enters the battlefield, exile up to one other target creature"); // Put exile trigger on the stack first (evoke trigger will resolve first) + addTarget(playerA, "Waterfall Aerialist"); + setChoice(playerA, "No"); // Do not pay Ward cost + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertExileCount("Healer's Hawk", 1); + assertGraveyardCount(playerA, "Solitude", 1); + assertPermanentCount(playerB, "Waterfall Aerialist", 1); + assertAllCommandsUsed(); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/SparkDoubleTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/SparkDoubleTest.java index dbdebe836fd..300c0fd0bad 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/SparkDoubleTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/SparkDoubleTest.java @@ -68,7 +68,6 @@ public class SparkDoubleTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Spark Double"); setChoice(playerA, true); setChoice(playerA, "Ajani, the Greathearted"); - setChoice(playerA, "Ajani, the Greathearted"); // two etb effects (own + copy) setStrictChooseMode(true); setStopAt(1, PhaseStep.BEGIN_COMBAT); @@ -120,7 +119,6 @@ public class SparkDoubleTest extends CardTestPlayerBase { castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Spark Double"); setChoice(playerA, true); setChoice(playerA, "Gideon, Ally of Zendikar"); - setChoice(playerA, "Gideon, Ally of Zendikar"); // two etb effects (own + copy) setStrictChooseMode(true); setStopAt(1, PhaseStep.END_TURN); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/cost/modaldoublefaces/ModalDoubleFacesCardsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modaldoublefaces/ModalDoubleFacesCardsTest.java index e917db36e11..5102c3debc7 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/cost/modaldoublefaces/ModalDoubleFacesCardsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modaldoublefaces/ModalDoubleFacesCardsTest.java @@ -1,5 +1,6 @@ package org.mage.test.cards.cost.modaldoublefaces; +import mage.abilities.keyword.HasteAbility; import mage.cards.Card; import mage.cards.ModalDoubleFacesCard; import mage.constants.PhaseStep; @@ -9,6 +10,7 @@ import mage.game.permanent.PermanentCard; import mage.util.CardUtil; import mage.util.ManaUtil; import org.junit.Assert; +import org.junit.Ignore; import org.junit.Test; import org.mage.test.player.TestPlayer; import org.mage.test.serverside.base.CardTestPlayerBase; @@ -863,4 +865,139 @@ public class ModalDoubleFacesCardsTest extends CardTestPlayerBase { assertPermanentCount(playerA, "The Omenkeel", 1); } + + @Test + public void test_Copy_AsSpell() { + addCard(Zone.HAND, playerA, "Akoum Warrior", 1); // {5}{R} + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 6); + // + // Copy target creature spell you control, except it isn't legendary if the spell is legendary. + addCard(Zone.HAND, playerA, "Double Major", 1); // {G}{U} + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + + // cast mdf card + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}", 6); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior"); + // prepare copy of spell + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Double Major", "Akoum Warrior", "Akoum Warrior"); + checkStackSize("before copy spell", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 2); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA, true); + checkStackSize("after copy spell", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 2); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior", 2); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_Copy_AsCloneFromPermanent() { + addCard(Zone.HAND, playerA, "Akoum Warrior", 1); // {5}{R} + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 6); + // + // You may have Clone enter the battlefield as a copy of any creature on the battlefield. + addCard(Zone.HAND, playerA, "Clone", 1); // {3}{U} + addCard(Zone.BATTLEFIELD, playerA, "Island", 4); + + // cast mdf card + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}", 6); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + // copy permanent + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Clone"); + setChoice(playerA, true); // use copy + setChoice(playerA, "Akoum Warrior"); // copy source + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior", 2); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + @Ignore // TODO: Zam Wesell must be reworked to use on cast + etb abilities + public void test_Copy_AsCloneFromCard_ZamWesell() { + // When you cast Zam Wesell, target opponent reveals their hand. You may choose a creature card from it + // and have Zam Wesell enter the battlefield as a copy of that creature card. + addCard(Zone.HAND, playerA, "Zam Wesell"); // {2}{U}{U} + addCard(Zone.BATTLEFIELD, playerA, "Island", 4); + // + addCard(Zone.HAND, playerB, "Akoum Warrior", 1); + + // cast as copy of mdf card + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Zam Wesell"); + addTarget(playerA, playerB); // target opponent + setChoice(playerA, "Akoum Warrior"); // creature card to copy + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior", 2); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_Copy_AsCloneFromCard_ValkiGodOfLies() { + // When Valki enters the battlefield, each opponent reveals their hand. For each opponent, + // exile a creature card they revealed this way until Valki leaves the battlefield. + // X: Choose a creature card exiled with Valki with converted mana cost X. Valki becomes a copy of that card. + addCard(Zone.HAND, playerA, "Valki, God of Lies"); // {1}{B} + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2 + 3); // 3 for X + // + addCard(Zone.HAND, playerB, "Birgi, God of Storytelling", 1); // {2}{R} + + // prepare valki + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Valki, God of Lies"); + setChoice(playerA, "Birgi, God of Storytelling"); // exile + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + // copy exiled card + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{X}:"); + setChoice(playerA, "X=3"); + setChoice(playerA, "Birgi, God of Storytelling"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Valki, God of Lies", 0); + assertPermanentCount(playerA, "Birgi, God of Storytelling", 1); + } + + @Test + public void test_FindMovedPermanentByCard() { + // original problem: you must be able to find a card after move it to battlefield + // https://github.com/magefree/mage/issues/8474 + + // {R}: You may put a creature card from your hand onto the battlefield. That creature gains haste. + // Sacrifice the creature at the beginning of the next end step. + addCard(Zone.BATTLEFIELD, playerA, "Sneak Attack"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + // + addCard(Zone.HAND, playerA, "Akoum Warrior", 1); + + // move + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{R}:"); + setChoice(playerA, true); // yes, activate + setChoice(playerA, "Akoum Warrior"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("after move", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior", 1); + checkAbility("must have haste after etb", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior", HasteAbility.class, true); + + // must sacrifice + checkPermanentCount("after sacrifice", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior", 0); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } } \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/NissaStewardOfElementsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/NissaStewardOfElementsTest.java new file mode 100644 index 00000000000..478ab343a91 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/NissaStewardOfElementsTest.java @@ -0,0 +1,54 @@ +package org.mage.test.cards.planeswalker; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author TheElk801 + */ +public class NissaStewardOfElementsTest extends CardTestPlayerBase { + + private static final String nissa = "Nissa, Steward of Elements"; + + private void doTest(int xValue) { + addCard(Zone.BATTLEFIELD, playerA, "Tropical Island", 2 + xValue); + addCard(Zone.HAND, playerA, nissa); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, nissa); + setChoice(playerA, "X=" + xValue); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + if (xValue == 0) { + assertGraveyardCount(playerA, nissa, 1); + } else { + assertCounterCount(playerA, nissa, CounterType.LOYALTY, xValue); + } + } + + @Test + public void test0Counters() { + doTest(0); + } + + @Test + public void test1Counter() { + doTest(1); + } + + @Test + public void test2Counters() { + doTest(2); + } + + @Test + public void test10Counters() { + doTest(10); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/afc/BeltOfGiantStrengthTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/afc/BeltOfGiantStrengthTest.java index 38262ef5d75..4fd9ab347b4 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/afc/BeltOfGiantStrengthTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/afc/BeltOfGiantStrengthTest.java @@ -42,7 +42,6 @@ public class BeltOfGiantStrengthTest extends CardTestPlayerBase { ); } - @Ignore // currently failing, need to fix @Test public void testWithoutManaAvailable() { addCard(Zone.BATTLEFIELD, playerA, belt); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/afc/DeathTyrantTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/afc/DeathTyrantTest.java new file mode 100644 index 00000000000..fb5528f6024 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/afc/DeathTyrantTest.java @@ -0,0 +1,45 @@ +package org.mage.test.cards.single.afc; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class DeathTyrantTest extends CardTestPlayerBase { + + @Test + public void attackerDies() { + addCard(Zone.BATTLEFIELD, playerA, "Death Tyrant", 1); + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1); + addCard(Zone.BATTLEFIELD, playerB, "Hill Giant", 1); + + attack(1, playerA, "Grizzly Bears"); + block(1, playerB, "Hill Giant", "Grizzly Bears"); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertGraveyardCount(playerA, "Grizzly Bears", 1); + assertPermanentCount(playerA, "Zombie", 1); + assertPermanentCount(playerA, "Death Tyrant", 1); + assertPermanentCount(playerB, "Hill Giant", 1); + } + + @Test + public void blockerDies() { + addCard(Zone.BATTLEFIELD, playerA, "Death Tyrant", 1); + addCard(Zone.BATTLEFIELD, playerA, "Hill Giant", 1); + addCard(Zone.BATTLEFIELD, playerB, "Grizzly Bears", 1); + + attack(1, playerA, "Hill Giant"); + block(1, playerB, "Grizzly Bears", "Hill Giant"); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertGraveyardCount(playerB, "Grizzly Bears", 1); + assertPermanentCount(playerA, "Zombie", 1); + assertPermanentCount(playerA, "Death Tyrant", 1); + assertPermanentCount(playerA, "Hill Giant", 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/bok/ShireiShizosCaretakerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/bok/ShireiShizosCaretakerTest.java new file mode 100644 index 00000000000..294fe1f6141 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/bok/ShireiShizosCaretakerTest.java @@ -0,0 +1,73 @@ +package org.mage.test.cards.single.bok; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author TheElk801 + */ +public class ShireiShizosCaretakerTest extends CardTestPlayerBase { + private static final String shirei = "Shirei, Shizo's Caretaker"; + private static final String rats = "Muck Rats"; + private static final String murder = "Murder"; + private static final String blink = "Momentary Blink"; + + @Test + public void testRegular() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); + addCard(Zone.BATTLEFIELD, playerA, shirei); + addCard(Zone.BATTLEFIELD, playerA, rats); + addCard(Zone.HAND, playerA, murder); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, murder, rats); + setChoice(playerA, true); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + assertPermanentCount(playerA, shirei, 1); + assertPermanentCount(playerA, rats, 1); + } + + @Test + public void testLeftBattlefield() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 6); + addCard(Zone.BATTLEFIELD, playerA, shirei); + addCard(Zone.BATTLEFIELD, playerA, rats); + addCard(Zone.HAND, playerA, murder, 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, murder, rats); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, murder, shirei); + setChoice(playerA, true); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + assertPermanentCount(playerA, shirei, 0); + assertPermanentCount(playerA, rats, 0); + } + + @Test + public void testBlinked() { + addCard(Zone.BATTLEFIELD, playerA, "Scrubland", 5); + addCard(Zone.BATTLEFIELD, playerA, shirei); + addCard(Zone.BATTLEFIELD, playerA, rats); + addCard(Zone.HAND, playerA, murder); + addCard(Zone.HAND, playerA, blink); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, murder, rats); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, blink, shirei); + setChoice(playerA, true); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + assertPermanentCount(playerA, shirei, 1); + assertPermanentCount(playerA, rats, 0); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/chk/Test_HorobiDeathsWail.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/chk/HorobiDeathsWailTest.java similarity index 93% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/chk/Test_HorobiDeathsWail.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/chk/HorobiDeathsWailTest.java index 5279f5be79f..9b8f2ac9844 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/chk/Test_HorobiDeathsWail.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/chk/HorobiDeathsWailTest.java @@ -6,7 +6,7 @@ import org.junit.Ignore; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; -public class Test_HorobiDeathsWail extends CardTestPlayerBase { +public class HorobiDeathsWailTest extends CardTestPlayerBase { // issue 7772 @Test diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/chk/UbaMaskTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/chk/UbaMaskTest.java new file mode 100644 index 00000000000..8973bebdabb --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/chk/UbaMaskTest.java @@ -0,0 +1,88 @@ +package org.mage.test.cards.single.chk; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.player.TestPlayer; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author JayDi85 + */ +public class UbaMaskTest extends CardTestPlayerBase { + + @Test + public void test_NormalCard() { + skipInitShuffling(); + + // If a player would draw a card, that player exiles that card face up instead. + // Each player may play cards they exiled with Uba Mask this turn. + addCard(Zone.BATTLEFIELD, playerA, "Uba Mask"); + // + addCard(Zone.LIBRARY, playerA, "Grizzly Bears"); // {1}{G} + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + // + addCard(Zone.LIBRARY, playerB, "Bronze Sable"); // {2} + addCard(Zone.BATTLEFIELD, playerB, "Forest", 2); + + // turn 1 - no draws + checkExileCount("no exile on turn 1 for A", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 0); + checkExileCount("no exile on turn 1 for B", 1, PhaseStep.PRECOMBAT_MAIN, playerB, "Bronze Sable", 0); + + // turn 2 - B draw and exile + checkExileCount("no exile on turn 2 for A", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 0); + checkExileCount("exiled on turn 2 for B", 2, PhaseStep.PRECOMBAT_MAIN, playerB, "Bronze Sable", 1); + checkPlayableAbility("A play on 2", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Grizzly Bears", false); + checkPlayableAbility("A play on 2", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Bronze Sable", false); + checkPlayableAbility("B play on 2", 2, PhaseStep.PRECOMBAT_MAIN, playerB, "Cast Grizzly Bears", false); + checkPlayableAbility("B play on 2", 2, PhaseStep.PRECOMBAT_MAIN, playerB, "Cast Bronze Sable", true); + + // turn 3 - A draw and exile + checkExileCount("exiled on turn 3 for A", 3, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 1); + checkExileCount("no exile on turn 3 for B", 3, PhaseStep.PRECOMBAT_MAIN, playerB, "Bronze Sable", 1); // exiled on prev turn + checkPlayableAbility("A play on 3", 3, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Grizzly Bears", true); + checkPlayableAbility("A play on 3", 3, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Bronze Sable", false); + checkPlayableAbility("B play on 3", 3, PhaseStep.PRECOMBAT_MAIN, playerB, "Cast Grizzly Bears", false); + checkPlayableAbility("B play on 3", 3, PhaseStep.PRECOMBAT_MAIN, playerB, "Cast Bronze Sable", false); + + // turn 3 - A play + castSpell(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Grizzly Bears"); + waitStackResolved(3, PhaseStep.POSTCOMBAT_MAIN); + + setStrictChooseMode(true); + setStopAt(3, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Grizzly Bears", 1); + } + + @Test + public void test_MDF() { + skipInitShuffling(); + + // If a player would draw a card, that player exiles that card face up instead. + // Each player may play cards they exiled with Uba Mask this turn. + addCard(Zone.BATTLEFIELD, playerA, "Uba Mask"); + // + addCard(Zone.LIBRARY, playerB, "Valki, God of Lies"); // {1}{B} + addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2); + + // turn 2 + + // B draw and exile valki + checkExileCount("exiled", 2, PhaseStep.PRECOMBAT_MAIN, playerB, "Valki, God of Lies", 1); + checkPlayableAbility("exiled", 2, PhaseStep.PRECOMBAT_MAIN, playerB, "Cast Valki, God of Lies", true); + + // cast valki + castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Valki, God of Lies"); + setChoice(playerB, TestPlayer.CHOICE_SKIP); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerB, "Valki, God of Lies", 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/eve/HotheadedGiantTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/eve/HotheadedGiantTest.java new file mode 100644 index 00000000000..4e482fd70e1 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/eve/HotheadedGiantTest.java @@ -0,0 +1,47 @@ +package org.mage.test.cards.single.eve; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author TheElk801 + */ +public class HotheadedGiantTest extends CardTestPlayerBase { + private static final String giant = "Hotheaded Giant"; + private static final String goblin = "Mons's Goblin Raiders"; + + @Test + public void testSpell() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); + addCard(Zone.HAND, playerA, goblin); + addCard(Zone.HAND, playerA, giant); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, goblin); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, giant); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertCounterCount(playerA, giant, CounterType.M1M1, 0); + } + + @Test + public void testNoSpell() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + addCard(Zone.HAND, playerA, giant); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, giant); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertCounterCount(playerA, giant, CounterType.M1M1, 2); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/eve/SoulReapTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/eve/SoulReapTest.java new file mode 100644 index 00000000000..13013b89dc9 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/eve/SoulReapTest.java @@ -0,0 +1,56 @@ +package org.mage.test.cards.single.eve; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author TheElk801 + */ +public class SoulReapTest extends CardTestPlayerBase { + private static final String reap = "Soul Reap"; + private static final String lion = "Silvercoat Lion"; + private static final String rats = "Muck Rats"; + + @Test + public void testSpell() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); + addCard(Zone.BATTLEFIELD, playerA, lion); + addCard(Zone.HAND, playerA, reap); + addCard(Zone.HAND, playerA, rats); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, rats); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, reap, lion); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, lion, 0); + assertPermanentCount(playerA, rats, 1); + assertGraveyardCount(playerA, lion, 1); + assertGraveyardCount(playerA, reap, 1); + assertLife(playerA, 20 - 3); + } + + @Test + public void testNoSpell() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + addCard(Zone.BATTLEFIELD, playerA, lion); + addCard(Zone.HAND, playerA, reap); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, reap, lion); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, lion, 0); + assertGraveyardCount(playerA, lion, 1); + assertGraveyardCount(playerA, reap, 1); + assertLife(playerA, 20); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/fut/YixlidJailerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/fut/YixlidJailerTest.java new file mode 100644 index 00000000000..426ca610138 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/fut/YixlidJailerTest.java @@ -0,0 +1,169 @@ +package org.mage.test.cards.single.fut; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class YixlidJailerTest extends CardTestPlayerBase { + + // Rulings on Yixlid Jailer + // 1. If an ability triggers when the object that has it is put into a graveyard from the battlefield, that ability triggers from the battlefield and isn’t affected by Yixlid Jailer. (2021-03-19) + // 2. If an ability triggers when the object that has it is put into a graveyard from anywhere other than the battlefield, such as Krosan Tusker or Narcomoeba, that ability triggers from the graveyard. + // Yixlid Jailer stops those abilities from triggering at all. This includes abilities that trigger when a card is put into a graveyard “from anywhere,” even if that card was on the battlefield. (2021-03-19) + + @Test + public void narcomoebaBaseCase() { + skipInitShuffling(); + addCard(Zone.LIBRARY, playerA, "Narcomoeba", 1); + addCard(Zone.HAND, playerA, "Thought Scour", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + setStrictChooseMode(true); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Thought Scour"); + addTarget(playerA, playerA); + setChoice(playerA, true); // Use Narcomoeba's ability + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertAllCommandsUsed(); + assertPermanentCount(playerA, "Narcomoeba", 1); + assertGraveyardCount(playerA, "Thought Scour", 1); + assertGraveyardCount(playerA, "Narcomoeba", 0); + } + + @Test + public void emrakulBaseCase() { + skipInitShuffling(); + addCard(Zone.LIBRARY, playerA, "Emrakul, the Aeons Torn", 1); + addCard(Zone.HAND, playerA, "Thought Scour", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + setStrictChooseMode(true); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Thought Scour"); + addTarget(playerA, playerA); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertAllCommandsUsed(); + assertGraveyardCount(playerA, 0); // Emrakul should shuffle graveyard into library + } + + @Test + public void emrakulWrathBaseCase() { + skipInitShuffling(); + addCard(Zone.BATTLEFIELD, playerA, "Emrakul, the Aeons Torn", 1); + addCard(Zone.HAND, playerA, "Wrath of God", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + setStrictChooseMode(true); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wrath of God"); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertAllCommandsUsed(); + assertGraveyardCount(playerA, 0); // Emrakul should shuffle graveyard into library + assertPermanentCount(playerA, "Emrakul, the Aeons Torn", 0); + } + + @Test + public void narcomoebaWithJailer() { + skipInitShuffling(); + addCard(Zone.LIBRARY, playerA, "Narcomoeba", 1); + addCard(Zone.HAND, playerA, "Thought Scour", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + addCard(Zone.BATTLEFIELD, playerB, "Yixlid Jailer", 1); + setStrictChooseMode(true); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Thought Scour"); + addTarget(playerA, playerA); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertAllCommandsUsed(); + assertPermanentCount(playerA, "Narcomoeba", 0); + assertGraveyardCount(playerA, "Thought Scour", 1); + assertGraveyardCount(playerA, "Narcomoeba", 1); + } + + @Test + public void emrakulWithJailer() { + skipInitShuffling(); + addCard(Zone.LIBRARY, playerA, "Emrakul, the Aeons Torn", 1); + addCard(Zone.HAND, playerA, "Thought Scour", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + addCard(Zone.BATTLEFIELD, playerB, "Yixlid Jailer", 1); + setStrictChooseMode(true); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Thought Scour"); + addTarget(playerA, playerA); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertAllCommandsUsed(); + assertGraveyardCount(playerA, "Emrakul, the Aeons Torn", 1); + } + + @Test + public void emrakulWrathWithJailer() { + skipInitShuffling(); + addCard(Zone.BATTLEFIELD, playerA, "Emrakul, the Aeons Torn", 1); + addCard(Zone.HAND, playerA, "Wrath of God", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + addCard(Zone.BATTLEFIELD, playerB, "Yixlid Jailer", 1); + setStrictChooseMode(true); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wrath of God"); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertAllCommandsUsed(); + assertGraveyardCount(playerA, "Emrakul, the Aeons Torn", 1); // Emrakul should not trigger even if removed from the battlefield + assertPermanentCount(playerA, "Emrakul, the Aeons Torn", 0); + } + + @Test + public void midnightReaperWithJailer() { + addCard(Zone.BATTLEFIELD, playerA, "Midnight Reaper", 1); + addCard(Zone.BATTLEFIELD, playerB, "Yixlid Jailer", 1); + addCard(Zone.HAND, playerA, "Lightning Bolt", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + setStrictChooseMode(true); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt"); + addTarget(playerA, "Midnight Reaper"); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertAllCommandsUsed(); + assertGraveyardCount(playerA, "Midnight Reaper", 1); + assertPermanentCount(playerB, "Yixlid Jailer", 1); + assertLife(playerA, 19); // Midnight Reaper should still trigger + } + + @Test + public void midnightReaperWrathWithJailer() { + addCard(Zone.BATTLEFIELD, playerA, "Midnight Reaper", 1); + addCard(Zone.BATTLEFIELD, playerB, "Yixlid Jailer", 1); + addCard(Zone.HAND, playerA, "Wrath of God", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + setStrictChooseMode(true); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wrath of God"); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertAllCommandsUsed(); + assertGraveyardCount(playerA, "Midnight Reaper", 1); + assertGraveyardCount(playerB, "Yixlid Jailer", 1); + assertLife(playerA, 19); // Midnight Reaper should still trigger + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/khm/ValkiGodOfLiesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/khm/ValkiGodOfLiesTest.java index 77ed48941e8..a705bddecff 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/khm/ValkiGodOfLiesTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/khm/ValkiGodOfLiesTest.java @@ -18,7 +18,6 @@ public class ValkiGodOfLiesTest extends CardTestPlayerBase { addCard(Zone.LIBRARY, playerB, "Ephemerate"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Tibalt, Cosmic Impostor"); - setChoice(playerA, "Tibalt, Cosmic Impostor"); // two etb effects activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+2: Exile the top card of each player's library."); playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Plains"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ephemerate", "Grizzly Bears"); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m12/AegisAngelTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m12/AegisAngelTest.java new file mode 100644 index 00000000000..b330ad64a11 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m12/AegisAngelTest.java @@ -0,0 +1,118 @@ +package org.mage.test.cards.single.m12; + +import mage.abilities.keyword.IndestructibleAbility; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author TheElk801 + */ +public class AegisAngelTest extends CardTestPlayerBase { + private static final String angel = "Aegis Angel"; + private static final String lion = "Silvercoat Lion"; + private static final String murder = "Murder"; + private static final String act = "Act of Treason"; + + @Test + public void testGainsAbility() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 6); + addCard(Zone.BATTLEFIELD, playerA, lion); + addCard(Zone.HAND, playerA, angel); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, angel); + addTarget(playerA, lion); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertAbility(playerA, lion, IndestructibleAbility.getInstance(), true); + } + + @Test + public void testKeepsAbility() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 6); + addCard(Zone.BATTLEFIELD, playerA, lion); + addCard(Zone.HAND, playerA, angel); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, angel); + addTarget(playerA, lion); + + setStrictChooseMode(true); + setStopAt(3, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertAbility(playerA, lion, IndestructibleAbility.getInstance(), true); + } + + @Test + public void testAngelDiesBeforeEntering() { + addCard(Zone.BATTLEFIELD, playerA, "Scrubland", 9); + addCard(Zone.BATTLEFIELD, playerA, lion); + addCard(Zone.HAND, playerA, angel); + addCard(Zone.HAND, playerA, murder); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, angel); + addTarget(playerA, lion); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, murder, angel); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, angel, 1); + assertGraveyardCount(playerA, murder, 1); + assertAbility(playerA, lion, IndestructibleAbility.getInstance(), false); + } + + @Test + public void testAngelDiesAfterEntering() { + addCard(Zone.BATTLEFIELD, playerA, "Scrubland", 9); + addCard(Zone.BATTLEFIELD, playerA, lion); + addCard(Zone.HAND, playerA, angel); + addCard(Zone.HAND, playerA, murder); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, angel); + addTarget(playerA, lion); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, murder, angel); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, angel, 1); + assertGraveyardCount(playerA, murder, 1); + assertAbility(playerA, lion, IndestructibleAbility.getInstance(), false); + } + + @Test + public void testAngelLoseControl() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 6); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 6); + addCard(Zone.BATTLEFIELD, playerA, lion); + addCard(Zone.HAND, playerA, angel); + addCard(Zone.HAND, playerB, act); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, angel); + addTarget(playerA, lion); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, act, angel); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerB, act, 1); + assertPermanentCount(playerB, angel, 1); + assertAbility(playerA, lion, IndestructibleAbility.getInstance(), false); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/mir/GrinningTotemTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/mir/GrinningTotemTest.java index 8069e2d2e1a..b77f1976596 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/mir/GrinningTotemTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/mir/GrinningTotemTest.java @@ -29,7 +29,7 @@ public class GrinningTotemTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerB, "Tormod's Crypt"); activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}, {T}, Sacrifice {this}: Search target opponent's library for a card and exile it", playerB); - activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "{T}, Sacrifice {this}: Exile all cards", playerA); + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "{T}, Sacrifice {this}: ", playerA); setStopAt(3, PhaseStep.BEGIN_COMBAT); execute(); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/neo/StoryweaveTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/neo/StoryweaveTest.java new file mode 100644 index 00000000000..aabc6fbf687 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/neo/StoryweaveTest.java @@ -0,0 +1,92 @@ +package org.mage.test.cards.single.neo; + +import mage.cards.s.Storyweave; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author TheElk801 + */ +public class StoryweaveTest extends CardTestPlayerBase { + private static final String fang = "Fang of Shigeki"; + private static final String colossus = "Nyxborn Colossus"; + private static final String intervention = "Fated Intervention"; + + private void addEffectToGame() { + // casting the spell is a pain to set up, this is easier + addCustomCardWithAbility("tester", playerA, Storyweave.makeAbility()); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{0}"); + } + + @Test + public void test__WorksOnlyOnce() { + addCard(Zone.BATTLEFIELD, playerA, "Forest", 7); + addCard(Zone.HAND, playerA, fang); + addCard(Zone.HAND, playerA, colossus); + + addEffectToGame(); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, fang); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, colossus); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertCounterCount(playerA, fang, CounterType.P1P1, 2); + assertCounterCount(playerA, colossus, CounterType.P1P1, 0); + } + + @Test + public void test__MultipleOnlyOnce() { + addCard(Zone.BATTLEFIELD, playerA, "Forest", 6); + addCard(Zone.HAND, playerA, intervention); + addCard(Zone.HAND, playerA, fang); + + addEffectToGame(); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, intervention); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, fang); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertCounterCount(playerA, fang, CounterType.P1P1, 0); + assertPermanentCount(playerA, "Centaur", 2); + currentGame + .getBattlefield() + .getAllActivePermanents() + .stream() + .filter(permanent -> "Centaur".equals(permanent.getName())) + .noneMatch(permanent -> permanent.getCounters(currentGame).getCount(CounterType.P1P1) != 2); + } + + @Test + public void test__SingleOnlyOnce() { + addCard(Zone.BATTLEFIELD, playerA, "Forest", 6); + addCard(Zone.HAND, playerA, intervention); + addCard(Zone.HAND, playerA, fang); + + addEffectToGame(); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, fang); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, intervention); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertCounterCount(playerA, fang, CounterType.P1P1, 2); + assertPermanentCount(playerA, "Centaur", 2); + currentGame + .getBattlefield() + .getAllActivePermanents() + .stream() + .filter(permanent -> "Centaur".equals(permanent.getName())) + .noneMatch(permanent -> permanent.getCounters(currentGame).getCount(CounterType.P1P1) != 0); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/commander/duel/OpalPalaceTest.java b/Mage.Tests/src/test/java/org/mage/test/commander/duel/OpalPalaceTest.java index 82a06303bb5..cc62b292f5c 100644 --- a/Mage.Tests/src/test/java/org/mage/test/commander/duel/OpalPalaceTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/commander/duel/OpalPalaceTest.java @@ -32,7 +32,6 @@ public class OpalPalaceTest extends CardTestCommanderDuelBase { // showAvailableAbilities("abi", 1, PhaseStep.PRECOMBAT_MAIN, playerA); activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {G}"); activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}, {T}"); - setChoice(playerA, "Opal Palace"); // activate mana replace effect first (+3 counters) castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ob Nixilis of the Black Oath"); // {3}{B}{B} setStrictChooseMode(true); diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index b700d8363f1..2a61c4fdda5 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -3308,6 +3308,11 @@ public class TestPlayer implements Player { computerPlayer.exchangeLife(player, source, game); } + @Override + public int damage(int damage, Ability source, Game game) { + return computerPlayer.damage(damage, source, game); + } + @Override public int damage(int damage, UUID attackerId, Ability source, Game game) { return computerPlayer.damage(damage, attackerId, source, game); diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestBase.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestBase.java index 996948230d4..72e129eddfd 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestBase.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestBase.java @@ -255,7 +255,7 @@ public abstract class MageTestBase { Card newCard = cardInfo != null ? cardInfo.getCard() : null; if (newCard != null) { if (gameZone == Zone.BATTLEFIELD) { - Card permCard = CardUtil.getDefaultCardSideForBattlefield(newCard); + Card permCard = CardUtil.getDefaultCardSideForBattlefield(currentGame, newCard); PermanentCard p = new PermanentCard(permCard, null, currentGame); p.setTapped(tapped); perms.add(p); diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java index 864103cd90b..ffd9f92a1b9 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java @@ -269,7 +269,7 @@ public abstract class MageTestPlayerBase { Card newCard = cardInfo != null ? cardInfo.getCard() : null; if (newCard != null) { if (gameZone == Zone.BATTLEFIELD) { - Card permCard = CardUtil.getDefaultCardSideForBattlefield(newCard); + Card permCard = CardUtil.getDefaultCardSideForBattlefield(currentGame, newCard); PermanentCard p = new PermanentCard(permCard, null, currentGame); p.setTapped(tapped); perms.add(p); @@ -422,7 +422,7 @@ public abstract class MageTestPlayerBase { CardSetInfo testSet = new CardSetInfo(customName, "custom", "123", Rarity.COMMON); Card newCard = new CustomTestCard(controllerPlayer.getId(), testSet, cardType, spellCost); - Card permCard = CardUtil.getDefaultCardSideForBattlefield(newCard); + Card permCard = CardUtil.getDefaultCardSideForBattlefield(currentGame, newCard); PermanentCard permanent = new PermanentCard(permCard, controllerPlayer.getId(), currentGame); switch (putAtZone) { diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java index 0dd3cdc2c81..8454fe95860 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java @@ -685,7 +685,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement if (gameZone == Zone.BATTLEFIELD) { for (int i = 0; i < count; i++) { Card newCard = cardInfo.getCard(); - Card permCard = CardUtil.getDefaultCardSideForBattlefield(newCard); + Card permCard = CardUtil.getDefaultCardSideForBattlefield(currentGame, newCard); PermanentCard p = new PermanentCard(permCard, player.getId(), currentGame); p.setTapped(tapped); @@ -1713,7 +1713,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement * Ends a block of actions to be added after an rollback action */ public void rollbackAfterActionsEnd() throws IllegalStateException { - if (rollbackBlockActive = false || rollbackPlayer == null) { + if (!rollbackBlockActive || rollbackPlayer == null) { throw new IllegalStateException("There was no rollback action defined before or no rollback block started. You can use this command only after a rollback action."); } rollbackBlockActive = false; diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/performance/SerializationTest.java b/Mage.Tests/src/test/java/org/mage/test/serverside/performance/SerializationTest.java index a6bf6033fd8..e115acd91d8 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/performance/SerializationTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/performance/SerializationTest.java @@ -34,7 +34,7 @@ public class SerializationTest extends CardTestPlayerBase { public void test_PermanentImpl_Simple() { CardInfo cardInfo = CardRepository.instance.findCard("Balduvian Bears"); Card newCard = cardInfo.getCard(); - Card permCard = CardUtil.getDefaultCardSideForBattlefield(newCard); + Card permCard = CardUtil.getDefaultCardSideForBattlefield(currentGame, newCard); PermanentImpl permanent = new PermanentCard(permCard, playerA.getId(), currentGame); currentGame.addPermanent(permanent, 0); @@ -48,7 +48,7 @@ public class SerializationTest extends CardTestPlayerBase { public void test_PermanentImpl_MarkedDamageInfo() { CardInfo cardInfo = CardRepository.instance.findCard("Balduvian Bears"); Card newCard = cardInfo.getCard(); - Card permCard = CardUtil.getDefaultCardSideForBattlefield(newCard); + Card permCard = CardUtil.getDefaultCardSideForBattlefield(currentGame, newCard); PermanentImpl permanent = new PermanentCard(permCard, playerA.getId(), currentGame); currentGame.addPermanent(permanent, 0); @@ -73,7 +73,7 @@ public class SerializationTest extends CardTestPlayerBase { CardUtil.getObjectPartsAsObjects(newCard).stream() .map(Card.class::cast) .forEach(card -> { - Card testCard = CardUtil.getDefaultCardSideForBattlefield(newCard); + Card testCard = CardUtil.getDefaultCardSideForBattlefield(currentGame, newCard); Card testPermanent = null; if (!testCard.isInstantOrSorcery()) { testPermanent = new PermanentCard(testCard, playerA.getId(), currentGame); diff --git a/Mage.Tests/src/test/java/org/mage/test/sets/BoosterGenerationTest.java b/Mage.Tests/src/test/java/org/mage/test/sets/BoosterGenerationTest.java index 42802f2e02d..af5d88d41ff 100644 --- a/Mage.Tests/src/test/java/org/mage/test/sets/BoosterGenerationTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/sets/BoosterGenerationTest.java @@ -177,29 +177,6 @@ public class BoosterGenerationTest extends MageTestBase { assertFalse(str(booster), contains(booster, basics, null)); } - @Test - public void testMastersEditionIV_UrzaSpecialLandsList() { - - List needUrzaList = new ArrayList<>(Arrays.asList( - "Urza's Mine", - "Urza's Power Plant", - "Urza's Tower" - )); - - List setOrzaList = MastersEditionIV.getInstance().getSpecialLand(); - Assert.assertEquals("Urza special lands must have 4 variation for each of 3 card", 3 * 4, setOrzaList.size()); - - List foundUrzaList = new ArrayList<>(); - for (CardInfo cardInfo : setOrzaList) { - Assert.assertTrue("card " + cardInfo.getName() + " must be in urza's list", needUrzaList.contains(cardInfo.getName())); - foundUrzaList.add(cardInfo.getName()); - } - - for (String needName : needUrzaList) { - Assert.assertTrue("can't find need card " + needName + " in special land list", foundUrzaList.contains(needName)); - } - } - @Test public void testMastersEditionIV_UrzaSpecialLandInBoosters() { // ME4 replace all basic lands with special (1 per booster) @@ -272,26 +249,6 @@ public class BoosterGenerationTest extends MageTestBase { } } - @Test - public void testAmonkhetRemastered_MustHaveSpecialLand() { - // AKR replace all basic lands with special (1 per booster) - // https://mtg.gamepedia.com/Amonkhet_Remastered - - for (int i = 1; i <= 5; i++) { - List booster = AmonkhetRemastered.getInstance().createBooster(); - - // no basic lands in booster - assertFalse(str(booster), contains(booster, basics, null)); - - // special lands in land slot (can have multiple special lands per booster: one from land slot, one from common slot) - List boosterLands = booster.stream().filter(card -> !card.isBasic() && card.isLand(currentGame)).collect(Collectors.toList()); - Assert.assertTrue("Amonkhet Remastered's booster must contains minimum 1 special land", boosterLands.size() >= 1); - - // Regal Caracal is top-boxer card, not booster - assertFalse("Amonkhet Remastered's booster must not contains Regal Caracal", contains(booster, "Regal Caracal", null)); - } - } - @Test public void testZendikarRising_MDFC() { for (int i = 0; i < 20; i++) { diff --git a/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java b/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java index 91a4b116bd0..58ed90d7d8d 100644 --- a/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java +++ b/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java @@ -147,6 +147,11 @@ public class PlayerStub implements Player { return 0; } + @Override + public int damage(int damage, Ability source, Game game) { + return 0; + } + @Override public int damage(int damage, UUID attackerId, Ability source, Game game, boolean combatDamage, boolean preventable) { return 0; diff --git a/Mage.Tests/src/test/java/org/mage/test/utils/BoostCountTest.java b/Mage.Tests/src/test/java/org/mage/test/utils/BoostCountTest.java index 6121fb2bad6..bd5b313c6dd 100644 --- a/Mage.Tests/src/test/java/org/mage/test/utils/BoostCountTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/utils/BoostCountTest.java @@ -1,5 +1,9 @@ package org.mage.test.utils; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.GetXValue; +import mage.abilities.dynamicvalue.common.SignInversionDynamicValue; +import mage.abilities.dynamicvalue.common.StaticValue; import mage.util.CardUtil; import org.junit.Assert; import org.junit.Test; @@ -21,4 +25,19 @@ public class BoostCountTest { Assert.assertEquals(CardUtil.getBoostCountAsStr(-1, 1), "-1/+1"); Assert.assertEquals(CardUtil.getBoostCountAsStr(1, -1), "+1/-1"); } + + @Test + public void test_DynamicBoostCountSigns() { + DynamicValue zero = StaticValue.get(0); + DynamicValue plusX = GetXValue.instance; + DynamicValue minusX = new SignInversionDynamicValue(plusX); + + Assert.assertEquals(CardUtil.getBoostCountAsStr(plusX, zero), "+X/+0"); + Assert.assertEquals(CardUtil.getBoostCountAsStr(zero, plusX), "+0/+X"); + Assert.assertEquals(CardUtil.getBoostCountAsStr(plusX, plusX), "+X/+X"); + Assert.assertEquals(CardUtil.getBoostCountAsStr(minusX, zero), "-X/-0"); + Assert.assertEquals(CardUtil.getBoostCountAsStr(zero, minusX), "-0/-X"); + Assert.assertEquals(CardUtil.getBoostCountAsStr(minusX, plusX), "-X/+X"); + Assert.assertEquals(CardUtil.getBoostCountAsStr(plusX, minusX), "+X/-X"); + } } diff --git a/Mage.Verify/src/main/java/mage/verify/mtgjson/MtgJsonCard.java b/Mage.Verify/src/main/java/mage/verify/mtgjson/MtgJsonCard.java index 2b448af36ce..865f505da5d 100644 --- a/Mage.Verify/src/main/java/mage/verify/mtgjson/MtgJsonCard.java +++ b/Mage.Verify/src/main/java/mage/verify/mtgjson/MtgJsonCard.java @@ -40,6 +40,9 @@ public final class MtgJsonCard { || "flip".equals(layout) || "adventure".equals(layout) || "modal_dfc".equals(layout) + || "reversible_card".equals(layout) // example: Zndrsplt, Eye of Wisdom + || "split".equals(layout) + || "aftermath".equals(layout) || "meld".equals(layout)) { // mtgjson uses composite names for meld cards, but scryfall uses simple face names return faceName; } diff --git a/Mage.Verify/src/main/java/mage/verify/mtgjson/MtgJsonService.java b/Mage.Verify/src/main/java/mage/verify/mtgjson/MtgJsonService.java index dbc1935c867..6171d0774d0 100644 --- a/Mage.Verify/src/main/java/mage/verify/mtgjson/MtgJsonService.java +++ b/Mage.Verify/src/main/java/mage/verify/mtgjson/MtgJsonService.java @@ -24,8 +24,6 @@ public final class MtgJsonService { for (Map.Entry entry : mtgJsonToXMageCodes.entrySet()) { xMageToMtgJsonCodes.put(entry.getValue(), entry.getKey()); } - xMageToMtgJsonCodes.put("8EB", "8ED"); - xMageToMtgJsonCodes.put("9EB", "9ED"); } private static Map loadAllCards() throws IOException { diff --git a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java index ed37b8723f1..cb80ec1e713 100644 --- a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java +++ b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java @@ -3,10 +3,14 @@ package mage.verify; import com.google.common.base.CharMatcher; import mage.ObjectColor; import mage.abilities.Ability; +import mage.abilities.Mode; import mage.abilities.common.SagaAbility; import mage.abilities.common.WerewolfBackTriggeredAbility; import mage.abilities.common.WerewolfFrontTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.FightTargetsEffect; import mage.abilities.effects.keyword.ScryEffect; +import mage.abilities.keyword.EnchantAbility; import mage.abilities.keyword.MenaceAbility; import mage.abilities.keyword.MultikickerAbility; import mage.abilities.keyword.TransformAbility; @@ -58,7 +62,7 @@ public class VerifyCardDataTest { private static final Logger logger = Logger.getLogger(VerifyCardDataTest.class); - private static final String FULL_ABILITIES_CHECK_SET_CODE = "MID"; // check all abilities and output cards with wrong abilities texts; + private static final String FULL_ABILITIES_CHECK_SET_CODE = "KHC"; // check all abilities and output cards with wrong abilities texts; private static final boolean AUTO_FIX_SAMPLE_DECKS = false; // debug only: auto-fix sample decks by test_checkSampleDecks test run private static final boolean ONLY_TEXT = false; // use when checking text locally, suppresses unnecessary checks and output messages @@ -80,7 +84,8 @@ public class VerifyCardDataTest { private static final String SKIP_LIST_SAMPLE_DECKS = "SAMPLE_DECKS"; private static final List evergreenKeywords = Arrays.asList( "flying", "lifelink", "menace", "trample", "haste", "first strike", "hexproof", - "deathtouch", "double strike", "indestructible", "reach", "flash", "defender", "vigilance" + "deathtouch", "double strike", "indestructible", "reach", "flash", "defender", "vigilance", + "plainswalk", "islandwalk", "swampwalk", "mountainwalk", "forestwalk" ); static { @@ -104,6 +109,7 @@ public class VerifyCardDataTest { skipListAddName(SKIP_LIST_TYPE, "UNH", "Old Fogey"); // uses summon word as a joke card skipListAddName(SKIP_LIST_TYPE, "UND", "Old Fogey"); skipListAddName(SKIP_LIST_TYPE, "UST", "capital offense"); // uses "instant" instead "Instant" as a joke card + skipListAddName(SKIP_LIST_TYPE, "NEO", "Oni-Cult Anvil"); // temporary // subtype skipListCreate(SKIP_LIST_SUBTYPE); @@ -192,17 +198,15 @@ public class VerifyCardDataTest { // wrond card numbers skip list skipListCreate(SKIP_LIST_WRONG_CARD_NUMBERS); skipListAddName(SKIP_LIST_WRONG_CARD_NUMBERS, "SWS"); // Star Wars - skipListAddName(SKIP_LIST_WRONG_CARD_NUMBERS, "POR"); // Portal, TODO: remove after bug fixed https://github.com/mtgjson/mtgjson/issues/660 skipListAddName(SKIP_LIST_WRONG_CARD_NUMBERS, "UND"); // un-sets don't have full implementation of card variations skipListAddName(SKIP_LIST_WRONG_CARD_NUMBERS, "UST"); // un-sets don't have full implementation of card variations skipListAddName(SKIP_LIST_WRONG_CARD_NUMBERS, "SOI", "Tamiyo's Journal"); // not all variations implemented + skipListAddName(SKIP_LIST_WRONG_CARD_NUMBERS, "SLD", "Zndrsplt, Eye of Wisdom"); // xmage adds additional card for alternative image (second side) // scryfall download sets (missing from scryfall website) skipListCreate(SKIP_LIST_SCRYFALL_DOWNLOAD_SETS); skipListAddName(SKIP_LIST_SCRYFALL_DOWNLOAD_SETS, "SWS"); // Star Wars - //skipListAddName(SKIP_LIST_SCRYFALL_DOWNLOAD_SETS, "8EB"); // Eighth Edition Box - inner xmage set, split from 8ED - //skipListAddName(SKIP_LIST_SCRYFALL_DOWNLOAD_SETS, "9EB"); // Ninth Edition Box - inner xmage set, split from 9ED // sample decks checking - some decks can contains unimplemented cards, so ignore it // file name must be related to sample-decks folder @@ -237,7 +241,7 @@ public class VerifyCardDataTest { } private static boolean evergreenCheck(String s) { - return evergreenKeywords.contains(s) || s.startsWith("protection from") || s.startsWith("hexproof from"); + return evergreenKeywords.contains(s) || s.startsWith("protection from") || s.startsWith("hexproof from") || s.startsWith("ward "); } private static boolean eqSet(Collection a, Collection b) { @@ -262,13 +266,13 @@ public class VerifyCardDataTest { for (Card card : CardScanner.getAllCards()) { cardIndex++; if (card instanceof SplitCard) { - check(((SplitCard) card).getLeftHalfCard(), cardIndex, true); - check(((SplitCard) card).getRightHalfCard(), cardIndex, true); + check(((SplitCard) card).getLeftHalfCard(), cardIndex); + check(((SplitCard) card).getRightHalfCard(), cardIndex); } else if (card instanceof ModalDoubleFacesCard) { - check(((ModalDoubleFacesCard) card).getLeftHalfCard(), cardIndex, false); - check(((ModalDoubleFacesCard) card).getRightHalfCard(), cardIndex, false); + check(((ModalDoubleFacesCard) card).getLeftHalfCard(), cardIndex); + check(((ModalDoubleFacesCard) card).getRightHalfCard(), cardIndex); } else { - check(card, cardIndex, false); + check(card, cardIndex); } } @@ -481,7 +485,6 @@ public class VerifyCardDataTest { continue; } - // TODO: 8EB and 9EB uses workaround to split from main set, so it will be in unofficial list until booster cards improve xmageUnofficialSets++; xmageUnofficialCards += set.getSetCardInfo().size(); info.add("Unofficial set: " + set.getCode() + " - " + set.getName() + ", cards: " + set.getSetCardInfo().size() + ", year: " + set.getReleaseYear()); @@ -703,10 +706,16 @@ public class VerifyCardDataTest { } for (ExpansionSet.SetCardInfo card : set.getSetCardInfo()) { + if (skipListHaveName(SKIP_LIST_WRONG_CARD_NUMBERS, set.getCode(), card.getName())) { + continue; + } + MtgJsonCard jsonCard = MtgJsonService.cardFromSet(set.getCode(), card.getName(), card.getCardNumber()); if (jsonCard == null) { // see convertMtgJsonToXmageCardNumber for card number convert notation - errorsList.add("Error: scryfall download can't find card from mtgjson " + set.getCode() + " - " + set.getName() + " - " + card.getName() + " - " + card.getCardNumber()); + if (!skipListHaveName(SKIP_LIST_WRONG_CARD_NUMBERS, set.getCode(), card.getName())) { + errorsList.add("Error: scryfall download can't find card from mtgjson " + set.getCode() + " - " + set.getName() + " - " + card.getName() + " - " + card.getCardNumber()); + } continue; } @@ -721,6 +730,16 @@ public class VerifyCardDataTest { errorsList.add("Error: scryfall download can't find non-ascii card link in direct download list " + set.getCode() + " - " + set.getName() + " - " + card.getName() + " - " + jsonCard.number); } } + + // CHECK: reversible_card must be in direct download list (xmage must have 2 cards with diff image face) + if (jsonCard.layout.equals("reversible_card")) { + String key = ScryfallImageSupportCards.findDirectDownloadKey(set.getCode(), card.getName(), card.getCardNumber()); + if (key != null) { + foundedDirectDownloadKeys.add(key); + } else { + errorsList.add("Error: scryfall download can't find face image of reversible_card in direct download list " + set.getCode() + " - " + set.getName() + " - " + card.getName() + " - " + jsonCard.number); + } + } } } @@ -734,7 +753,7 @@ public class VerifyCardDataTest { continue; } - // skip non implemented cards list + // skip non-implemented cards list if (CardRepository.instance.findCard(cardName) == null) { continue; } @@ -1231,11 +1250,11 @@ public class VerifyCardDataTest { } } - private void check(Card card, int cardIndex, boolean skipWarning) { + private void check(Card card, int cardIndex) { MtgJsonCard ref = MtgJsonService.cardFromSet(card.getExpansionSetCode(), card.getName(), card.getCardNumber()); if (ref != null) { checkAll(card, ref, cardIndex); - } else if (!skipWarning && !ONLY_TEXT) { + } else { warn(card, "Missing card reference"); } } @@ -1248,10 +1267,10 @@ public class VerifyCardDataTest { if (!ONLY_TEXT) { return true; } - if (checkedNames.contains(ref.name)) { + if (checkedNames.contains(ref.getRealCardName())) { return false; } - checkedNames.add(ref.name); + checkedNames.add(ref.getRealCardName()); return true; } @@ -1369,9 +1388,14 @@ public class VerifyCardDataTest { fail(card, "abilities", "card have Multikicker ability, but missing it in rules text"); } + // special check: Auras need to have enchant ability added + if (card.hasSubtype(SubType.AURA, null) && !card.getAbilities().containsClass(EnchantAbility.class)) { + fail(card, "abilities", "card is an Aura but is missing this.addAbility(EnchantAbility)"); + } + // special check: Sagas need to have saga ability added if (card.hasSubtype(SubType.SAGA, null) && !card.getAbilities().containsClass(SagaAbility.class)) { - fail(card, "abilities", "card is a Saga but is missing this.addAbility(sagaAbility)"); + fail(card, "abilities", "card is a Saga but is missing this.addAbility(SagaAbility)"); } // special check: Werewolves front ability should only be on front and vice versa @@ -1390,25 +1414,24 @@ public class VerifyCardDataTest { fail(card, "abilities", "double-faced cards should not have transform ability on the back"); } + if (card.getSecondCardFace() != null && !card.getSecondCardFace().isNightCard()) { + fail(card, "abilities", "the back face of a double-faced card should be nightCard = true"); + } + // special check: missing or wrong ability/effect hints Map hints = new HashMap<>(); + + hints.put(FightTargetsEffect.class, "Each deals damage equal to its power to the other"); hints.put(MenaceAbility.class, "can't be blocked except by two or more"); - hints.put(ScryEffect.class, "Look at the top card of your library"); + hints.put(ScryEffect.class, "Look at the top card of your library. You may put that card on the bottom of your library"); + for (Class objectClass : hints.keySet()) { String objectHint = hints.get(objectClass); // ability/effect must have description or not - boolean mustCheck = card.getAbilities().containsClass(objectClass) - || card.getAbilities().stream() - .map(Ability::getAllEffects) - .flatMap(Collection::stream) - .anyMatch(effect -> effect.getClass().isAssignableFrom(objectClass)); - mustCheck = false; // TODO: enable and fix all problems with effect and ability hints - if (mustCheck) { - boolean needHint = ref.text.contains(objectHint); - boolean haveHint = card.getRules().stream().anyMatch(rule -> rule.contains(objectHint)); - if (needHint != haveHint) { - fail(card, "abilities", "card have " + objectClass.getSimpleName() + " but hint is wrong (it must be " + (needHint ? "enabled" : "disabled") + ")"); - } + boolean needHint = ref.text.contains(objectHint); + boolean haveHint = card.getRules().stream().anyMatch(rule -> rule.contains(objectHint)); + if (needHint != haveHint) { + fail(card, "abilities", "card have " + objectClass.getSimpleName() + " but hint is wrong (it must be " + (needHint ? "enabled" : "disabled") + ")"); } } @@ -1428,14 +1451,20 @@ public class VerifyCardDataTest { } } + private static final String[] wrongSymbols = {"’", "“", "”"}; + private void checkWrongSymbolsInRules(Card card) { - if (card.getName().contains("’")) { - fail(card, "card name", "card's names contains restricted symbol ’"); + for (String s : wrongSymbols) { + if (card.getName().contains(s)) { + fail(card, "card name", "card's name contains restricted symbol " + s); + } } for (String rule : card.getRules()) { - if (rule.contains("’")) { - fail(card, "rules", "card's rules contains restricted symbol ’"); + for (String s : wrongSymbols) { + if (rule.contains(s)) { + fail(card, "rules", "card's rules contains restricted symbol " + s); + } } if (rule.contains("&mdash ")) { fail(card, "rules", "card's rules contains restricted test [&mdash ] instead [—]"); @@ -1459,6 +1488,13 @@ public class VerifyCardDataTest { newRule = newRule.replaceAll("(?i) \\(.+\\)", ""); newRule = newRule.replaceAll("(?i) \\(.+\\)", ""); + // fix specifically for mana abilities + if (newRule.startsWith("({T}: Add")) { + newRule = newRule + .replace("(", "") + .replace(")", ""); + } + // replace special text and symbols newRule = newRule .replace("{this}", cardName) @@ -1521,7 +1557,7 @@ public class VerifyCardDataTest { .replace("{this}", card.getName()) //.replace("", "") //.replace("", "") - .replace("—", "—");; + .replace("—", "—"); boolean found = false; for (String refRule : refRules) { @@ -1568,6 +1604,17 @@ public class VerifyCardDataTest { return cardText.replace(name, name.split(" ")[0]).equals(refText); } + private static final boolean checkForEffect(Card card, Class effectClazz) { + return card.getAbilities() + .stream() + .map(Ability::getModes) + .map(LinkedHashMap::values) + .flatMap(Collection::stream) + .map(Mode::getEffects) + .flatMap(Collection::stream) + .anyMatch(effectClazz::isInstance); + } + private void checkWrongAbilitiesText(Card card, MtgJsonCard ref, int cardIndex) { // checks missing or wrong text if (!card.getExpansionSetCode().equals(FULL_ABILITIES_CHECK_SET_CODE) || !checkName(ref)) { @@ -1579,25 +1626,20 @@ public class VerifyCardDataTest { } String refText = ref.text; - // lands fix - if (refText.startsWith("(") && refText.endsWith(")")) { - refText = refText.substring(1, refText.length() - 1); - } // planeswalker fix [-7]: xxx - if (refText.contains("[") && refText.contains("]")) { - refText = refText.replace("[", "").replace("]", ""); - } + refText = refText.replaceAll("\\[([\\−\\+]?\\d*)\\]\\: ", "$1: ").replaceAll("\\[\\−X\\]\\: ", "-X: "); + // evergreen keyword fix - for (String s : refText.split("[\\$\\\n]")) { + for (String s : refText.replaceAll(" \\(.+?\\)", "").split("[\\$\\\n]")) { if (Arrays - .stream(s.split(", ")) + .stream(s.split("[,;] ")) .map(String::toLowerCase) .allMatch(VerifyCardDataTest::evergreenCheck)) { String replacement = Arrays - .stream(s.split(", ")) + .stream(s.split("[,;] ")) .map(CardUtil::getTextWithFirstCharUpperCase) - .reduce("", (a, b) -> a + '\n' + b); - refText = refText.replace(s, replacement.substring(1)); + .collect(Collectors.joining("\n")); + refText = refText.replace(s, replacement); } } // modal spell fix @@ -1612,7 +1654,8 @@ public class VerifyCardDataTest { } // mana ability fix for (String s : refText.split("[\\$\\\n]")) { - if (!(s.startsWith("{T}: Add {") || s.startsWith("({T}: Add {")) || !s.contains("} or {")) { + if (!(s.startsWith("{T}: Add {") || s.startsWith("({T}: Add {")) + || !(s.contains("} or {") || s.contains("}, or {"))) { continue; } String newStr = ""; diff --git a/Mage/pom.xml b/Mage/pom.xml index c704f8d6c88..9bce03e1797 100644 --- a/Mage/pom.xml +++ b/Mage/pom.xml @@ -25,7 +25,7 @@ com.google.protobuf protobuf-java - 4.0.0-rc-2 + 3.19.3 @@ -55,7 +55,7 @@ - com.google.protobuf:protoc:3.0.0 + com.google.protobuf:protoc:3.18.0 ${project.basedir}/src/main/proto diff --git a/Mage/src/main/java/mage/MageIdentifier.java b/Mage/src/main/java/mage/MageIdentifier.java index 09d0c38892e..e2c4fc1c1c5 100644 --- a/Mage/src/main/java/mage/MageIdentifier.java +++ b/Mage/src/main/java/mage/MageIdentifier.java @@ -7,10 +7,13 @@ package mage; * @author LevelX2 */ public enum MageIdentifier { + CemeteryIlluminatorWatcher, GisaAndGeralfWatcher, + HaukensInsightWatcher, KaradorGhostChieftainWatcher, KessDissidentMageWatcher, LurrusOfTheDreamDenWatcher, MuldrothaTheGravetideWatcher, - WishWatcher + WishWatcher, + GlimpseTheCosmosWatcher } diff --git a/Mage/src/main/java/mage/MageObject.java b/Mage/src/main/java/mage/MageObject.java index 970fc3627ce..49a731dbc85 100644 --- a/Mage/src/main/java/mage/MageObject.java +++ b/Mage/src/main/java/mage/MageObject.java @@ -4,7 +4,6 @@ import mage.abilities.Abilities; import mage.abilities.Ability; import mage.abilities.costs.mana.ManaCost; import mage.abilities.costs.mana.ManaCosts; -import mage.abilities.text.TextPart; import mage.cards.Card; import mage.cards.FrameStyle; import mage.constants.CardType; @@ -508,13 +507,5 @@ public interface MageObject extends MageItem, Serializable, Copyable */ void setIsAllCreatureTypes(Game game, boolean value); - List getTextParts(); - - TextPart addTextPart(TextPart textPart); - void removePTCDA(); - - default void changeSubType(SubType fromSubType, SubType toSubType) { - - } } diff --git a/Mage/src/main/java/mage/MageObjectImpl.java b/Mage/src/main/java/mage/MageObjectImpl.java index ffbcdcd9e97..83a51295caf 100644 --- a/Mage/src/main/java/mage/MageObjectImpl.java +++ b/Mage/src/main/java/mage/MageObjectImpl.java @@ -3,16 +3,12 @@ package mage; import mage.abilities.Abilities; import mage.abilities.AbilitiesImpl; import mage.abilities.Ability; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.abilities.costs.mana.ManaCost; import mage.abilities.costs.mana.ManaCosts; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.Effect; -import mage.abilities.keyword.ChangelingAbility; import mage.abilities.mana.ActivatedManaAbilityImpl; -import mage.abilities.text.TextPart; -import mage.abilities.text.TextPartSubType; import mage.cards.FrameStyle; import mage.cards.mock.MockCard; import mage.constants.*; @@ -43,9 +39,9 @@ public abstract class MageObjectImpl implements MageObject { protected String text; protected MageInt power; protected MageInt toughness; + protected int startingLoyalty = -1; // -2 means X, -1 means none, 0 and up is normal protected boolean copy; protected MageObject copyFrom; // copied card INFO (used to call original adjusters) - protected List textParts; public MageObjectImpl() { this(UUID.randomUUID()); @@ -60,7 +56,6 @@ public abstract class MageObjectImpl implements MageObject { frameStyle = FrameStyle.M15_NORMAL; manaCost = new ManaCostsImpl<>(); abilities = new AbilitiesImpl<>(); - textParts = new ArrayList<>(); } public MageObjectImpl(final MageObjectImpl object) { @@ -73,14 +68,13 @@ public abstract class MageObjectImpl implements MageObject { frameStyle = object.frameStyle; power = object.power.copy(); toughness = object.toughness.copy(); + startingLoyalty = object.startingLoyalty; abilities = object.abilities.copy(); this.cardType.addAll(object.cardType); this.subtype.copyFrom(object.subtype); supertype.addAll(object.supertype); this.copy = object.copy; this.copyFrom = (object.copyFrom != null ? object.copyFrom.copy() : null); - textParts = new ArrayList<>(); - textParts.addAll(object.textParts); } @Override @@ -174,21 +168,12 @@ public abstract class MageObjectImpl implements MageObject { @Override public int getStartingLoyalty() { - for (Ability ab : getAbilities()) { - if (ab instanceof PlaneswalkerEntersWithLoyaltyCountersAbility) { - return ((PlaneswalkerEntersWithLoyaltyCountersAbility) ab).getStartingLoyalty(); - } - } - return 0; + return startingLoyalty; } @Override public void setStartingLoyalty(int startingLoyalty) { - for (Ability ab : getAbilities()) { - if (ab instanceof PlaneswalkerEntersWithLoyaltyCountersAbility) { - ((PlaneswalkerEntersWithLoyaltyCountersAbility) ab).setStartingLoyalty(startingLoyalty); - } - } + this.startingLoyalty = startingLoyalty; } @Override @@ -316,26 +301,6 @@ public abstract class MageObjectImpl implements MageObject { this.getSubtype(game).setIsAllCreatureTypes(value && (this.isTribal(game) || this.isCreature(game))); } - @Override - public List getTextParts() { - return textParts; - } - - @Override - public TextPart addTextPart(TextPart textPart) { - textParts.add(textPart); - return textPart; - } - - @Override - public void changeSubType(SubType fromSubType, SubType toSubType) { - for (TextPart textPart : textParts) { - if (textPart instanceof TextPartSubType && textPart.getCurrentValue().equals(fromSubType)) { - textPart.replaceWith(toSubType); - } - } - } - /** * Remove power/toughness character defining abilities */ diff --git a/Mage/src/main/java/mage/MageObjectReference.java b/Mage/src/main/java/mage/MageObjectReference.java index f8ca0fb9271..413bd895dff 100644 --- a/Mage/src/main/java/mage/MageObjectReference.java +++ b/Mage/src/main/java/mage/MageObjectReference.java @@ -147,6 +147,14 @@ public class MageObjectReference implements Comparable, Ser return false; } + public boolean refersTo(Ability source, Game game) { + if (source == null || !source.getSourceId().equals(sourceId)) { + return false; + } + return zoneChangeCounter * source.getSourceObjectZoneChangeCounter() == 0 + || zoneChangeCounter == source.getSourceObjectZoneChangeCounter(); + } + public Permanent getPermanent(Game game) { Permanent permanent = game.getPermanent(sourceId); if (permanent != null && permanent.getZoneChangeCounter(game) == zoneChangeCounter) { diff --git a/Mage/src/main/java/mage/ManaSymbol.java b/Mage/src/main/java/mage/ManaSymbol.java index 35b692de7b7..ca46df5cb13 100644 --- a/Mage/src/main/java/mage/ManaSymbol.java +++ b/Mage/src/main/java/mage/ManaSymbol.java @@ -83,6 +83,16 @@ public enum ManaSymbol { PHYREXIAN_R("{R/P}", R, Type.PHYREXIAN, Type.COLORED, Type.MONOCOLORED), PHYREXIAN_B("{B/P}", B, Type.PHYREXIAN, Type.COLORED, Type.MONOCOLORED), PHYREXIAN_U("{U/P}", U, Type.PHYREXIAN, Type.COLORED, Type.MONOCOLORED), + PHYREXIAN_HYBRID_WU("{W/U/P}", W, U, Type.PHYREXIAN, Type.HYBRID, Type.COLORED), + PHYREXIAN_HYBRID_WB("{W/B/P}", W, B, Type.PHYREXIAN, Type.HYBRID, Type.COLORED), + PHYREXIAN_HYBRID_UB("{U/B/P}", U, B, Type.PHYREXIAN, Type.HYBRID, Type.COLORED), + PHYREXIAN_HYBRID_UR("{U/R/P}", U, R, Type.PHYREXIAN, Type.HYBRID, Type.COLORED), + PHYREXIAN_HYBRID_BR("{B/R/P}", B, R, Type.PHYREXIAN, Type.HYBRID, Type.COLORED), + PHYREXIAN_HYBRID_BG("{B/G/P}", B, G, Type.PHYREXIAN, Type.HYBRID, Type.COLORED), + PHYREXIAN_HYBRID_RG("{R/G/P}", R, G, Type.PHYREXIAN, Type.HYBRID, Type.COLORED), + PHYREXIAN_HYBRID_RW("{R/W/P}", R, W, Type.PHYREXIAN, Type.HYBRID, Type.COLORED), + PHYREXIAN_HYBRID_GW("{G/W/P}", G, W, Type.PHYREXIAN, Type.HYBRID, Type.COLORED), + PHYREXIAN_HYBRID_GU("{G/U/P}", G, U, Type.PHYREXIAN, Type.HYBRID, Type.COLORED), SNOW("{S}", Type.SNOW); private enum Type { diff --git a/Mage/src/main/java/mage/abilities/Ability.java b/Mage/src/main/java/mage/abilities/Ability.java index 63cb477b0c7..42b6ab715bc 100644 --- a/Mage/src/main/java/mage/abilities/Ability.java +++ b/Mage/src/main/java/mage/abilities/Ability.java @@ -531,13 +531,17 @@ public interface Ability extends Controllable, Serializable { */ Permanent getSourcePermanentOrLKI(Game game); + void setSourcePermanentTransformCount(Game game); + + boolean checkTransformCount(Permanent permanent, Game game); + String getTargetDescription(Targets targets, Game game); void setCanFizzle(boolean canFizzle); boolean canFizzle(); - void setTargetAdjuster(TargetAdjuster targetAdjuster); + Ability setTargetAdjuster(TargetAdjuster targetAdjuster); TargetAdjuster getTargetAdjuster(); diff --git a/Mage/src/main/java/mage/abilities/AbilityImpl.java b/Mage/src/main/java/mage/abilities/AbilityImpl.java index 44306218224..4de229cbf4a 100644 --- a/Mage/src/main/java/mage/abilities/AbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/AbilityImpl.java @@ -4,7 +4,10 @@ import mage.MageIdentifier; import mage.MageObject; import mage.abilities.costs.*; import mage.abilities.costs.common.PayLifeCost; -import mage.abilities.costs.mana.*; +import mage.abilities.costs.mana.ManaCost; +import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.costs.mana.VariableManaCost; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.Effect; import mage.abilities.effects.Effects; @@ -80,6 +83,7 @@ public abstract class AbilityImpl implements Ability { protected Outcome customOutcome = null; // uses for AI decisions instead effects protected MageIdentifier identifier; // used to identify specific ability (e.g. to match with corresponding watcher) protected String appendToRule = null; + protected int sourcePermanentTransformCount = 0; public AbilityImpl(AbilityType abilityType, Zone zone) { this.id = UUID.randomUUID(); @@ -101,7 +105,7 @@ public abstract class AbilityImpl implements Ability { this.zone = ability.zone; this.name = ability.name; this.usesStack = ability.usesStack; - this.manaCosts = ability.manaCosts; + this.manaCosts = ability.manaCosts.copy(); this.manaCostsToPay = ability.manaCostsToPay.copy(); this.costs = ability.costs.copy(); for (Watcher watcher : ability.getWatchers()) { @@ -135,6 +139,7 @@ public abstract class AbilityImpl implements Ability { this.identifier = ability.identifier; this.activated = ability.activated; this.appendToRule = ability.appendToRule; + this.sourcePermanentTransformCount = ability.sourcePermanentTransformCount; } @Override @@ -246,6 +251,7 @@ public abstract class AbilityImpl implements Ability { if (getSourceObjectZoneChangeCounter() == 0) { setSourceObjectZoneChangeCounter(game.getState().getZoneChangeCounter(getSourceId())); } + setSourcePermanentTransformCount(game); /* 20130201 - 601.2b * If the player wishes to splice any cards onto the spell (see rule 702.45), he @@ -429,6 +435,7 @@ public abstract class AbilityImpl implements Ability { case FLASHBACK: case MADNESS: + case DISTURB: // from Snapcaster Mage: // If you cast a spell from a graveyard using its flashback ability, you can’t pay other alternative costs // (such as that of Foil). (2018-12-07) @@ -532,14 +539,15 @@ public abstract class AbilityImpl implements Ability { while (costIterator.hasNext()) { ManaCost cost = costIterator.next(); - if (cost instanceof PhyrexianManaCost) { - PhyrexianManaCost phyrexianManaCost = (PhyrexianManaCost) cost; - PayLifeCost payLifeCost = new PayLifeCost(2); - if (payLifeCost.canPay(this, this, controller.getId(), game) - && controller.chooseUse(Outcome.LoseLife, "Pay 2 life instead of " + phyrexianManaCost.getBaseText() + '?', this, game)) { - costIterator.remove(); - costs.add(payLifeCost); - } + if (!cost.isPhyrexian()) { + continue; + } + PayLifeCost payLifeCost = new PayLifeCost(2); + if (payLifeCost.canPay(this, this, controller.getId(), game) + && controller.chooseUse(Outcome.LoseLife, "Pay 2 life instead of " + cost.getText().replace("/P", "") + '?', this, game)) { + costIterator.remove(); + costs.add(payLifeCost); + manaCostsToPay.incrPhyrexianPaid(); } } } @@ -1292,6 +1300,24 @@ public abstract class AbilityImpl implements Ability { return sourceObjectZoneChangeCounter; } + @Override + public void setSourcePermanentTransformCount(Game game) { + Permanent permanent = getSourcePermanentOrLKI(game); + if (permanent != null) { + this.sourcePermanentTransformCount = permanent.getTransformCount(); + } + } + + @Override + public boolean checkTransformCount(Permanent permanent, Game game) { + if (permanent == null + || !permanent.getId().equals(sourceId) + || permanent.getZoneChangeCounter(game) != sourceObjectZoneChangeCounter) { + return true; + } + return permanent.getTransformCount() == sourcePermanentTransformCount; + } + @Override public boolean canFizzle() { return canFizzle; @@ -1303,8 +1329,9 @@ public abstract class AbilityImpl implements Ability { } @Override - public void setTargetAdjuster(TargetAdjuster targetAdjuster) { + public AbilityImpl setTargetAdjuster(TargetAdjuster targetAdjuster) { this.targetAdjuster = targetAdjuster; + return this; } @Override diff --git a/Mage/src/main/java/mage/abilities/ActivatedAbilityImpl.java b/Mage/src/main/java/mage/abilities/ActivatedAbilityImpl.java index 2149d7fe87d..62cef1c9976 100644 --- a/Mage/src/main/java/mage/abilities/ActivatedAbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/ActivatedAbilityImpl.java @@ -6,10 +6,8 @@ import mage.abilities.condition.Condition; import mage.abilities.costs.Cost; import mage.abilities.costs.Costs; import mage.abilities.costs.mana.ManaCosts; -import mage.abilities.costs.mana.PhyrexianManaCost; import mage.abilities.effects.Effect; import mage.abilities.effects.Effects; -import mage.abilities.keyword.FlashAbility; import mage.abilities.mana.ManaOptions; import mage.cards.Card; import mage.constants.*; @@ -61,19 +59,13 @@ public abstract class ActivatedAbilityImpl extends AbilityImpl implements Activa public ActivatedAbilityImpl(Zone zone, Effect effect) { super(AbilityType.ACTIVATED, zone); - if (effect != null) { - this.addEffect(effect); - } + this.addEffect(effect); } public ActivatedAbilityImpl(Zone zone, Effect effect, ManaCosts cost) { super(AbilityType.ACTIVATED, zone); - if (effect != null) { - this.addEffect(effect); - } - if (cost != null) { - this.addManaCost(cost); - } + this.addEffect(effect); + this.addManaCost(cost); } public ActivatedAbilityImpl(Zone zone, Effects effects, ManaCosts cost) { @@ -83,30 +75,18 @@ public abstract class ActivatedAbilityImpl extends AbilityImpl implements Activa this.addEffect(effect); } } - if (cost != null) { - this.addManaCost(cost); - } + this.addManaCost(cost); } public ActivatedAbilityImpl(Zone zone, Effect effect, Cost cost) { super(AbilityType.ACTIVATED, zone); - if (effect != null) { - this.addEffect(effect); - } - if (cost != null) { - if (cost instanceof PhyrexianManaCost) { - this.addManaCost((PhyrexianManaCost) cost); - } else { - this.addCost(cost); - } - } + this.addEffect(effect); + this.addCost(cost); } public ActivatedAbilityImpl(Zone zone, Effect effect, Costs costs) { super(AbilityType.ACTIVATED, zone); - if (effect != null) { - this.addEffect(effect); - } + this.addEffect(effect); if (costs != null) { for (Cost cost : costs) { this.addCost(cost); @@ -121,15 +101,13 @@ public abstract class ActivatedAbilityImpl extends AbilityImpl implements Activa this.addEffect(effect); } } - if (cost != null) { - this.addCost(cost); - } + this.addCost(cost); } public ActivatedAbilityImpl(Zone zone, Effects effects, Costs costs) { super(AbilityType.ACTIVATED, zone); - for (Effect effect : effects) { - if (effect != null) { + if (effects != null) { + for (Effect effect : effects) { this.addEffect(effect); } } @@ -205,11 +183,6 @@ public abstract class ActivatedAbilityImpl extends AbilityImpl implements Activa game); asInstant = approvingObject != null; asInstant |= (timing == TimingRule.INSTANT); - Card card = game.getCard(getSourceId()); - if (card != null) { - asInstant |= card.isInstant(game); - asInstant |= card.hasAbility(FlashAbility.getInstance(), game); - } if (!asInstant && !game.canPlaySorcery(playerId)) { return ActivationStatus.getFalse(); } diff --git a/Mage/src/main/java/mage/abilities/Gender.java b/Mage/src/main/java/mage/abilities/Gender.java deleted file mode 100644 index be6f3980065..00000000000 --- a/Mage/src/main/java/mage/abilities/Gender.java +++ /dev/null @@ -1,25 +0,0 @@ -package mage.abilities; - -/** - * Created by IGOUDT on 5-3-2017. - */ -public enum Gender { - MALE("his", "him"), FEMALE("her", "her"), NEUTRAL("its", "it"); - - String personalPronoun; - String possesivePronoun; - - Gender(String possessive, String personal) { - personalPronoun = personal; - possesivePronoun = possessive; - } - - public String getPersonalPronoun() { - return personalPronoun; - } - - public String getPossesivePronoun() { - return possesivePronoun; - } - -} diff --git a/Mage/src/main/java/mage/abilities/Modes.java b/Mage/src/main/java/mage/abilities/Modes.java index b64179d50e9..fc34a14567b 100644 --- a/Mage/src/main/java/mage/abilities/Modes.java +++ b/Mage/src/main/java/mage/abilities/Modes.java @@ -483,7 +483,7 @@ public class Modes extends LinkedHashMap { throw new UnsupportedOperationException("no text available for this selection of min and max modes"); } - if (isEachModeOnlyOnce()) { + if (isEachModeOnlyOnce() && this.getMaxModesFilter() == null) { sb.append(" that hasn't been chosen"); } if (isResetEachTurn()) { diff --git a/Mage/src/main/java/mage/abilities/Pronoun.java b/Mage/src/main/java/mage/abilities/Pronoun.java new file mode 100644 index 00000000000..68cbc89d7b0 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/Pronoun.java @@ -0,0 +1,33 @@ +package mage.abilities; + +/** + * Created by IGOUDT on 5-3-2017. + */ +public enum Pronoun { + HE("he", "him", "his"), + SHE("she", "her", "her"), + THEY("they", "them", "their"), + IT("it", "it", "its"); + + private final String subjective; + private final String objective; + private final String possessive; + + Pronoun(String subjective, String objective, String possessive) { + this.subjective = subjective; + this.objective = objective; + this.possessive = possessive; + } + + public String getSubjective() { + return subjective; + } + + public String getObjective() { + return objective; + } + + public String getPossessive() { + return possessive; + } +} diff --git a/Mage/src/main/java/mage/abilities/abilityword/KinshipAbility.java b/Mage/src/main/java/mage/abilities/abilityword/KinshipAbility.java index cf290341730..f48a73e4336 100644 --- a/Mage/src/main/java/mage/abilities/abilityword/KinshipAbility.java +++ b/Mage/src/main/java/mage/abilities/abilityword/KinshipAbility.java @@ -102,7 +102,7 @@ class KinshipBaseEffect extends OneShotEffect { if (controller.chooseUse(outcome, new StringBuilder("Kinship - Reveal ").append(card.getLogName()).append('?').toString(), source, game)) { controller.revealCards(sourcePermanent.getName(), cards, game); for (Effect effect : kinshipEffects) { - effect.setTargetPointer(new FixedTarget(card.getId())); + effect.setTargetPointer(new FixedTarget(card.getId(), game)); if (effect.getEffectType() == EffectType.ONESHOT) { effect.apply(game, source); } else { diff --git a/Mage/src/main/java/mage/abilities/common/AttacksAllTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/AttacksAllTriggeredAbility.java index 47686abe0a6..e78ab1da0b6 100644 --- a/Mage/src/main/java/mage/abilities/common/AttacksAllTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/AttacksAllTriggeredAbility.java @@ -1,6 +1,5 @@ package mage.abilities.common; -import java.util.UUID; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.Effect; import mage.constants.SetTargetPointer; @@ -11,9 +10,11 @@ import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; + +import java.util.UUID; /** - * * @author LevelX2 */ public class AttacksAllTriggeredAbility extends TriggeredAbilityImpl { @@ -74,18 +75,15 @@ public class AttacksAllTriggeredAbility extends TriggeredAbilityImpl { return false; } } + getEffects().setValue("attacker", permanent); switch (setTargetPointer) { case PERMANENT: - for (Effect effect : getEffects()) { - effect.setTargetPointer(new FixedTarget(permanent, game)); - } + getEffects().setTargetPointer(new FixedTarget(permanent, game)); break; case PLAYER: UUID playerId = controller ? permanent.getControllerId() : game.getCombat().getDefendingPlayerId(permanent.getId(), game); if (playerId != null) { - for (Effect effect : getEffects()) { - effect.setTargetPointer(new FixedTarget(playerId)); - } + getEffects().setTargetPointer(new FixedTarget(playerId)); } break; } @@ -101,10 +99,8 @@ public class AttacksAllTriggeredAbility extends TriggeredAbilityImpl { @Override public String getTriggerPhrase() { - return "Whenever " + (filter.getMessage().startsWith("an") ? "" : "a ") - + filter.getMessage() + " attacks" - + (attacksYouOrYourPlaneswalker ? " you or a planeswalker you control" : "") - + ", " ; + return "Whenever " + CardUtil.addArticle(filter.getMessage()) + " attacks" + + (attacksYouOrYourPlaneswalker ? " you or a planeswalker you control" : "") + ", "; } } diff --git a/Mage/src/main/java/mage/abilities/common/AttacksAloneControlledTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/AttacksAloneControlledTriggeredAbility.java new file mode 100644 index 00000000000..93b64fbc433 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/AttacksAloneControlledTriggeredAbility.java @@ -0,0 +1,74 @@ +package mage.abilities.common; + +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.targetpointer.FixedTarget; + +/** + * @author TheElk801 + */ +public class AttacksAloneControlledTriggeredAbility extends TriggeredAbilityImpl { + + private final FilterPermanent filter; + private final boolean setTargetPointer; + + public AttacksAloneControlledTriggeredAbility(Effect effect) { + this(effect, false); + } + + public AttacksAloneControlledTriggeredAbility(Effect effect, boolean optional) { + this(effect, false, optional); + } + + public AttacksAloneControlledTriggeredAbility(Effect effect, boolean setTargetPointer, boolean optional) { + this(effect, StaticFilters.FILTER_CONTROLLED_A_CREATURE, setTargetPointer, optional); + } + + public AttacksAloneControlledTriggeredAbility(Effect effect, FilterPermanent filter, boolean setTargetPointer, boolean optional) { + super(Zone.BATTLEFIELD, effect, optional); + this.filter = filter; + this.setTargetPointer = setTargetPointer; + } + + private AttacksAloneControlledTriggeredAbility(final AttacksAloneControlledTriggeredAbility ability) { + super(ability); + this.filter = ability.filter; + this.setTargetPointer = ability.setTargetPointer; + } + + @Override + public AttacksAloneControlledTriggeredAbility copy() { + return new AttacksAloneControlledTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DECLARED_ATTACKERS; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (!game.getCombat().attacksAlone()) { + return false; + } + Permanent permanent = game.getPermanent(game.getCombat().getAttackers().get(0)); + if (permanent == null || !filter.match(permanent, getSourceId(), getControllerId(), game)) { + return false; + } + if (setTargetPointer) { + this.getEffects().setTargetPointer(new FixedTarget(permanent, game)); + } + return true; + } + + @Override + public String getTriggerPhrase() { + return "Whenever " + filter.getMessage() + " attacks alone, "; + } +} diff --git a/Mage/src/main/java/mage/abilities/common/AttacksAloneTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/AttacksAloneSourceTriggeredAbility.java similarity index 79% rename from Mage/src/main/java/mage/abilities/common/AttacksAloneTriggeredAbility.java rename to Mage/src/main/java/mage/abilities/common/AttacksAloneSourceTriggeredAbility.java index b9bb15536ec..293eeac4367 100644 --- a/Mage/src/main/java/mage/abilities/common/AttacksAloneTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/AttacksAloneSourceTriggeredAbility.java @@ -7,7 +7,6 @@ import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.Effect; import mage.constants.Zone; import mage.game.Game; -import mage.game.events.GameEvent.EventType; import mage.game.events.GameEvent; import mage.target.targetpointer.FixedTarget; @@ -15,19 +14,19 @@ import mage.target.targetpointer.FixedTarget; * * @author LoneFox */ -public class AttacksAloneTriggeredAbility extends TriggeredAbilityImpl { +public class AttacksAloneSourceTriggeredAbility extends TriggeredAbilityImpl { - public AttacksAloneTriggeredAbility(Effect effect) { + public AttacksAloneSourceTriggeredAbility(Effect effect) { super(Zone.BATTLEFIELD, effect); } - public AttacksAloneTriggeredAbility(final AttacksAloneTriggeredAbility ability) { + public AttacksAloneSourceTriggeredAbility(final AttacksAloneSourceTriggeredAbility ability) { super(ability); } @Override - public AttacksAloneTriggeredAbility copy() { - return new AttacksAloneTriggeredAbility(this); + public AttacksAloneSourceTriggeredAbility copy() { + return new AttacksAloneSourceTriggeredAbility(this); } @Override diff --git a/Mage/src/main/java/mage/abilities/common/AttacksAttachedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/AttacksAttachedTriggeredAbility.java index bdf2123bcae..c7ec728ab2a 100644 --- a/Mage/src/main/java/mage/abilities/common/AttacksAttachedTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/AttacksAttachedTriggeredAbility.java @@ -8,6 +8,7 @@ import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; +import mage.target.targetpointer.FixedTarget; import java.util.Locale; @@ -19,6 +20,7 @@ import java.util.Locale; public class AttacksAttachedTriggeredAbility extends TriggeredAbilityImpl { private AttachmentType attachmentType; + private final boolean setTargetPointer; public AttacksAttachedTriggeredAbility(Effect effect) { this(effect, false); @@ -29,13 +31,19 @@ public class AttacksAttachedTriggeredAbility extends TriggeredAbilityImpl { } public AttacksAttachedTriggeredAbility(Effect effect, AttachmentType attachmentType, boolean optional) { + this(effect, attachmentType, optional, false); + } + + public AttacksAttachedTriggeredAbility(Effect effect, AttachmentType attachmentType, boolean optional, boolean setTargetPointer) { super(Zone.BATTLEFIELD, effect, optional); this.attachmentType = attachmentType; + this.setTargetPointer = setTargetPointer; } public AttacksAttachedTriggeredAbility(final AttacksAttachedTriggeredAbility abiltity) { super(abiltity); this.attachmentType = abiltity.attachmentType; + this.setTargetPointer = abiltity.setTargetPointer; } @Override @@ -54,7 +62,14 @@ public class AttacksAttachedTriggeredAbility extends TriggeredAbilityImpl { if (equipment != null && equipment.getAttachedTo() != null && event.getSourceId().equals(equipment.getAttachedTo())) { getEffects().setValue("sourceId", event.getSourceId()); - getEffects().setValue("attachedPermanent", game.getPermanent(event.getSourceId())); + // TODO: Passing a permanent object like this can cause bugs. May need refactoring to use UUID instead. + // See https://github.com/magefree/mage/issues/8377 + // 11-08-2021: Added a new constructor to set target pointer. Should probably be using this instead. + Permanent attachedPermanent = game.getPermanent(event.getSourceId()); + getEffects().setValue("attachedPermanent", attachedPermanent); + if (setTargetPointer && attachedPermanent != null) { + getEffects().setTargetPointer(new FixedTarget(attachedPermanent, game)); + } return true; } return false; diff --git a/Mage/src/main/java/mage/abilities/common/AttacksWithCreaturesTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/AttacksWithCreaturesTriggeredAbility.java index 66f769353e8..2ea2a0b5138 100644 --- a/Mage/src/main/java/mage/abilities/common/AttacksWithCreaturesTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/AttacksWithCreaturesTriggeredAbility.java @@ -49,14 +49,22 @@ public class AttacksWithCreaturesTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - return isControlledBy(game.getCombat().getAttackingPlayerId()) - && game + if (!isControlledBy(game.getCombat().getAttackingPlayerId())) { + return false; + } + int attackers = game .getCombat() .getAttackers() .stream() .map(game::getPermanent) .filter(permanent -> filter.match(permanent, sourceId, controllerId, game)) - .mapToInt(x -> 1).sum() >= minAttackers; + .mapToInt(x -> 1) + .sum(); + if (attackers < minAttackers) { + return false; + } + getEffects().setValue("attackers", attackers); + return true; } @Override diff --git a/Mage/src/main/java/mage/abilities/common/BecomeDayAsEntersAbility.java b/Mage/src/main/java/mage/abilities/common/BecomeDayAsEntersAbility.java index ac555d784e7..80ec12f0dca 100644 --- a/Mage/src/main/java/mage/abilities/common/BecomeDayAsEntersAbility.java +++ b/Mage/src/main/java/mage/abilities/common/BecomeDayAsEntersAbility.java @@ -2,17 +2,18 @@ package mage.abilities.common; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; +import mage.abilities.hint.common.DayNightHint; import mage.constants.Outcome; import mage.game.Game; /** * @author TheElk801 - * TODO: this is just a placeholder for the actual ability */ public class BecomeDayAsEntersAbility extends EntersBattlefieldAbility { public BecomeDayAsEntersAbility() { super(new BecomeDayEffect()); + this.addHint(DayNightHint.instance); } private BecomeDayAsEntersAbility(final BecomeDayAsEntersAbility ability) { @@ -33,7 +34,7 @@ public class BecomeDayAsEntersAbility extends EntersBattlefieldAbility { class BecomeDayEffect extends OneShotEffect { BecomeDayEffect() { - super(Outcome.Benefit); + super(Outcome.Neutral); } private BecomeDayEffect(final BecomeDayEffect effect) { @@ -47,6 +48,10 @@ class BecomeDayEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - return true; + if (!game.hasDayNight()) { + game.setDaytime(true); + return true; + } + return false; } } diff --git a/Mage/src/main/java/mage/abilities/common/BecomesBlockedAllTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesBlockedAllTriggeredAbility.java index e2c08920b2f..fc6228268ec 100644 --- a/Mage/src/main/java/mage/abilities/common/BecomesBlockedAllTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/BecomesBlockedAllTriggeredAbility.java @@ -19,7 +19,7 @@ public class BecomesBlockedAllTriggeredAbility extends TriggeredAbilityImpl { private final boolean setTargetPointer; public BecomesBlockedAllTriggeredAbility(Effect effect, boolean optional) { - this(effect, optional, StaticFilters.FILTER_PERMANENT_CREATURE_A, false); + this(effect, optional, StaticFilters.FILTER_PERMANENT_A_CREATURE, false); } public BecomesBlockedAllTriggeredAbility(Effect effect, boolean optional, FilterCreaturePermanent filter, boolean setTargetPointer) { diff --git a/Mage/src/main/java/mage/abilities/common/BecomesBlockedByCreatureTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesBlockedByCreatureTriggeredAbility.java index a61cad97f90..80bdb8296db 100644 --- a/Mage/src/main/java/mage/abilities/common/BecomesBlockedByCreatureTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/BecomesBlockedByCreatureTriggeredAbility.java @@ -46,7 +46,7 @@ public class BecomesBlockedByCreatureTriggeredAbility extends TriggeredAbilityIm return false; } for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getSourceId())); + effect.setTargetPointer(new FixedTarget(event.getSourceId(), game)); } return true; } diff --git a/Mage/src/main/java/mage/abilities/common/BecomesBlockedSourceTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesBlockedSourceTriggeredAbility.java index af264909995..e09abd8e27b 100644 --- a/Mage/src/main/java/mage/abilities/common/BecomesBlockedSourceTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/BecomesBlockedSourceTriggeredAbility.java @@ -16,11 +16,11 @@ public class BecomesBlockedSourceTriggeredAbility extends TriggeredAbilityImpl { boolean setTargetPointer; public BecomesBlockedSourceTriggeredAbility(Effect effect, boolean optional) { - this(Zone.BATTLEFIELD, effect, optional, false); + this(effect, optional, false); } - public BecomesBlockedSourceTriggeredAbility(Zone zone, Effect effect, boolean optional, boolean setTargetPointer) { - super(zone, effect, optional); + public BecomesBlockedSourceTriggeredAbility(Effect effect, boolean optional, boolean setTargetPointer) { + super(Zone.BATTLEFIELD, effect, optional); this.setTargetPointer = setTargetPointer; } diff --git a/Mage/src/main/java/mage/abilities/common/BecomesDayOrNightTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesDayOrNightTriggeredAbility.java index e955649af17..237805acbb7 100644 --- a/Mage/src/main/java/mage/abilities/common/BecomesDayOrNightTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/BecomesDayOrNightTriggeredAbility.java @@ -8,7 +8,6 @@ import mage.game.events.GameEvent; /** * @author TheElk801 - * TODO: this is just a placeholder for the actual ability */ public class BecomesDayOrNightTriggeredAbility extends TriggeredAbilityImpl { @@ -26,7 +25,7 @@ public class BecomesDayOrNightTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return false; + return event.getType() == GameEvent.EventType.BECOMES_DAY_NIGHT; } @Override diff --git a/Mage/src/main/java/mage/abilities/common/BecomesTappedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesTappedTriggeredAbility.java index 60169f7b080..828c36ba477 100644 --- a/Mage/src/main/java/mage/abilities/common/BecomesTappedTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/BecomesTappedTriggeredAbility.java @@ -1,21 +1,18 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.common; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.Effect; import mage.constants.Zone; import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; /** - * - * @author Jeff + * @author jeffwadsworth */ public class BecomesTappedTriggeredAbility extends TriggeredAbilityImpl { @@ -23,7 +20,7 @@ public class BecomesTappedTriggeredAbility extends TriggeredAbilityImpl { protected boolean setTargetPointer; public BecomesTappedTriggeredAbility(Effect effect, boolean optional) { - this(effect, optional, new FilterPermanent("a permanent")); + this(effect, optional, StaticFilters.FILTER_PERMANENT_A); } public BecomesTappedTriggeredAbility(Effect effect, boolean optional, FilterPermanent filter) { @@ -31,7 +28,11 @@ public class BecomesTappedTriggeredAbility extends TriggeredAbilityImpl { } public BecomesTappedTriggeredAbility(Effect effect, boolean optional, FilterPermanent filter, boolean setTargetPointer) { - super(Zone.BATTLEFIELD, effect, optional); + this(Zone.BATTLEFIELD, effect, optional, filter, setTargetPointer); + } + + public BecomesTappedTriggeredAbility(Zone zone, Effect effect, boolean optional, FilterPermanent filter, boolean setTargetPointer) { + super(zone, effect, optional); this.filter = filter; this.setTargetPointer = setTargetPointer; } @@ -55,17 +56,17 @@ public class BecomesTappedTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { Permanent permanent = game.getPermanent(event.getTargetId()); - if (filter.match(permanent, getSourceId(), getControllerId(), game)) { - if (setTargetPointer) { - this.getEffects().setTargetPointer(new FixedTarget(event.getTargetId())); - } - return true; + if (!filter.match(permanent, getSourceId(), getControllerId(), game)) { + return false; } - return false; + if (setTargetPointer) { + this.getEffects().setTargetPointer(new FixedTarget(event.getTargetId(), game)); + } + return true; } @Override public String getTriggerPhrase() { - return "Whenever " + filter.getMessage() + " becomes tapped, " ; + return "Whenever " + CardUtil.addArticle(filter.getMessage()) + " becomes tapped, "; } } diff --git a/Mage/src/main/java/mage/abilities/common/BecomesTargetAttachedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesTargetAttachedTriggeredAbility.java index d9e61c229f1..fef952f3a74 100644 --- a/Mage/src/main/java/mage/abilities/common/BecomesTargetAttachedTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/BecomesTargetAttachedTriggeredAbility.java @@ -1,12 +1,14 @@ - package mage.abilities.common; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.Effect; import mage.constants.Zone; +import mage.filter.FilterStackObject; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent.EventType; import mage.game.events.GameEvent; +import mage.game.stack.StackObject; import mage.game.permanent.Permanent; /** @@ -15,19 +17,26 @@ import mage.game.permanent.Permanent; */ public class BecomesTargetAttachedTriggeredAbility extends TriggeredAbilityImpl { + private final FilterStackObject filter; private final String enchantType; public BecomesTargetAttachedTriggeredAbility(Effect effect) { - this(effect, "creature"); + this(effect, StaticFilters.FILTER_SPELL_OR_ABILITY_A); } - public BecomesTargetAttachedTriggeredAbility(Effect effect, String enchantType) { + public BecomesTargetAttachedTriggeredAbility(Effect effect, FilterStackObject filter) { + this(effect, filter, "creature"); + } + + public BecomesTargetAttachedTriggeredAbility(Effect effect, FilterStackObject filter, String enchantType) { super(Zone.BATTLEFIELD, effect); + this.filter = filter.copy(); this.enchantType = enchantType; } public BecomesTargetAttachedTriggeredAbility(final BecomesTargetAttachedTriggeredAbility ability) { super(ability); + this.filter = ability.filter.copy(); this.enchantType = ability.enchantType; } @@ -44,8 +53,10 @@ public class BecomesTargetAttachedTriggeredAbility extends TriggeredAbilityImpl @Override public boolean checkTrigger(GameEvent event, Game game) { Permanent enchantment = game.getPermanent(sourceId); + StackObject sourceObject = game.getStack().getStackObject(event.getSourceId()); if (enchantment != null && enchantment.getAttachedTo() != null) { - if (event.getTargetId().equals(enchantment.getAttachedTo())) { + if (event.getTargetId().equals(enchantment.getAttachedTo()) + && filter.match(sourceObject, getSourceId(), getControllerId(), game)) { return true; } } @@ -54,6 +65,6 @@ public class BecomesTargetAttachedTriggeredAbility extends TriggeredAbilityImpl @Override public String getTriggerPhrase() { - return "When enchanted " + enchantType + " becomes the target of a spell or ability, " ; + return "When enchanted " + enchantType + " becomes the target of " + filter.getMessage() + ", "; } } diff --git a/Mage/src/main/java/mage/abilities/common/BlocksOrBecomesBlockedSourceTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BlocksOrBecomesBlockedSourceTriggeredAbility.java index ba64cb6b26d..3cefef7c511 100644 --- a/Mage/src/main/java/mage/abilities/common/BlocksOrBecomesBlockedSourceTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/BlocksOrBecomesBlockedSourceTriggeredAbility.java @@ -21,7 +21,11 @@ public class BlocksOrBecomesBlockedSourceTriggeredAbility extends TriggeredAbili protected boolean setTargetPointer; public BlocksOrBecomesBlockedSourceTriggeredAbility(Effect effect, boolean optional) { - this(effect, StaticFilters.FILTER_PERMANENT_CREATURE, optional, null, true); + this(effect, optional, true); + } + + public BlocksOrBecomesBlockedSourceTriggeredAbility(Effect effect, boolean optional, boolean setTargetPointer) { + this(effect, StaticFilters.FILTER_PERMANENT_CREATURE, optional, null, setTargetPointer); } public BlocksOrBecomesBlockedSourceTriggeredAbility(Effect effect, FilterPermanent filter, boolean optional) { diff --git a/Mage/src/main/java/mage/abilities/common/BlocksOrBecomesBlockedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BlocksOrBecomesBlockedTriggeredAbility.java deleted file mode 100644 index b592d1c08bf..00000000000 --- a/Mage/src/main/java/mage/abilities/common/BlocksOrBecomesBlockedTriggeredAbility.java +++ /dev/null @@ -1,95 +0,0 @@ - -package mage.abilities.common; - -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; -import mage.constants.Zone; -import mage.filter.FilterPermanent; -import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.target.targetpointer.FixedTarget; - -/** - * @author North, Loki - */ -public class BlocksOrBecomesBlockedTriggeredAbility extends TriggeredAbilityImpl { - - protected FilterPermanent filter; - protected String rule; - protected boolean setTargetPointer; - - public BlocksOrBecomesBlockedTriggeredAbility(Effect effect, boolean optional) { - this(effect, StaticFilters.FILTER_PERMANENT_CREATURE, optional, null, true); - } - - public BlocksOrBecomesBlockedTriggeredAbility(Effect effect, FilterPermanent filter, boolean optional) { - this(effect, filter, optional, null, true); - } - - public BlocksOrBecomesBlockedTriggeredAbility(Effect effect, FilterPermanent filter, boolean optional, String rule) { - this(effect, filter, optional, rule, true); - } - - public BlocksOrBecomesBlockedTriggeredAbility(Effect effect, FilterPermanent filter, boolean optional, String rule, boolean setTargetPointer) { - super(Zone.BATTLEFIELD, effect, optional); - this.filter = filter; - this.rule = rule; - this.setTargetPointer = setTargetPointer; - } - - public BlocksOrBecomesBlockedTriggeredAbility(final BlocksOrBecomesBlockedTriggeredAbility ability) { - super(ability); - this.filter = ability.filter; - this.rule = ability.rule; - this.setTargetPointer = ability.setTargetPointer; - - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.BLOCKER_DECLARED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getSourceId().equals(this.getSourceId())) { - Permanent blocked = game.getPermanent(event.getTargetId()); - if (filter.match(blocked, game)) { - if (setTargetPointer) { - this.getEffects().setTargetPointer(new FixedTarget(blocked, game)); - } - return true; - } - } - if (event.getTargetId().equals(this.getSourceId())) { - Permanent blocker = game.getPermanent(event.getSourceId()); - if (filter.match(blocker, game)) { - if (setTargetPointer) { - this.getEffects().setTargetPointer(new FixedTarget(blocker, game)); - } - return true; - } - } - return false; - } - - @Override - public String getRule() { - if (rule != null) { - return rule; - } - return super.getRule(); - } - - @Override - public String getTriggerPhrase() { - return "Whenever {this} blocks or becomes blocked" + (setTargetPointer ? " by a " + filter.getMessage() : "") + ", "; - } - - @Override - public BlocksOrBecomesBlockedTriggeredAbility copy() { - return new BlocksOrBecomesBlockedTriggeredAbility(this); - } -} diff --git a/Mage/src/main/java/mage/abilities/common/BlocksTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BlocksTriggeredAbility.java deleted file mode 100644 index 4b63f816249..00000000000 --- a/Mage/src/main/java/mage/abilities/common/BlocksTriggeredAbility.java +++ /dev/null @@ -1,67 +0,0 @@ - -package mage.abilities.common; - -import mage.constants.Zone; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.target.targetpointer.FixedTarget; - -/** - * - * @author North - */ -public class BlocksTriggeredAbility extends TriggeredAbilityImpl { - - private boolean setTargetPointer; - private boolean once = false; - - public BlocksTriggeredAbility(Effect effect, boolean optional) { - this(effect, optional, false); - } - - public BlocksTriggeredAbility(Effect effect, boolean optional, boolean setTargetPointer) { - this(effect, optional, setTargetPointer, false); - } - - public BlocksTriggeredAbility(Effect effect, boolean optional, boolean setTargetPointer, boolean once) { - super(Zone.BATTLEFIELD, effect, optional); - this.setTargetPointer = setTargetPointer; - this.once = once; - } - - public BlocksTriggeredAbility(final BlocksTriggeredAbility ability) { - super(ability); - this.setTargetPointer = ability.setTargetPointer; - this.once = ability.once; - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.BLOCKER_DECLARED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getSourceId().equals(this.getSourceId())) { - if (setTargetPointer) { - for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getTargetId())); - } - } - return true; - } - return false; - } - - @Override - public String getTriggerPhrase() { - return "When" + (once ? "" : "ever") + " {this} blocks" + (setTargetPointer ? " a creature, " : ", ") ; - } - - @Override - public BlocksTriggeredAbility copy() { - return new BlocksTriggeredAbility(this); - } -} diff --git a/Mage/src/main/java/mage/abilities/common/CrewIncreasedPowerAbility.java b/Mage/src/main/java/mage/abilities/common/CrewIncreasedPowerAbility.java new file mode 100644 index 00000000000..29411d66f74 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/CrewIncreasedPowerAbility.java @@ -0,0 +1,28 @@ +package mage.abilities.common; + +import mage.abilities.StaticAbility; +import mage.abilities.effects.common.InfoEffect; +import mage.constants.Zone; + +/** + * @author TheElk801 + */ +public class CrewIncreasedPowerAbility extends StaticAbility { + + public CrewIncreasedPowerAbility() { + this("{this}"); + } + + public CrewIncreasedPowerAbility(String selfName) { + super(Zone.BATTLEFIELD, new InfoEffect(selfName + " crews Vehicles as though its power were 2 greater.")); + } + + private CrewIncreasedPowerAbility(final CrewIncreasedPowerAbility ability) { + super(ability); + } + + @Override + public CrewIncreasedPowerAbility copy() { + return new CrewIncreasedPowerAbility(this); + } +} diff --git a/Mage/src/main/java/mage/abilities/common/DealsCombatDamageTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DealsCombatDamageTriggeredAbility.java index b5dfec5711a..96ecbbcdd1f 100644 --- a/Mage/src/main/java/mage/abilities/common/DealsCombatDamageTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/DealsCombatDamageTriggeredAbility.java @@ -1,5 +1,3 @@ - - package mage.abilities.common; import mage.abilities.TriggeredAbilityImpl; @@ -26,13 +24,13 @@ public class DealsCombatDamageTriggeredAbility extends TriggeredAbilityImpl { } public DealsCombatDamageTriggeredAbility(final DealsCombatDamageTriggeredAbility ability) { - super(ability); - this.usedInPhase = ability.usedInPhase; + super(ability); + this.usedInPhase = ability.usedInPhase; } @Override public DealsCombatDamageTriggeredAbility copy() { - return new DealsCombatDamageTriggeredAbility(this); + return new DealsCombatDamageTriggeredAbility(this); } @Override @@ -47,10 +45,10 @@ public class DealsCombatDamageTriggeredAbility extends TriggeredAbilityImpl { && event.getSourceId().equals(this.sourceId) && ((DamagedEvent) event).isCombatDamage()) { usedInPhase = true; + getEffects().setValue("damage", event.getAmount()); return true; - } - if (event.getType()== GameEvent.EventType.COMBAT_DAMAGE_STEP_PRE) { + if (event.getType() == GameEvent.EventType.COMBAT_DAMAGE_STEP_PRE) { usedInPhase = false; } return false; @@ -58,7 +56,6 @@ public class DealsCombatDamageTriggeredAbility extends TriggeredAbilityImpl { @Override public String getTriggerPhrase() { - return "Whenever {this} deals combat damage, " ; + return "Whenever {this} deals combat damage, "; } - } diff --git a/Mage/src/main/java/mage/abilities/common/DealsDamageToACreatureAttachedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DealsDamageToACreatureAttachedTriggeredAbility.java index 1af62161b74..60e86de639f 100644 --- a/Mage/src/main/java/mage/abilities/common/DealsDamageToACreatureAttachedTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/DealsDamageToACreatureAttachedTriggeredAbility.java @@ -51,7 +51,7 @@ public class DealsDamageToACreatureAttachedTriggeredAbility extends TriggeredAbi && attachment.isAttachedTo(event.getSourceId())) { if (setTargetPointer) { for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getTargetId())); + effect.setTargetPointer(new FixedTarget(event.getTargetId(), game)); effect.setValue("damage", event.getAmount()); } } diff --git a/Mage/src/main/java/mage/abilities/common/DealsDamageToACreatureTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DealsDamageToACreatureTriggeredAbility.java index 53bbf49c146..7e1386d93e7 100644 --- a/Mage/src/main/java/mage/abilities/common/DealsDamageToACreatureTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/DealsDamageToACreatureTriggeredAbility.java @@ -1,9 +1,9 @@ package mage.abilities.common; -import mage.constants.Zone; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.Effect; +import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.events.DamagedEvent; @@ -12,7 +12,6 @@ import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; /** - * * @author LevelX */ public class DealsDamageToACreatureTriggeredAbility extends TriggeredAbilityImpl { @@ -43,7 +42,7 @@ public class DealsDamageToACreatureTriggeredAbility extends TriggeredAbilityImpl public DealsDamageToACreatureTriggeredAbility copy() { return new DealsDamageToACreatureTriggeredAbility(this); } - + @Override public boolean checkEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; @@ -60,10 +59,8 @@ public class DealsDamageToACreatureTriggeredAbility extends TriggeredAbilityImpl } } if (setTargetPointer) { - for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getTargetId())); - effect.setValue("damage", event.getAmount()); - } + this.getEffects().setTargetPointer(new FixedTarget(event.getTargetId(), game)); + this.getEffects().setValue("damage", event.getAmount()); } return true; } diff --git a/Mage/src/main/java/mage/abilities/common/DealsDamageToAPlayerAttachedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DealsDamageToAPlayerAttachedTriggeredAbility.java index e8fe47c2c66..c97a149e373 100644 --- a/Mage/src/main/java/mage/abilities/common/DealsDamageToAPlayerAttachedTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/DealsDamageToAPlayerAttachedTriggeredAbility.java @@ -77,8 +77,8 @@ public class DealsDamageToAPlayerAttachedTriggeredAbility extends TriggeredAbili || p == null || !p.getAttachments().contains(this.getSourceId())) { return false; } + getEffects().setValue("damage", event.getAmount()); if (setFixedTargetPointer) { - getEffects().setValue("damage", event.getAmount()); getEffects().setTargetPointer(new FixedTarget(event.getPlayerId())); } return true; diff --git a/Mage/src/main/java/mage/abilities/common/DealsDamageToOpponentTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DealsDamageToOpponentTriggeredAbility.java index a733c5c0acb..9af79f042d8 100644 --- a/Mage/src/main/java/mage/abilities/common/DealsDamageToOpponentTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/DealsDamageToOpponentTriggeredAbility.java @@ -62,7 +62,7 @@ public class DealsDamageToOpponentTriggeredAbility extends TriggeredAbilityImpl } if(setTargetPointer) { for (Effect effect : getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getTargetId())); + effect.setTargetPointer(new FixedTarget(event.getTargetId(), game)); effect.setValue("damage", event.getAmount()); } } diff --git a/Mage/src/main/java/mage/abilities/common/DealtDamageAndDiedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DealtDamageAndDiedTriggeredAbility.java index 8ef3f6d6725..a80afc10131 100644 --- a/Mage/src/main/java/mage/abilities/common/DealtDamageAndDiedTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/DealtDamageAndDiedTriggeredAbility.java @@ -67,7 +67,7 @@ public class DealtDamageAndDiedTriggeredAbility extends TriggeredAbilityImpl { if (damageDealt) { if (this.setTargetPointer == SetTargetPointer.PERMANENT) { for (Effect effect : getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getTargetId())); + effect.setTargetPointer(new FixedTarget(event.getTargetId(), game)); } } return true; diff --git a/Mage/src/main/java/mage/abilities/common/DealtDamageAttachedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DealtDamageAttachedTriggeredAbility.java index 5c8d338cca0..e5ca0a8b8dc 100644 --- a/Mage/src/main/java/mage/abilities/common/DealtDamageAttachedTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/DealtDamageAttachedTriggeredAbility.java @@ -53,7 +53,7 @@ public class DealtDamageAttachedTriggeredAbility extends TriggeredAbilityImpl { effect.setValue("damage", event.getAmount()); switch(setTargetPointer) { case PERMANENT: - effect.setTargetPointer(new FixedTarget(targetId)); + effect.setTargetPointer(new FixedTarget(targetId, game)); break; case PLAYER: effect.setTargetPointer(new FixedTarget(game.getPermanentOrLKIBattlefield(targetId).getControllerId())); diff --git a/Mage/src/main/java/mage/abilities/common/DealtDamageToSourceTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DealtDamageToSourceTriggeredAbility.java index 97fb969e584..dea7681fae7 100644 --- a/Mage/src/main/java/mage/abilities/common/DealtDamageToSourceTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/DealtDamageToSourceTriggeredAbility.java @@ -1,14 +1,10 @@ - package mage.abilities.common; -import java.util.UUID; - import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.Effect; import mage.constants.AbilityWord; import mage.constants.Zone; import mage.game.Game; -import mage.game.events.DamagedEvent; import mage.game.events.DamagedPermanentBatchEvent; import mage.game.events.GameEvent; @@ -17,19 +13,12 @@ import mage.game.events.GameEvent; */ public class DealtDamageToSourceTriggeredAbility extends TriggeredAbilityImpl { - private final boolean useValue; - public DealtDamageToSourceTriggeredAbility(Effect effect, boolean optional) { this(effect, optional, false); } public DealtDamageToSourceTriggeredAbility(Effect effect, boolean optional, boolean enrage) { - this(effect, optional, enrage, false); - } - - public DealtDamageToSourceTriggeredAbility(Effect effect, boolean optional, boolean enrage, boolean useValue) { super(Zone.BATTLEFIELD, effect, optional); - this.useValue = useValue; if (enrage) { this.setAbilityWord(AbilityWord.ENRAGE); } @@ -37,7 +26,6 @@ public class DealtDamageToSourceTriggeredAbility extends TriggeredAbilityImpl { public DealtDamageToSourceTriggeredAbility(final DealtDamageToSourceTriggeredAbility ability) { super(ability); - this.useValue = ability.useValue; } @Override @@ -52,31 +40,18 @@ public class DealtDamageToSourceTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - - if (event == null || game == null || this.getSourceId() == null) { + DamagedPermanentBatchEvent dEvent = (DamagedPermanentBatchEvent) event; + int damage = dEvent + .getEvents() + .stream() + .filter(damagedEvent -> getSourceId().equals(damagedEvent.getTargetId())) + .mapToInt(GameEvent::getAmount) + .sum(); + if (damage < 1) { return false; } - - int damage = 0; - DamagedPermanentBatchEvent dEvent = (DamagedPermanentBatchEvent) event; - for (DamagedEvent damagedEvent : dEvent.getEvents()) { - UUID targetID = damagedEvent.getTargetId(); - if (targetID == null) { - continue; - } - - if (targetID == this.getSourceId()) { - damage += damagedEvent.getAmount(); - } - } - - if (damage > 0) { - if (this.useValue) { - this.getEffects().setValue("damage", damage); - } - return true; - } - return false; + this.getEffects().setValue("damage", damage); + return true; } @Override diff --git a/Mage/src/main/java/mage/abilities/common/DiesAttachedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DiesAttachedTriggeredAbility.java index 204d6119a4c..3b94b15b382 100644 --- a/Mage/src/main/java/mage/abilities/common/DiesAttachedTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/DiesAttachedTriggeredAbility.java @@ -110,6 +110,19 @@ public class DiesAttachedTriggeredAbility extends TriggeredAbilityImpl { } } } + // set targetpointer to the creature that died + if (setTargetPointer == SetTargetPointer.CARD + || setTargetPointer == SetTargetPointer.PERMANENT) { + Permanent attachment = game.getPermanentOrLKIBattlefield(getSourceId()); + if (attachment != null + && attachment.getAttachedTo() != null) { + Permanent attachedTo = (Permanent) game.getLastKnownInformation(attachment.getAttachedTo(), + Zone.BATTLEFIELD, attachment.getAttachedToZoneChangeCounter()); + if (attachedTo != null) { + getEffects().setTargetPointer(new FixedTarget(attachedTo.getId())); + } + } + } return true; } @@ -125,7 +138,7 @@ public class DiesAttachedTriggeredAbility extends TriggeredAbilityImpl { if (diesRuleText) { sb.append(" dies, "); } else { - sb.append(" is put into graveyard, "); + sb.append(" is put into a graveyard, "); } return sb.toString(); } diff --git a/Mage/src/main/java/mage/abilities/common/EnchantedCreatureBlockedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/EnchantedCreatureBlockedTriggeredAbility.java deleted file mode 100644 index bcae0ec90ec..00000000000 --- a/Mage/src/main/java/mage/abilities/common/EnchantedCreatureBlockedTriggeredAbility.java +++ /dev/null @@ -1,52 +0,0 @@ -package mage.abilities.common; - -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.target.targetpointer.FixedTarget; - -/** - * - * @author L_J - */ -public class EnchantedCreatureBlockedTriggeredAbility extends TriggeredAbilityImpl { - - public EnchantedCreatureBlockedTriggeredAbility(Effect effect, boolean optional) { - super(Zone.BATTLEFIELD, effect, optional); - } - - public EnchantedCreatureBlockedTriggeredAbility(final EnchantedCreatureBlockedTriggeredAbility ability) { - super(ability); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.CREATURE_BLOCKED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent equipment = game.getPermanent(sourceId); - if (equipment != null && equipment.getAttachedTo() != null) { - Permanent equipped = game.getPermanent(equipment.getAttachedTo()); - if (equipped.getId().equals(event.getTargetId())) { - getEffects().get(1).setTargetPointer(new FixedTarget(equipped, game)); - return true; - } - } - return false; - } - - @Override - public String getTriggerPhrase() { - return "Whenever enchanted creature becomes blocked, " ; - } - - @Override - public EnchantedCreatureBlockedTriggeredAbility copy() { - return new EnchantedCreatureBlockedTriggeredAbility(this); - } -} diff --git a/Mage/src/main/java/mage/abilities/common/EntersBattlefieldOrAttacksAllTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/EntersBattlefieldOrAttacksAllTriggeredAbility.java index 900d7f8d5cd..1a92950bcd3 100644 --- a/Mage/src/main/java/mage/abilities/common/EntersBattlefieldOrAttacksAllTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/EntersBattlefieldOrAttacksAllTriggeredAbility.java @@ -103,7 +103,7 @@ public class EntersBattlefieldOrAttacksAllTriggeredAbility extends TriggeredAbil for (Effect effect : this.getEffects()) { switch (setTargetPointer) { case PERMANENT: - effect.setTargetPointer(new FixedTarget(attacker.getId())); + effect.setTargetPointer(new FixedTarget(attacker.getId(), game)); break; case PLAYER: UUID playerId = controlledText ? attacker.getControllerId() : game.getCombat().getDefendingPlayerId(attacker.getId(), game); diff --git a/Mage/src/main/java/mage/abilities/common/ExploitCreatureTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/ExploitCreatureTriggeredAbility.java index 25494195784..1c9f7501967 100644 --- a/Mage/src/main/java/mage/abilities/common/ExploitCreatureTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/ExploitCreatureTriggeredAbility.java @@ -11,12 +11,15 @@ import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; /** - * * @author LevelX2 */ public class ExploitCreatureTriggeredAbility extends TriggeredAbilityImpl { - private SetTargetPointer setTargetPointer; + private final SetTargetPointer setTargetPointer; + + public ExploitCreatureTriggeredAbility(Effect effect) { + this(effect, false); + } public ExploitCreatureTriggeredAbility(Effect effect, boolean optional) { this(effect, optional, SetTargetPointer.NONE); @@ -65,7 +68,7 @@ public class ExploitCreatureTriggeredAbility extends TriggeredAbilityImpl { if (event.getSourceId().equals(getSourceId())) { for (Effect effect : getEffects()) { if (setTargetPointer == SetTargetPointer.PERMANENT) { - effect.setTargetPointer(new FixedTarget(event.getTargetId())); + effect.setTargetPointer(new FixedTarget(event.getTargetId(), game)); } } return true; @@ -75,6 +78,6 @@ public class ExploitCreatureTriggeredAbility extends TriggeredAbilityImpl { @Override public String getTriggerPhrase() { - return "When {this} exploits a creature, " ; + return "When {this} exploits a creature, "; } } diff --git a/Mage/src/main/java/mage/abilities/common/GoadAttachedAbility.java b/Mage/src/main/java/mage/abilities/common/GoadAttachedAbility.java deleted file mode 100644 index 3f5c6b43a96..00000000000 --- a/Mage/src/main/java/mage/abilities/common/GoadAttachedAbility.java +++ /dev/null @@ -1,78 +0,0 @@ -package mage.abilities.common; - -import mage.abilities.Ability; -import mage.abilities.StaticAbility; -import mage.abilities.effects.Effect; -import mage.abilities.effects.RestrictionEffect; -import mage.abilities.effects.common.combat.AttacksIfAbleAttachedEffect; -import mage.constants.AttachmentType; -import mage.constants.Duration; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.permanent.Permanent; - -import java.util.UUID; - -/** - * @author TheElk801 - */ -public class GoadAttachedAbility extends StaticAbility { - - public GoadAttachedAbility(Effect... effects) { - super(Zone.BATTLEFIELD, null); - for (Effect effect : effects) { - this.addEffect(effect); - } - this.addEffect(new AttacksIfAbleAttachedEffect( - Duration.WhileOnBattlefield, AttachmentType.AURA - ).setText((getEffects().size() > 1 ? ", " : " ") + "and is goaded. (It attacks each combat if able")); - this.addEffect(new GoadAttackEffect()); - } - - private GoadAttachedAbility(final GoadAttachedAbility ability) { - super(ability); - } - - @Override - public GoadAttachedAbility copy() { - return new GoadAttachedAbility(this); - } -} - -class GoadAttackEffect extends RestrictionEffect { - - GoadAttackEffect() { - super(Duration.WhileOnBattlefield); - staticText = "and attacks a player other than you if able.)"; - } - - private GoadAttackEffect(final GoadAttackEffect effect) { - super(effect); - } - - @Override - public GoadAttackEffect copy() { - return new GoadAttackEffect(this); - } - - @Override - public boolean applies(Permanent permanent, Ability source, Game game) { - Permanent attachment = game.getPermanent(source.getSourceId()); - return attachment != null && attachment.getAttachedTo() != null - && permanent.getId().equals(attachment.getAttachedTo()); - } - - @Override - public boolean canAttack(Permanent attacker, UUID defenderId, Ability source, Game game, boolean canUseChooseDialogs) { - if (defenderId == null - || game.getState().getPlayersInRange(attacker.getControllerId(), game).size() == 2) { // just 2 players left, so it may attack you - return true; - } - // A planeswalker controlled by the controller is the defender - if (game.getPermanent(defenderId) != null) { - return !game.getPermanent(defenderId).getControllerId().equals(source.getControllerId()); - } - // The controller is the defender - return !defenderId.equals(source.getControllerId()); - } -} diff --git a/Mage/src/main/java/mage/abilities/common/OpponentSacrificesPermanentTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/OpponentSacrificesPermanentTriggeredAbility.java index 7d1075f9eb6..7cefb76c257 100644 --- a/Mage/src/main/java/mage/abilities/common/OpponentSacrificesPermanentTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/OpponentSacrificesPermanentTriggeredAbility.java @@ -31,7 +31,7 @@ public class OpponentSacrificesPermanentTriggeredAbility extends TriggeredAbilit MageObject object = game.getLastKnownInformation(event.getTargetId(), Zone.BATTLEFIELD); if (object instanceof Permanent) { for (Effect effect : getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getTargetId())); + effect.setTargetPointer(new FixedTarget(event.getTargetId(), game)); } return true; } diff --git a/Mage/src/main/java/mage/abilities/common/PayMoreToCastAsThoughtItHadFlashAbility.java b/Mage/src/main/java/mage/abilities/common/PayMoreToCastAsThoughtItHadFlashAbility.java index 7595f01dc4a..52e6f6e7695 100644 --- a/Mage/src/main/java/mage/abilities/common/PayMoreToCastAsThoughtItHadFlashAbility.java +++ b/Mage/src/main/java/mage/abilities/common/PayMoreToCastAsThoughtItHadFlashAbility.java @@ -10,7 +10,6 @@ import mage.constants.Zone; import mage.util.CardUtil; /** - * * @author LevelX2 */ public class PayMoreToCastAsThoughtItHadFlashAbility extends SpellAbility { @@ -42,7 +41,7 @@ public class PayMoreToCastAsThoughtItHadFlashAbility extends SpellAbility { @Override public String getRule() { - return "You may cast {this} as though it had flash if you pay " + costsToAdd.getText() + " more to cast it. (You may cast it any time you could cast an instant.)"; + return "You may cast this spell as though it had flash if you pay " + costsToAdd.getText() + " more to cast it. (You may cast it any time you could cast an instant.)"; } } diff --git a/Mage/src/main/java/mage/abilities/common/PlaneswalkerEntersWithLoyaltyCountersAbility.java b/Mage/src/main/java/mage/abilities/common/PlaneswalkerEntersWithLoyaltyCountersAbility.java deleted file mode 100644 index db7355e37ab..00000000000 --- a/Mage/src/main/java/mage/abilities/common/PlaneswalkerEntersWithLoyaltyCountersAbility.java +++ /dev/null @@ -1,39 +0,0 @@ -package mage.abilities.common; - -import mage.abilities.effects.EntersBattlefieldEffect; -import mage.abilities.effects.common.counter.AddCountersSourceEffect; -import mage.counters.CounterType; - -/** - * @author LevelX2 - */ -public class PlaneswalkerEntersWithLoyaltyCountersAbility extends EntersBattlefieldAbility { - - private int startingLoyalty; - - public PlaneswalkerEntersWithLoyaltyCountersAbility(int loyalty) { - super(new AddCountersSourceEffect(CounterType.LOYALTY.createInstance(loyalty))); - startingLoyalty = loyalty; - setRuleVisible(false); - } - - public PlaneswalkerEntersWithLoyaltyCountersAbility(final PlaneswalkerEntersWithLoyaltyCountersAbility ability) { - super(ability); - startingLoyalty = ability.startingLoyalty; - } - - public int getStartingLoyalty() { - return startingLoyalty; - } - - public void setStartingLoyalty(int startingLoyalty) { - this.startingLoyalty = startingLoyalty; - this.getEffects().clear(); - this.addEffect(new EntersBattlefieldEffect(new AddCountersSourceEffect(CounterType.LOYALTY.createInstance(startingLoyalty)))); - } - - @Override - public PlaneswalkerEntersWithLoyaltyCountersAbility copy() { - return new PlaneswalkerEntersWithLoyaltyCountersAbility(this); - } -} diff --git a/Mage/src/main/java/mage/abilities/common/SacrificePermanentTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/SacrificePermanentTriggeredAbility.java index 8747f3c093d..2a0925c3340 100644 --- a/Mage/src/main/java/mage/abilities/common/SacrificePermanentTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/SacrificePermanentTriggeredAbility.java @@ -27,7 +27,11 @@ public class SacrificePermanentTriggeredAbility extends TriggeredAbilityImpl { } public SacrificePermanentTriggeredAbility(Effect effect, FilterPermanent filter, boolean setTargetPointer) { - super(Zone.BATTLEFIELD, effect); + this(effect, filter, setTargetPointer, false); + } + + public SacrificePermanentTriggeredAbility(Effect effect, FilterPermanent filter, boolean setTargetPointer, boolean optional) { + super(Zone.BATTLEFIELD, effect, optional); setLeavesTheBattlefieldTrigger(true); this.filter = filter; this.setTargetPointer = setTargetPointer; diff --git a/Mage/src/main/java/mage/abilities/common/SacrificeSourceTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/SacrificeSourceTriggeredAbility.java index 0e230578f91..cd25ab6809e 100644 --- a/Mage/src/main/java/mage/abilities/common/SacrificeSourceTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/SacrificeSourceTriggeredAbility.java @@ -1,5 +1,3 @@ - - package mage.abilities.common; import mage.abilities.TriggeredAbilityImpl; @@ -7,20 +5,28 @@ import mage.abilities.effects.Effect; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; +import mage.target.targetpointer.FixedTarget; /** - * * @author LevelX2 */ public class SacrificeSourceTriggeredAbility extends TriggeredAbilityImpl { + private final boolean setTargetPointer; + public SacrificeSourceTriggeredAbility(Effect effect, boolean optional) { + this(effect, optional, false); + } + + public SacrificeSourceTriggeredAbility(Effect effect, boolean optional, boolean setTargetPointer) { super(Zone.ALL, effect, optional); + this.setTargetPointer = setTargetPointer; } public SacrificeSourceTriggeredAbility(final SacrificeSourceTriggeredAbility ability) { super(ability); + this.setTargetPointer = ability.setTargetPointer; } @Override @@ -35,11 +41,17 @@ public class SacrificeSourceTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - return event.getTargetId().equals(this.getSourceId()); + if (!event.getTargetId().equals(this.getSourceId())) { + return false; + } + if (this.setTargetPointer) { + this.getEffects().setTargetPointer(new FixedTarget(event.getTargetId(), game)); + } + return true; } @Override public String getTriggerPhrase() { - return "When you sacrifice {this}, " ; + return "When you sacrifice {this}, "; } } diff --git a/Mage/src/main/java/mage/abilities/common/SagaAbility.java b/Mage/src/main/java/mage/abilities/common/SagaAbility.java index 977c76f3894..2287d992f94 100644 --- a/Mage/src/main/java/mage/abilities/common/SagaAbility.java +++ b/Mage/src/main/java/mage/abilities/common/SagaAbility.java @@ -1,6 +1,7 @@ package mage.abilities.common; import mage.abilities.Ability; +import mage.abilities.Mode; import mage.abilities.TriggeredAbility; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.Effect; @@ -27,10 +28,16 @@ import java.util.Arrays; public class SagaAbility extends SimpleStaticAbility { private final SagaChapter maxChapter; + private final boolean showSacText; + + public SagaAbility(Card card) { + this(card, SagaChapter.CHAPTER_III); + } public SagaAbility(Card card, SagaChapter maxChapter) { super(Zone.ALL, new AddCountersSourceEffect(CounterType.LORE.createInstance())); this.maxChapter = maxChapter; + this.showSacText = card.getSecondCardFace() == null; this.setRuleVisible(true); this.setRuleAtTheTop(true); Ability ability = new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.LORE.createInstance())); @@ -41,6 +48,7 @@ public class SagaAbility extends SimpleStaticAbility { public SagaAbility(final SagaAbility ability) { super(ability); this.maxChapter = ability.maxChapter; + this.showSacText = ability.showSacText; } public void addChapterEffect(Card card, SagaChapter chapter, Effect... effects) { @@ -71,10 +79,9 @@ public class SagaAbility extends SimpleStaticAbility { addChapterEffect(card, fromChapter, toChapter, effects, targets, false); } - public void addChapterEffect(Card card, SagaChapter fromChapter, SagaChapter toChapter, Effects effects, Targets targets, boolean optional) { - ChapterTriggeredAbility ability; + public void addChapterEffect(Card card, SagaChapter fromChapter, SagaChapter toChapter, Effects effects, Targets targets, boolean optional, Mode... modes) { for (int i = fromChapter.getNumber(); i <= toChapter.getNumber(); i++) { - ability = new ChapterTriggeredAbility(null, SagaChapter.getChapter(i), toChapter, optional); + ChapterTriggeredAbility ability = new ChapterTriggeredAbility(null, SagaChapter.getChapter(i), toChapter, optional); for (Effect effect : effects) { if (effect != null) { ability.addEffect(effect.copy()); @@ -85,6 +92,9 @@ public class SagaAbility extends SimpleStaticAbility { ability.addTarget(target.copy()); } } + for (Mode mode : modes) { + ability.addMode(mode.copy()); + } if (i > fromChapter.getNumber()) { ability.setRuleVisible(false); } @@ -98,7 +108,8 @@ public class SagaAbility extends SimpleStaticAbility { @Override public String getRule() { - return "(As this Saga enters and after your draw step, add a lore counter. Sacrifice after " + maxChapter.toString() + ".) "; + return "(As this Saga enters and after your draw step, add a lore counter." + + (showSacText ? " Sacrifice after " + maxChapter.toString() + '.' : "") + ") "; } @Override diff --git a/Mage/src/main/java/mage/abilities/common/SpellCastControllerTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/SpellCastControllerTriggeredAbility.java index 911ead8e714..775efe507ac 100644 --- a/Mage/src/main/java/mage/abilities/common/SpellCastControllerTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/SpellCastControllerTriggeredAbility.java @@ -69,8 +69,8 @@ public class SpellCastControllerTriggeredAbility extends TriggeredAbilityImpl { if (event.getPlayerId().equals(this.getControllerId())) { Spell spell = game.getStack().getSpell(event.getTargetId()); if (filter.match(spell, getSourceId(), getControllerId(), game)) { + this.getEffects().setValue("spellCast", spell); if (rememberSource) { - this.getEffects().setValue("spellCast", spell); if (rememberSourceAsCard) { this.getEffects().setTargetPointer(new FixedTarget(spell.getCard().getId(), game)); } else { diff --git a/Mage/src/main/java/mage/abilities/common/TappedNotAttackingTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/TappedNotAttackingTriggeredAbility.java new file mode 100644 index 00000000000..3a136f02b0e --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/TappedNotAttackingTriggeredAbility.java @@ -0,0 +1,48 @@ +package mage.abilities.common; + +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; + +/** + * @author TheElk801 + */ +public class TappedNotAttackingTriggeredAbility extends TriggeredAbilityImpl { + + public TappedNotAttackingTriggeredAbility(Effect effect, boolean optional) { + super(Zone.BATTLEFIELD, effect, optional); + } + + private TappedNotAttackingTriggeredAbility(final TappedNotAttackingTriggeredAbility ability) { + super(ability); + } + + @Override + public TappedNotAttackingTriggeredAbility copy() { + return new TappedNotAttackingTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.TAPPED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (event.getFlag()) { + return false; + } + Permanent permanent = game.getPermanent(event.getTargetId()); + return permanent != null && permanent.isCreature(game) + && game.getOpponents(permanent.getControllerId()).contains(getControllerId()); + } + + @Override + public String getTriggerPhrase() { + return "Whenever a creature an opponent controls becomes tapped, " + + "if it isn't being declared as an attacker, "; + } +} diff --git a/Mage/src/main/java/mage/abilities/common/TransformIntoSourceTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/TransformIntoSourceTriggeredAbility.java new file mode 100644 index 00000000000..4902f91d450 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/TransformIntoSourceTriggeredAbility.java @@ -0,0 +1,58 @@ +package mage.abilities.common; + +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; + +/** + * @author TheElk801 + */ +public class TransformIntoSourceTriggeredAbility extends TriggeredAbilityImpl { + + private final boolean whenever; + + public TransformIntoSourceTriggeredAbility(Effect effect) { + this(effect, false); + } + + public TransformIntoSourceTriggeredAbility(Effect effect, boolean optional) { + this(effect, optional, false); + } + + public TransformIntoSourceTriggeredAbility(Effect effect, boolean optional, boolean whenever) { + super(Zone.BATTLEFIELD, effect, optional); + this.whenever = whenever; + } + + private TransformIntoSourceTriggeredAbility(final TransformIntoSourceTriggeredAbility ability) { + super(ability); + this.whenever = ability.whenever; + } + + @Override + public TransformIntoSourceTriggeredAbility copy() { + return new TransformIntoSourceTriggeredAbility(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(this.getSourceId())) { + return false; + } + Permanent permanent = getSourcePermanentIfItStillExists(game); + return permanent != null && permanent.isTransformed(); + } + + @Override + public String getTriggerPhrase() { + return "When" + (whenever ? "ever" : "") + " this creature transforms into {this}, "; + } +} diff --git a/Mage/src/main/java/mage/abilities/common/TransformsOrEntersTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/TransformsOrEntersTriggeredAbility.java new file mode 100644 index 00000000000..de57ff9824d --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/TransformsOrEntersTriggeredAbility.java @@ -0,0 +1,53 @@ +package mage.abilities.common; + +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; + +/** + * @author TheElk801 + */ +public class TransformsOrEntersTriggeredAbility extends TriggeredAbilityImpl { + + public TransformsOrEntersTriggeredAbility(Effect effect, boolean optional) { + super(Zone.BATTLEFIELD, effect, optional); + } + + private TransformsOrEntersTriggeredAbility(final TransformsOrEntersTriggeredAbility ability) { + super(ability); + } + + @Override + public TransformsOrEntersTriggeredAbility copy() { + return new TransformsOrEntersTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.TRANSFORMED + || event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (!event.getTargetId().equals(this.getSourceId())) { + return false; + } + switch (event.getType()) { + case TRANSFORMED: + Permanent permanent = getSourcePermanentIfItStillExists(game); + return permanent != null && !permanent.isTransformed(); + case ENTERS_THE_BATTLEFIELD: + return true; + } + return false; + } + + @Override + public String getTriggerPhrase() { + return "Whenever this creature enters the battlefield or transforms into {this}, "; + } +} diff --git a/Mage/src/main/java/mage/abilities/common/TurnedFaceUpAllTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/TurnedFaceUpAllTriggeredAbility.java index 7ece1e1a7c5..f1b45368546 100644 --- a/Mage/src/main/java/mage/abilities/common/TurnedFaceUpAllTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/TurnedFaceUpAllTriggeredAbility.java @@ -70,7 +70,7 @@ public class TurnedFaceUpAllTriggeredAbility extends TriggeredAbilityImpl { if (filter.match(permanent, getSourceId(), getControllerId(), game)) { if (setTargetPointer) { for (Effect effect : getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getTargetId())); + effect.setTargetPointer(new FixedTarget(event.getTargetId(), game)); } } return true; diff --git a/Mage/src/main/java/mage/abilities/common/TurnedFaceUpSourceTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/TurnedFaceUpSourceTriggeredAbility.java index c1a44ffe6e6..954c983252b 100644 --- a/Mage/src/main/java/mage/abilities/common/TurnedFaceUpSourceTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/TurnedFaceUpSourceTriggeredAbility.java @@ -52,7 +52,7 @@ public class TurnedFaceUpSourceTriggeredAbility extends TriggeredAbilityImpl { if (event.getTargetId().equals(this.getSourceId())) { if (setTargetPointer) { for (Effect effect: getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getTargetId())); + effect.setTargetPointer(new FixedTarget(event.getTargetId(), game)); } } return true; diff --git a/Mage/src/main/java/mage/abilities/common/WerewolfBackTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/WerewolfBackTriggeredAbility.java index 536d4c10a05..1915abb7ec7 100644 --- a/Mage/src/main/java/mage/abilities/common/WerewolfBackTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/WerewolfBackTriggeredAbility.java @@ -11,7 +11,7 @@ import mage.game.Game; public class WerewolfBackTriggeredAbility extends BeginningOfUpkeepTriggeredAbility { public WerewolfBackTriggeredAbility() { - super(new TransformSourceEffect(false), TargetController.ANY, false); + super(new TransformSourceEffect(), TargetController.ANY, false); } private WerewolfBackTriggeredAbility(final WerewolfBackTriggeredAbility ability) { diff --git a/Mage/src/main/java/mage/abilities/common/WerewolfFrontTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/WerewolfFrontTriggeredAbility.java index 70d91b1a37c..033cb066818 100644 --- a/Mage/src/main/java/mage/abilities/common/WerewolfFrontTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/WerewolfFrontTriggeredAbility.java @@ -11,7 +11,7 @@ import mage.game.Game; public class WerewolfFrontTriggeredAbility extends BeginningOfUpkeepTriggeredAbility { public WerewolfFrontTriggeredAbility() { - super(new TransformSourceEffect(true), TargetController.ANY, false); + super(new TransformSourceEffect(), TargetController.ANY, false); } private WerewolfFrontTriggeredAbility(final WerewolfFrontTriggeredAbility ability) { diff --git a/Mage/src/main/java/mage/abilities/common/delayed/AtTheBeginOfNextEndStepDelayedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/delayed/AtTheBeginOfNextEndStepDelayedTriggeredAbility.java index 0fff4ee32ad..5a87f0dc469 100644 --- a/Mage/src/main/java/mage/abilities/common/delayed/AtTheBeginOfNextEndStepDelayedTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/delayed/AtTheBeginOfNextEndStepDelayedTriggeredAbility.java @@ -1,4 +1,3 @@ - package mage.abilities.common.delayed; import mage.abilities.DelayedTriggeredAbility; @@ -6,34 +5,32 @@ import mage.abilities.condition.Condition; import mage.abilities.effects.Effect; import mage.constants.Duration; import mage.constants.TargetController; -import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; /** - * * @author North */ public class AtTheBeginOfNextEndStepDelayedTriggeredAbility extends DelayedTriggeredAbility { - private TargetController targetController; - private Condition condition; + private final TargetController targetController; + private final Condition condition; public AtTheBeginOfNextEndStepDelayedTriggeredAbility(Effect effect) { this(effect, TargetController.ANY); } public AtTheBeginOfNextEndStepDelayedTriggeredAbility(Effect effect, TargetController targetController) { - this(Zone.ALL, effect, targetController); + this(effect, targetController, null); } - public AtTheBeginOfNextEndStepDelayedTriggeredAbility(Zone zone, Effect effect, TargetController targetController) { - this(zone, effect, targetController, null); + public AtTheBeginOfNextEndStepDelayedTriggeredAbility(Effect effect, TargetController targetController, Condition condition) { + this(effect, targetController, condition, false); } - public AtTheBeginOfNextEndStepDelayedTriggeredAbility(Zone zone, Effect effect, TargetController targetController, Condition condition) { - super(effect, Duration.Custom); + public AtTheBeginOfNextEndStepDelayedTriggeredAbility(Effect effect, TargetController targetController, Condition condition, boolean optional) { + super(effect, Duration.Custom, true, optional); this.zone = zone; this.targetController = targetController; this.condition = condition; @@ -52,32 +49,33 @@ public class AtTheBeginOfNextEndStepDelayedTriggeredAbility extends DelayedTrigg @Override public boolean checkTrigger(GameEvent event, Game game) { - boolean correctEndPhase = false; switch (targetController) { case ANY: - correctEndPhase = true; break; case YOU: - correctEndPhase = event.getPlayerId().equals(this.controllerId); + if (!isControlledBy(event.getPlayerId())) { + return false; + } break; case OPPONENT: - if (game.getPlayer(this.getControllerId()).hasOpponent(event.getPlayerId(), game)) { - correctEndPhase = true; + if (!game.getOpponents(this.getControllerId()).contains(event.getPlayerId())) { + return false; } break; case CONTROLLER_ATTACHED_TO: - Permanent attachment = game.getPermanent(sourceId); - if (attachment != null && attachment.getAttachedTo() != null) { - Permanent attachedTo = game.getPermanent(attachment.getAttachedTo()); - if (attachedTo != null && attachedTo.isControlledBy(event.getPlayerId())) { - correctEndPhase = true; - } + Permanent attachment = game.getPermanent(getSourceId()); + if (attachment == null || attachment.getAttachedTo() == null) { + return false; } + Permanent attachedTo = game.getPermanent(attachment.getAttachedTo()); + if (attachedTo == null || !attachedTo.isControlledBy(event.getPlayerId())) { + return false; + } + break; + default: + throw new UnsupportedOperationException("TargetController not supported"); } - if (correctEndPhase) { - return !(condition != null && !condition.apply(game, this)); - } - return false; + return condition == null || condition.apply(game, this); } @Override @@ -86,23 +84,17 @@ public class AtTheBeginOfNextEndStepDelayedTriggeredAbility extends DelayedTrigg } @Override - public String getRule() { - StringBuilder sb = new StringBuilder(); + public String getTriggerPhrase() { switch (targetController) { case YOU: - sb.append("At the beginning of your next end step, "); - break; + return "At the beginning of your next end step, "; case OPPONENT: - sb.append("At the beginning of an opponent's next end step, "); - break; + return "At the beginning of an opponent's next end step, "; case ANY: - sb.append("At the beginning of the next end step, "); - break; + return "At the beginning of the next end step, "; case CONTROLLER_ATTACHED_TO: - sb.append("At the beginning of the next end step of enchanted creature's controller, "); - break; + return "At the beginning of the next end step of enchanted creature's controller, "; } - sb.append(getEffects().getText(modes.getMode())); - return sb.toString(); + throw new UnsupportedOperationException("TargetController not supported"); } } diff --git a/Mage/src/main/java/mage/abilities/common/delayed/ReflexiveTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/delayed/ReflexiveTriggeredAbility.java index e8202aa8347..baa7c6049e5 100644 --- a/Mage/src/main/java/mage/abilities/common/delayed/ReflexiveTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/delayed/ReflexiveTriggeredAbility.java @@ -15,6 +15,10 @@ public class ReflexiveTriggeredAbility extends DelayedTriggeredAbility { private final String text; + public ReflexiveTriggeredAbility(Effect effect, boolean optional) { + this(effect, optional, null); + } + public ReflexiveTriggeredAbility(Effect effect, boolean optional, String text) { super(effect, Duration.EndOfTurn, true, optional); this.text = text; @@ -32,19 +36,18 @@ public class ReflexiveTriggeredAbility extends DelayedTriggeredAbility { @Override public boolean checkTrigger(GameEvent event, Game game) { - return event.getPlayerId().equals(this.getControllerId()) + return this.isControlledBy(event.getPlayerId()) && event.getSourceId().equals(this.getSourceId()); } @Override public String getRule() { + if (text == null) { + return super.getRule(); + } return text.substring(0, 1).toUpperCase(Locale.ENGLISH) + text.substring(1) + '.'; } - public String getText() { - return text; - } - @Override public ReflexiveTriggeredAbility copy() { return new ReflexiveTriggeredAbility(this); diff --git a/Mage/src/main/java/mage/abilities/condition/common/ControlArtifactAndEnchantmentCondition.java b/Mage/src/main/java/mage/abilities/condition/common/ControlArtifactAndEnchantmentCondition.java new file mode 100644 index 00000000000..69c55da2c69 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/condition/common/ControlArtifactAndEnchantmentCondition.java @@ -0,0 +1,24 @@ +package mage.abilities.condition.common; + +import mage.abilities.Ability; +import mage.abilities.condition.Condition; +import mage.filter.StaticFilters; +import mage.game.Game; + +/** + * @author TheElk801 + */ +public enum ControlArtifactAndEnchantmentCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + return game.getBattlefield().contains(StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT, source, game, 1) + && game.getBattlefield().contains(StaticFilters.FILTER_CONTROLLED_PERMANENT_ENCHANTMENT, source, game, 1); + } + + @Override + public String toString() { + return "you control an artifact and an enchantment"; + } +} diff --git a/Mage/src/main/java/mage/abilities/condition/common/ControlledModifiedCreatureAsSpellCastCondition.java b/Mage/src/main/java/mage/abilities/condition/common/ControlledModifiedCreatureAsSpellCastCondition.java new file mode 100644 index 00000000000..4af273a385e --- /dev/null +++ b/Mage/src/main/java/mage/abilities/condition/common/ControlledModifiedCreatureAsSpellCastCondition.java @@ -0,0 +1,31 @@ +package mage.abilities.condition.common; + +import mage.MageObject; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.condition.Condition; +import mage.game.Game; +import mage.watchers.common.ControlledModifiedCreatureAsSpellCastWatcher; + +/** + * + * @author weirddan455 + */ +public enum ControlledModifiedCreatureAsSpellCastCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + MageObject sourceObject = source.getSourceObject(game); + ControlledModifiedCreatureAsSpellCastWatcher watcher = game.getState().getWatcher(ControlledModifiedCreatureAsSpellCastWatcher.class); + if (sourceObject == null || watcher == null) { + return false; + } + return watcher.getModifiedCreatureCount(new MageObjectReference(sourceObject, game)) > 0; + } + + @Override + public String toString() { + return "you controlled a modified creature as you cast this spell"; + } +} diff --git a/Mage/src/main/java/mage/abilities/condition/common/KickedCostCondition.java b/Mage/src/main/java/mage/abilities/condition/common/KickedCostCondition.java index 3250490bc49..d847018644e 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/KickedCostCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/KickedCostCondition.java @@ -1,5 +1,6 @@ package mage.abilities.condition.common; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.condition.Condition; import mage.abilities.keyword.KickerAbility; @@ -21,9 +22,9 @@ public class KickedCostCondition implements Condition { @Override public boolean apply(Game game, Ability source) { - Card card = game.getCard(source.getSourceId()); - if (card != null) { - for (Ability ability: card.getAbilities()) { + MageObject sourceObject = source.getSourceObject(game); + if (sourceObject instanceof Card) { + for (Ability ability : ((Card) sourceObject).getAbilities(game)) { if (ability instanceof KickerAbility) { return ((KickerAbility) ability).isKicked(game, source, kickerCostText); } diff --git a/Mage/src/main/java/mage/abilities/condition/common/NightCondition.java b/Mage/src/main/java/mage/abilities/condition/common/NightCondition.java index 29e5c709d64..11a6cd4bb58 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/NightCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/NightCondition.java @@ -6,14 +6,13 @@ import mage.game.Game; /** * @author TheElk801 - * TODO: Implement this */ public enum NightCondition implements Condition { instance; @Override public boolean apply(Game game, Ability source) { - return false; + return game.checkDayNight(false); } @Override diff --git a/Mage/src/main/java/mage/abilities/condition/common/NotMyTurnCondition.java b/Mage/src/main/java/mage/abilities/condition/common/NotMyTurnCondition.java index 42b2b2d854c..e87fe697948 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/NotMyTurnCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/NotMyTurnCondition.java @@ -1,22 +1,15 @@ - package mage.abilities.condition.common; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.condition.Condition; import mage.game.Game; public enum NotMyTurnCondition implements Condition { - instance; @Override public boolean apply(Game game, Ability source) { - UUID activePlayerId = game.getActivePlayerId(); - if (activePlayerId != null) { - return !activePlayerId.equals(source.getControllerId()); - } - return false; + return !source.isControlledBy(game.getActivePlayerId()); } @Override diff --git a/Mage/src/main/java/mage/abilities/condition/common/OpponentsTurnCondition.java b/Mage/src/main/java/mage/abilities/condition/common/OpponentsTurnCondition.java new file mode 100644 index 00000000000..beac66cb7f2 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/condition/common/OpponentsTurnCondition.java @@ -0,0 +1,22 @@ +package mage.abilities.condition.common; + +import mage.abilities.Ability; +import mage.abilities.condition.Condition; +import mage.game.Game; + +/** + * @author TheElk801 + */ +public enum OpponentsTurnCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + return game.getOpponents(source.getControllerId()).contains(game.getActivePlayerId()); + } + + @Override + public String toString() { + return "if it's an opponent's turn"; + } +} diff --git a/Mage/src/main/java/mage/abilities/condition/common/PermanentsOnTheBattlefieldCondition.java b/Mage/src/main/java/mage/abilities/condition/common/PermanentsOnTheBattlefieldCondition.java index 59ec3e3a204..0643c27dd8a 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/PermanentsOnTheBattlefieldCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/PermanentsOnTheBattlefieldCondition.java @@ -1,4 +1,3 @@ - package mage.abilities.condition.common; import mage.abilities.Ability; @@ -15,15 +14,13 @@ import mage.game.Game; * @author nantuko * @author maurer.it_at_gmail.com * @see #Controls(mage.filter.Filter) - * @see #Controls(mage.filter.Filter, mage.abilities.condition.Condition) */ public class PermanentsOnTheBattlefieldCondition implements Condition { - private FilterPermanent filter; - private Condition condition; - private ComparisonType type; - private int count; - private boolean onlyControlled; + private final FilterPermanent filter; + private final ComparisonType type; + private final int count; + private final boolean onlyControlled; /** * Applies a filter and delegates creation to @@ -33,7 +30,11 @@ public class PermanentsOnTheBattlefieldCondition implements Condition { * @param filter */ public PermanentsOnTheBattlefieldCondition(FilterPermanent filter) { - this(filter, ComparisonType.MORE_THAN, 0); + this(filter, true); + } + + public PermanentsOnTheBattlefieldCondition(FilterPermanent filter, boolean onlyControlled) { + this(filter, ComparisonType.MORE_THAN, 0, onlyControlled); } /** @@ -57,40 +58,18 @@ public class PermanentsOnTheBattlefieldCondition implements Condition { this.onlyControlled = onlyControlled; } - /** - * Applies a filter, a {@link ComparisonType}, and count to permanents on - * the battlefield and calls the decorated condition to see if it - * {@link #apply(mage.game.Game, mage.abilities.Ability) applies} as well. - * This will force both conditions to apply for this to be true. - * - * @param filter - * @param type - * @param count - * @param conditionToDecorate - */ - public PermanentsOnTheBattlefieldCondition(FilterPermanent filter, ComparisonType type, int count, Condition conditionToDecorate) { - this(filter, type, count); - this.condition = conditionToDecorate; - } - @Override public boolean apply(Game game, Ability source) { - boolean conditionApplies; - - FilterPermanent localFilter = filter.copy(); + FilterPermanent localFilter; if (onlyControlled) { + localFilter = filter.copy(); localFilter.add(new ControllerIdPredicate(source.getControllerId())); + } else { + localFilter = filter; } - - int permanentsOnBattlefield = game.getBattlefield().count(localFilter, source.getSourceId(), source.getControllerId(), game); - conditionApplies = ComparisonType.compare(permanentsOnBattlefield, type, count); - - //If a decorated condition exists, check it as well and apply them together. - if (this.condition != null) { - conditionApplies = conditionApplies && this.condition.apply(game, source); - } - - return conditionApplies; + return ComparisonType.compare(game.getBattlefield().count( + localFilter, source.getSourceId(), source.getControllerId(), game + ), type, count); } @Override diff --git a/Mage/src/main/java/mage/abilities/condition/common/SourceAttackingAloneCondition.java b/Mage/src/main/java/mage/abilities/condition/common/SourceAttackingAloneCondition.java index 5e747aae046..048bb5dfed4 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/SourceAttackingAloneCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/SourceAttackingAloneCondition.java @@ -14,7 +14,7 @@ public enum SourceAttackingAloneCondition implements Condition { @Override public boolean apply(Game game, Ability source) { - return game.getCombat().attacksAlone() && game.getCombat().getAttackers().get(0).equals(source.getSourceId()); + return game.getCombat().attacksAlone() && game.getCombat().getAttackers().contains(source.getSourceId()); } @Override diff --git a/Mage/src/main/java/mage/abilities/condition/common/SourceOnBattlefieldCondition.java b/Mage/src/main/java/mage/abilities/condition/common/SourceOnBattlefieldCondition.java index 50c334dd2b1..d5b042add5b 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/SourceOnBattlefieldCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/SourceOnBattlefieldCondition.java @@ -1,11 +1,9 @@ - package mage.abilities.condition.common; import mage.abilities.Ability; import mage.abilities.condition.Condition; import mage.game.Game; - /** * As long as the sourceId permanent is * on the battlefield, the condition is true. @@ -13,18 +11,15 @@ import mage.game.Game; * @author LevelX2 */ public enum SourceOnBattlefieldCondition implements Condition { - instance; - + @Override public boolean apply(Game game, Ability source) { - return (game.getPermanent(source.getSourceId()) != null); + return source.getSourcePermanentIfItStillExists(game) != null; } @Override public String toString() { return "if {this} is on the battlefield"; } - - } diff --git a/Mage/src/main/java/mage/abilities/condition/common/SourceOnBattlefieldControlUnchangedCondition.java b/Mage/src/main/java/mage/abilities/condition/common/SourceOnBattlefieldControlUnchangedCondition.java deleted file mode 100644 index 64f600e444c..00000000000 --- a/Mage/src/main/java/mage/abilities/condition/common/SourceOnBattlefieldControlUnchangedCondition.java +++ /dev/null @@ -1,43 +0,0 @@ -package mage.abilities.condition.common; - -import mage.abilities.Ability; -import mage.abilities.condition.Condition; -import mage.game.Game; -import mage.watchers.common.LostControlWatcher; - -/** - * This condition checks if ever since first call of the apply method the - * controller of the source has changed - * - * Monitoring the LOST_CONTROL event has the advantage that also all layered - * effects can correctly check for controller change because comparing old and - * new controller during their apply time does not take into account layered - * change control effects that will be applied later. - * - * This condition needs the LostControlWatcher, so be sure to add it to the card - * that uses the condition. - * - * @author LevelX2 - */ -public class SourceOnBattlefieldControlUnchangedCondition implements Condition { - - private Long checkingSince; - private int startingZoneChangeCounter; - - @Override - public boolean apply(Game game, Ability source) { - if (checkingSince == null) { - checkingSince = System.currentTimeMillis() - 1; - startingZoneChangeCounter = game.getState().getZoneChangeCounter(source.getSourceId()); - } - if (game.getState().getZoneChangeCounter(source.getSourceId()) > startingZoneChangeCounter) { - return false; - } - LostControlWatcher watcher = game.getState().getWatcher(LostControlWatcher.class); - if (watcher != null) { - return checkingSince > watcher.getOrderOfLastLostControl(source.getSourceId()); - } - throw new UnsupportedOperationException("LostControlWatcher not found!"); - } - -} diff --git a/Mage/src/main/java/mage/abilities/condition/common/SourceTappedCondition.java b/Mage/src/main/java/mage/abilities/condition/common/SourceTappedCondition.java index f6b64cbff71..6f63e20f2b8 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/SourceTappedCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/SourceTappedCondition.java @@ -1,5 +1,3 @@ - - package mage.abilities.condition.common; import mage.abilities.Ability; @@ -8,21 +6,22 @@ import mage.game.Game; import mage.game.permanent.Permanent; /** - * * @author LevelX2 */ public enum SourceTappedCondition implements Condition { + TAPPED(true), + UNTAPPED(false); + private final boolean tapped; - instance; + SourceTappedCondition(boolean tapped) { + this.tapped = tapped; + } @Override public boolean apply(Game game, Ability source) { Permanent permanent = game.getBattlefield().getPermanent(source.getSourceId()); - if (permanent != null) { - return permanent.isTapped(); - } - return false; + return permanent != null && permanent.isTapped() == tapped; } } diff --git a/Mage/src/main/java/mage/abilities/costs/CostAdjuster.java b/Mage/src/main/java/mage/abilities/costs/CostAdjuster.java index b7bc0d23a50..ea1d77449d0 100644 --- a/Mage/src/main/java/mage/abilities/costs/CostAdjuster.java +++ b/Mage/src/main/java/mage/abilities/costs/CostAdjuster.java @@ -3,10 +3,13 @@ package mage.abilities.costs; import mage.abilities.Ability; import mage.game.Game; +import java.io.Serializable; + /** * @author TheElk801 */ -public interface CostAdjuster { +@FunctionalInterface +public interface CostAdjuster extends Serializable { /** * Must check playable and real cast states. diff --git a/Mage/src/main/java/mage/abilities/costs/common/ExileFromGraveCost.java b/Mage/src/main/java/mage/abilities/costs/common/ExileFromGraveCost.java index dacc8bcbde1..e8cda8f583c 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/ExileFromGraveCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/ExileFromGraveCost.java @@ -1,4 +1,3 @@ - package mage.abilities.costs.common; import mage.abilities.Ability; @@ -33,7 +32,8 @@ public class ExileFromGraveCost extends CostImpl { this.addTarget(target); if (target.getMaxNumberOfTargets() > 1) { this.text = "exile " - + (target.getNumberOfTargets() == 1 && target.getMaxNumberOfTargets() == Integer.MAX_VALUE ? "one or more" + + (target.getNumberOfTargets() == 1 + && target.getMaxNumberOfTargets() == Integer.MAX_VALUE ? "one or more" : ((target.getNumberOfTargets() < target.getMaxNumberOfTargets() ? "up to " : "")) + CardUtil.numberToText(target.getMaxNumberOfTargets())) + ' ' + target.getTargetName(); @@ -81,14 +81,19 @@ public class ExileFromGraveCost extends CostImpl { if (targets.choose(Outcome.Exile, controllerId, source.getSourceId(), game)) { for (UUID targetId : targets.get(0).getTargets()) { Card card = game.getCard(targetId); - if (card == null || game.getState().getZone(targetId) != Zone.GRAVEYARD) { + if (card == null + || game.getState().getZone(targetId) != Zone.GRAVEYARD) { return false; } exiledCards.add(card); } Cards cardsToExile = new CardsImpl(); cardsToExile.addAll(exiledCards); - controller.moveCards(cardsToExile, Zone.EXILED, ability, game); + controller.moveCardsToExile( + cardsToExile.getCards(game), source, game, true, + CardUtil.getExileZoneId(game, source), + CardUtil.getSourceName(game, source) + ); if (setTargetPointer) { source.getEffects().setTargetPointer(new FixedTargets(cardsToExile, game)); } diff --git a/Mage/src/main/java/mage/abilities/costs/common/ExileSourceCost.java b/Mage/src/main/java/mage/abilities/costs/common/ExileSourceCost.java index 1531dfb7ae1..2bbcc90f400 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/ExileSourceCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/ExileSourceCost.java @@ -1,7 +1,6 @@ package mage.abilities.costs.common; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.costs.Cost; @@ -11,23 +10,23 @@ import mage.game.Game; import mage.players.Player; import mage.util.CardUtil; +import java.util.UUID; + /** - * * @author BetaSteward_at_googlemail.com */ public class ExileSourceCost extends CostImpl { - private boolean toUniqueExileZone; + private final boolean toUniqueExileZone; public ExileSourceCost() { - this.text = "exile {this}"; + this(false); } /** - * * @param toUniqueExileZone moves the card to a source object dependant - * unique exile zone, so another effect of the same source object (e.g. - * Deadeye Navigator) can identify the card + * unique exile zone, so another effect of the same source object (e.g. + * Deadeye Navigator) can identify the card */ public ExileSourceCost(boolean toUniqueExileZone) { this.text = "exile {this}"; @@ -52,7 +51,7 @@ public class ExileSourceCost extends CostImpl { game.getState().setValue(sourceObject.getId().toString(), ability.getSourceObjectZoneChangeCounter()); } controller.moveCardToExileWithInfo((Card) sourceObject, exileZoneId, exileZoneName, source, game, game.getState().getZone(sourceObject.getId()), true); - // 117.11. The actions performed when paying a cost may be modified by effects. + // 117.11. The actions performed when paying a cost may be modified by effects. // Even if they are, meaning the actions that are performed don't match the actions // that are called for, the cost has still been paid. // so return state here is not important because the user indended to exile the target anyway diff --git a/Mage/src/main/java/mage/abilities/costs/common/ExileSourceFromGraveCost.java b/Mage/src/main/java/mage/abilities/costs/common/ExileSourceFromGraveCost.java index 1169199a7b9..e6dcf15e195 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/ExileSourceFromGraveCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/ExileSourceFromGraveCost.java @@ -17,7 +17,7 @@ import mage.players.Player; public class ExileSourceFromGraveCost extends CostImpl { public ExileSourceFromGraveCost() { - this.text = "Exile {this} from your graveyard"; + this.text = "exile {this} from your graveyard"; } public ExileSourceFromGraveCost(ExileSourceFromGraveCost cost) { diff --git a/Mage/src/main/java/mage/abilities/costs/common/ExileTargetCost.java b/Mage/src/main/java/mage/abilities/costs/common/ExileTargetCost.java index 551fd5909fc..65e64d087fd 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/ExileTargetCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/ExileTargetCost.java @@ -2,20 +2,23 @@ package mage.abilities.costs.common; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.costs.Cost; import mage.abilities.costs.CostImpl; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.Outcome; -import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.players.Player; import mage.target.common.TargetControlledPermanent; +import mage.util.CardUtil; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; /** - * * @author emerald000 */ public class ExileTargetCost extends CostImpl { @@ -26,35 +29,43 @@ public class ExileTargetCost extends CostImpl { this.addTarget(target); this.text = "Exile " + target.getTargetName(); } - + public ExileTargetCost(TargetControlledPermanent target, boolean noText) { this.addTarget(target); } public ExileTargetCost(ExileTargetCost cost) { super(cost); - for (Permanent permanent: cost.permanents) { + for (Permanent permanent : cost.permanents) { this.permanents.add(permanent.copy()); } } @Override public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) { - if (targets.choose(Outcome.Exile, controllerId, source.getSourceId(), game)) { - for (UUID targetId: targets.get(0).getTargets()) { - Permanent permanent = game.getPermanent(targetId); - if (permanent == null) { - return false; - } - permanents.add(permanent.copy()); - // 117.11. The actions performed when paying a cost may be modified by effects. - // Even if they are, meaning the actions that are performed don't match the actions - // that are called for, the cost has still been paid. - // so return state here is not important because the user indended to exile the target anyway - game.getPlayer(ability.getControllerId()).moveCardToExileWithInfo(permanent, null, null, source, game, Zone.BATTLEFIELD, true); - } - paid = true; + Player player = game.getPlayer(ability.getControllerId()); + if (player == null || !targets.choose(Outcome.Exile, controllerId, source.getSourceId(), game)) { + return paid; } + Cards cards = new CardsImpl(); + for (UUID targetId : targets.get(0).getTargets()) { + Permanent permanent = game.getPermanent(targetId); + if (permanent == null) { + return false; + } + cards.add(permanent); + permanents.add(permanent.copy()); + // 117.11. The actions performed when paying a cost may be modified by effects. + // Even if they are, meaning the actions that are performed don't match the actions + // that are called for, the cost has still been paid. + // so return state here is not important because the user indended to exile the target anyway + } + player.moveCardsToExile( + cards.getCards(game), source, game, false, + CardUtil.getExileZoneId(game, source), + CardUtil.getSourceName(game, source) + ); + paid = true; return paid; } @@ -71,5 +82,4 @@ public class ExileTargetCost extends CostImpl { public List getPermanents() { return permanents; } - } diff --git a/Mage/src/main/java/mage/abilities/costs/common/PayEnergyCost.java b/Mage/src/main/java/mage/abilities/costs/common/PayEnergyCost.java index e2462f3a25f..75e11fa4214 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/PayEnergyCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/PayEnergyCost.java @@ -49,7 +49,7 @@ public class PayEnergyCost extends CostImpl { } private void setText() { - StringBuilder sb = new StringBuilder("Pay "); + StringBuilder sb = new StringBuilder("pay "); for (int i = 0; i < amount; i++) { sb.append("{E}"); } diff --git a/Mage/src/main/java/mage/abilities/costs/common/PayVariableLifeCost.java b/Mage/src/main/java/mage/abilities/costs/common/PayVariableLifeCost.java index 4224907d130..885bf3a52fa 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/PayVariableLifeCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/PayVariableLifeCost.java @@ -19,7 +19,7 @@ public class PayVariableLifeCost extends VariableCostImpl { public PayVariableLifeCost(boolean useAsAdditionalCost) { super(useAsAdditionalCost ? VariableCostType.ADDITIONAL : VariableCostType.NORMAL, "life to pay"); - this.text = new StringBuilder(useAsAdditionalCost ? "as an additional cost to cast this spell, pay " : "Pay ") + this.text = new StringBuilder(useAsAdditionalCost ? "As an additional cost to cast this spell, pay " : "Pay ") .append(xText).append(' ').append("life").toString(); } diff --git a/Mage/src/main/java/mage/abilities/costs/common/RemoveCounterCost.java b/Mage/src/main/java/mage/abilities/costs/common/RemoveCounterCost.java index 63e91293e11..8c423662572 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/RemoveCounterCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/RemoveCounterCost.java @@ -23,10 +23,9 @@ import java.util.UUID; */ public class RemoveCounterCost extends CostImpl { - protected TargetPermanent target; - private String name; - private CounterType counterTypeToRemove; - protected int countersToRemove; + protected final TargetPermanent target; + private final CounterType counterTypeToRemove; + protected final int countersToRemove; public RemoveCounterCost(TargetPermanent target) { this(target, null); @@ -47,7 +46,6 @@ public class RemoveCounterCost extends CostImpl { public RemoveCounterCost(final RemoveCounterCost cost) { super(cost); this.target = cost.target.copy(); - this.name = cost.name; this.countersToRemove = cost.countersToRemove; this.counterTypeToRemove = cost.counterTypeToRemove; } @@ -128,7 +126,7 @@ public class RemoveCounterCost extends CostImpl { } private String setText() { - StringBuilder sb = new StringBuilder("Remove "); + StringBuilder sb = new StringBuilder("remove "); if (counterTypeToRemove != null) { sb.append(CardUtil.numberToText(countersToRemove, counterTypeToRemove.getArticle())).append(' ').append(counterTypeToRemove.getName()); } else { diff --git a/Mage/src/main/java/mage/abilities/costs/common/ReturnToHandChosenControlledPermanentCost.java b/Mage/src/main/java/mage/abilities/costs/common/ReturnToHandChosenControlledPermanentCost.java index 7bc29c35039..b89ff713b4a 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/ReturnToHandChosenControlledPermanentCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/ReturnToHandChosenControlledPermanentCost.java @@ -1,9 +1,6 @@ package mage.abilities.costs.common; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.costs.Cost; import mage.abilities.costs.CostImpl; @@ -16,8 +13,11 @@ import mage.players.Player; import mage.target.common.TargetControlledPermanent; import mage.util.CardUtil; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + /** - * * @author BetaSteward_at_googlemail.com */ public class ReturnToHandChosenControlledPermanentCost extends CostImpl { @@ -31,7 +31,7 @@ public class ReturnToHandChosenControlledPermanentCost extends CostImpl { + (target.getTargetName().endsWith(" you control") ? "" : " you control") + " to their owner's hand"; } else { - this.text = "return " + target.getTargetName() + this.text = "return " + CardUtil.addArticle(target.getTargetName()) + (target.getTargetName().endsWith(" you control") ? "" : " you control") + " to its owner's hand"; } diff --git a/Mage/src/main/java/mage/abilities/costs/common/SacrificeXTargetCost.java b/Mage/src/main/java/mage/abilities/costs/common/SacrificeXTargetCost.java index 27d6a1b3f46..fcc8b41a238 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/SacrificeXTargetCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/SacrificeXTargetCost.java @@ -14,22 +14,29 @@ import mage.target.common.TargetControlledPermanent; */ public class SacrificeXTargetCost extends VariableCostImpl { - protected FilterControlledPermanent filter; + protected final FilterControlledPermanent filter; + private final int minValue; public SacrificeXTargetCost(FilterControlledPermanent filter) { this(filter, false); } public SacrificeXTargetCost(FilterControlledPermanent filter, boolean useAsAdditionalCost) { + this(filter, useAsAdditionalCost, 0); + } + + public SacrificeXTargetCost(FilterControlledPermanent filter, boolean useAsAdditionalCost, int minValue) { super(useAsAdditionalCost ? VariableCostType.ADDITIONAL : VariableCostType.NORMAL, filter.getMessage() + " to sacrifice"); this.text = (useAsAdditionalCost ? "as an additional cost to cast this spell, sacrifice " : "Sacrifice ") + xText + ' ' + filter.getMessage(); this.filter = filter; + this.minValue = minValue; } public SacrificeXTargetCost(final SacrificeXTargetCost cost) { super(cost); this.filter = cost.filter; + this.minValue = cost.minValue; } @Override @@ -37,6 +44,11 @@ public class SacrificeXTargetCost extends VariableCostImpl { return new SacrificeXTargetCost(this); } + @Override + public int getMinValue(Ability source, Game game) { + return minValue; + } + @Override public int getMaxValue(Ability source, Game game) { return game.getBattlefield().count(filter, source.getSourceId(), source.getControllerId(), game); @@ -47,7 +59,7 @@ public class SacrificeXTargetCost extends VariableCostImpl { TargetControlledPermanent target = new TargetControlledPermanent(xValue, xValue, filter, true); return new SacrificeTargetCost(target); } - + public Filter getFilter() { return filter; } diff --git a/Mage/src/main/java/mage/abilities/costs/costadjusters/ExileCardsFromHandAdjuster.java b/Mage/src/main/java/mage/abilities/costs/costadjusters/ExileCardsFromHandAdjuster.java new file mode 100644 index 00000000000..742e315efcb --- /dev/null +++ b/Mage/src/main/java/mage/abilities/costs/costadjusters/ExileCardsFromHandAdjuster.java @@ -0,0 +1,54 @@ +package mage.abilities.costs.costadjusters; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.CostAdjuster; +import mage.abilities.costs.common.ExileFromHandCost; +import mage.abilities.effects.common.InfoEffect; +import mage.cards.Card; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInHand; +import mage.util.CardUtil; + +/** + * @author TheElk801 + */ +public class ExileCardsFromHandAdjuster implements CostAdjuster { + + private final FilterCard filter; + + private ExileCardsFromHandAdjuster(FilterCard filter) { + this.filter = filter; + } + + @Override + public void adjustCosts(Ability ability, Game game) { + if (game.inCheckPlayableState()) { + return; + } + Player player = game.getPlayer(ability.getControllerId()); + if (player == null) { + return; + } + int cardCount = player.getHand().count(filter, game); + int toExile = cardCount > 0 ? player.getAmount( + 0, cardCount, "Choose how many " + filter.getMessage() + " to exile", game + ) : 0; + if (toExile > 0) { + ability.addCost(new ExileFromHandCost(new TargetCardInHand(toExile, filter))); + CardUtil.reduceCost(ability, 2 * toExile); + } + } + + public static final void addAdjusterAndMessage(Card card, FilterCard filter) { + card.addAbility(new SimpleStaticAbility( + Zone.ALL, + new InfoEffect("as an additional cost to cast this spell, you may exile any number of " + + filter.getMessage() + ". This spell costs {2} less to cast for each card exiled this way") + ).setRuleAtTheTop(true)); + card.getSpellAbility().setCostAdjuster(new ExileCardsFromHandAdjuster(filter)); + } +} diff --git a/Mage/src/main/java/mage/abilities/costs/costadjusters/LegendaryCreatureCostAdjuster.java b/Mage/src/main/java/mage/abilities/costs/costadjusters/LegendaryCreatureCostAdjuster.java new file mode 100644 index 00000000000..51ef9b608a6 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/costs/costadjusters/LegendaryCreatureCostAdjuster.java @@ -0,0 +1,31 @@ +package mage.abilities.costs.costadjusters; + +import mage.abilities.Ability; +import mage.abilities.costs.CostAdjuster; +import mage.constants.SuperType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.game.Game; +import mage.util.CardUtil; + +/** + * @author TheElk801 + */ +public enum LegendaryCreatureCostAdjuster implements CostAdjuster { + instance; + private static final FilterPermanent filter = new FilterControlledCreaturePermanent(); + + static { + filter.add(SuperType.LEGENDARY.getPredicate()); + } + + @Override + public void adjustCosts(Ability ability, Game game) { + int count = game.getBattlefield().count( + filter, ability.getSourceId(), ability.getControllerId(), game + ); + if (count > 0) { + CardUtil.reduceCost(ability, count); + } + } +} \ No newline at end of file diff --git a/Mage/src/main/java/mage/abilities/costs/mana/ColoredManaCost.java b/Mage/src/main/java/mage/abilities/costs/mana/ColoredManaCost.java index 87319e0172a..6369c9dc794 100644 --- a/Mage/src/main/java/mage/abilities/costs/mana/ColoredManaCost.java +++ b/Mage/src/main/java/mage/abilities/costs/mana/ColoredManaCost.java @@ -43,7 +43,7 @@ public class ColoredManaCost extends ManaCostImpl { @Override public String getText() { - return '{' + mana.toString() + '}'; + return '{' + mana.toString() + (this.phyrexian ? "/P" : "") + '}'; } @Override diff --git a/Mage/src/main/java/mage/abilities/costs/mana/HybridManaCost.java b/Mage/src/main/java/mage/abilities/costs/mana/HybridManaCost.java index 9c2193aa565..e15bc56054d 100644 --- a/Mage/src/main/java/mage/abilities/costs/mana/HybridManaCost.java +++ b/Mage/src/main/java/mage/abilities/costs/mana/HybridManaCost.java @@ -1,8 +1,5 @@ - package mage.abilities.costs.mana; -import java.util.ArrayList; -import java.util.List; import mage.Mana; import mage.abilities.Ability; import mage.abilities.costs.Cost; @@ -10,6 +7,9 @@ import mage.constants.ColoredManaSymbol; import mage.game.Game; import mage.players.ManaPool; +import java.util.ArrayList; +import java.util.List; + public class HybridManaCost extends ManaCostImpl { private final ColoredManaSymbol mana1; @@ -50,7 +50,7 @@ public class HybridManaCost extends ManaCostImpl { @Override public String getText() { - return '{' + mana1.toString() + '/' + mana2.toString() + '}'; + return '{' + mana1.toString() + '/' + mana2.toString() + (this.phyrexian ? "/P" : "") + '}'; } @Override diff --git a/Mage/src/main/java/mage/abilities/costs/mana/ManaCost.java b/Mage/src/main/java/mage/abilities/costs/mana/ManaCost.java index 9f5a3ed02db..91a3f0194f4 100644 --- a/Mage/src/main/java/mage/abilities/costs/mana/ManaCost.java +++ b/Mage/src/main/java/mage/abilities/costs/mana/ManaCost.java @@ -1,7 +1,5 @@ - package mage.abilities.costs.mana; -import java.util.List; import mage.Mana; import mage.abilities.Ability; import mage.abilities.costs.Cost; @@ -11,6 +9,8 @@ import mage.filter.Filter; import mage.game.Game; import mage.players.ManaPool; +import java.util.List; + public interface ManaCost extends Cost { int manaValue(); @@ -45,4 +45,7 @@ public interface ManaCost extends Cost { @Override ManaCost copy(); + boolean isPhyrexian(); + + void setPhyrexian(boolean phyrexian); } diff --git a/Mage/src/main/java/mage/abilities/costs/mana/ManaCostImpl.java b/Mage/src/main/java/mage/abilities/costs/mana/ManaCostImpl.java index d0503b870cb..59122a61edb 100644 --- a/Mage/src/main/java/mage/abilities/costs/mana/ManaCostImpl.java +++ b/Mage/src/main/java/mage/abilities/costs/mana/ManaCostImpl.java @@ -25,6 +25,7 @@ public abstract class ManaCostImpl extends CostImpl implements ManaCost { protected Mana cost; protected ManaOptions options; protected Filter sourceFilter; + protected boolean phyrexian = false; public ManaCostImpl() { payment = new Mana(); @@ -41,6 +42,7 @@ public abstract class ManaCostImpl extends CostImpl implements ManaCost { if (manaCost.sourceFilter != null) { this.sourceFilter = manaCost.sourceFilter.copy(); } + this.phyrexian = manaCost.phyrexian; } @Override @@ -288,4 +290,17 @@ public abstract class ManaCostImpl extends CostImpl implements ManaCost { public String toString() { return getText(); } + + @Override + public boolean isPhyrexian() { + return phyrexian; + } + + @Override + public void setPhyrexian(boolean phyrexian) { + if (phyrexian) { + this.options.add(Mana.GenericMana(0)); + } + this.phyrexian = phyrexian; + } } diff --git a/Mage/src/main/java/mage/abilities/costs/mana/ManaCosts.java b/Mage/src/main/java/mage/abilities/costs/mana/ManaCosts.java index c1196269e3f..def7aef85c8 100644 --- a/Mage/src/main/java/mage/abilities/costs/mana/ManaCosts.java +++ b/Mage/src/main/java/mage/abilities/costs/mana/ManaCosts.java @@ -54,4 +54,8 @@ public interface ManaCosts extends List, ManaCost { .collect(Collectors.toCollection(ManaCostsImpl::new)); } + + void incrPhyrexianPaid(); + + int getPhyrexianPaid(); } diff --git a/Mage/src/main/java/mage/abilities/costs/mana/ManaCostsImpl.java b/Mage/src/main/java/mage/abilities/costs/mana/ManaCostsImpl.java index 59e2e9dbd72..1e2126737d9 100644 --- a/Mage/src/main/java/mage/abilities/costs/mana/ManaCostsImpl.java +++ b/Mage/src/main/java/mage/abilities/costs/mana/ManaCostsImpl.java @@ -28,6 +28,8 @@ public class ManaCostsImpl extends ArrayList implements M protected final UUID id; protected String text = null; + protected boolean phyrexian = false; + private int phyrexianPaid = 0; private static final Map costsCache = new ConcurrentHashMap<>(); // must be thread safe, can't use nulls @@ -46,6 +48,7 @@ public class ManaCostsImpl extends ArrayList implements M for (T cost : costs) { this.add(cost.copy()); } + this.phyrexian = costs.phyrexian; } @Override @@ -175,58 +178,53 @@ public class ManaCostsImpl extends ArrayList implements M while (manaCostIterator.hasNext()) { ManaCost manaCost = manaCostIterator.next(); - if (manaCost instanceof PhyrexianManaCost) { - PhyrexianManaCost phyrexianManaCost = (PhyrexianManaCost) manaCost; - PayLifeCost payLifeCost = new PayLifeCost(2); - if (payLifeCost.canPay(abilityToPay, source, payingPlayer.getId(), game) - && payingPlayer.chooseUse(Outcome.LoseLife, "Pay 2 life instead of " + phyrexianManaCost.getBaseText() + '?', source, game)) { - manaCostIterator.remove(); - tempCosts.add(payLifeCost); - } + if (!manaCost.isPhyrexian()) { + continue; + } + PayLifeCost payLifeCost = new PayLifeCost(2); + if (payLifeCost.canPay(abilityToPay, source, payingPlayer.getId(), game) + && payingPlayer.chooseUse(Outcome.LoseLife, "Pay 2 life instead of " + manaCost.getText().replace("/P", "") + '?', source, game)) { + manaCostIterator.remove(); + tempCosts.add(payLifeCost); + this.incrPhyrexianPaid(); } } tempCosts.pay(source, game, source, payingPlayer.getId(), false, null); } + private void handleLikePhyrexianManaCosts(Player player, Ability source, Game game) { if (this.isEmpty()) { return; // nothing to be done without any mana costs. prevents NRE from occurring here } FilterMana phyrexianColors = player.getPhyrexianColors(); - if (player.getPhyrexianColors() != null) { - Costs tempCosts = new CostsImpl<>(); - - Iterator manaCostIterator = this.iterator(); - while (manaCostIterator.hasNext()) { - ManaCost manaCost = manaCostIterator.next(); - Mana mana = manaCost.getMana(); - PhyrexianManaCost tempPhyrexianCost = null; - - /* find which color mana is in the cost and set it in the temp Phyrexian cost */ - if (phyrexianColors.isWhite() && mana.getWhite() > 0) { - tempPhyrexianCost = new PhyrexianManaCost(ColoredManaSymbol.W); - } else if (phyrexianColors.isBlue() && mana.getBlue() > 0) { - tempPhyrexianCost = new PhyrexianManaCost(ColoredManaSymbol.U); - } else if (phyrexianColors.isBlack() && mana.getBlack() > 0) { - tempPhyrexianCost = new PhyrexianManaCost(ColoredManaSymbol.B); - } else if (phyrexianColors.isRed() && mana.getRed() > 0) { - tempPhyrexianCost = new PhyrexianManaCost(ColoredManaSymbol.R); - } else if (phyrexianColors.isGreen() && mana.getGreen() > 0) { - tempPhyrexianCost = new PhyrexianManaCost(ColoredManaSymbol.G); - } - - if (tempPhyrexianCost != null) { - PayLifeCost payLifeCost = new PayLifeCost(2); - if (payLifeCost.canPay(source, source, player.getId(), game) - && player.chooseUse(Outcome.LoseLife, "Pay 2 life (using an active ability) instead of " + tempPhyrexianCost.getBaseText() + '?', source, game)) { - manaCostIterator.remove(); - tempCosts.add(payLifeCost); - } - } - } - tempCosts.pay(source, game, source, player.getId(), false, null); + if (player.getPhyrexianColors() == null) { + return; } + Costs tempCosts = new CostsImpl<>(); + + Iterator manaCostIterator = this.iterator(); + while (manaCostIterator.hasNext()) { + ManaCost manaCost = manaCostIterator.next(); + Mana mana = manaCost.getMana(); + + /* find which color mana is in the cost and set it in the temp Phyrexian cost */ + if ((!phyrexianColors.isWhite() || mana.getWhite() <= 0) + && (!phyrexianColors.isBlue() || mana.getBlue() <= 0) + && (!phyrexianColors.isBlack() || mana.getBlack() <= 0) + && (!phyrexianColors.isRed() || mana.getRed() <= 0) + && (!phyrexianColors.isGreen() || mana.getGreen() <= 0)) { + continue; + } + PayLifeCost payLifeCost = new PayLifeCost(2); + if (payLifeCost.canPay(source, source, player.getId(), game) + && player.chooseUse(Outcome.LoseLife, "Pay 2 life (using an active ability) instead of " + manaCost.getMana() + '?', source, game)) { + manaCostIterator.remove(); + tempCosts.add(payLifeCost); + } + } + tempCosts.pay(source, game, source, player.getId(), false, null); } @Override @@ -447,49 +445,57 @@ public class ManaCostsImpl extends ArrayList implements M for (ManaCost cost : savedCosts) { this.add(cost.copy()); } - } else { - String[] symbols = mana.split("^\\{|}\\{|}$"); - int modifierForX = 0; - for (String symbol : symbols) { - if (!symbol.isEmpty()) { - if (symbol.length() == 1 || isNumeric(symbol)) { - if (Character.isDigit(symbol.charAt(0))) { - this.add(new GenericManaCost(Integer.valueOf(symbol))); - } else if (symbol.equals("S")) { - this.add(new SnowManaCost()); - } else if (symbol.equals("C")) { - this.add(new ColorlessManaCost(1)); - } else if (!symbol.equals("X")) { - this.add(new ColoredManaCost(ColoredManaSymbol.lookup(symbol.charAt(0)))); - } else // check X wasn't added before - if (modifierForX == 0) { - // count X occurence - for (String s : symbols) { - if (s.equals("X")) { - modifierForX++; - } - } - this.add(new VariableManaCost(VariableCostType.NORMAL, modifierForX)); - } //TODO: handle multiple {X} and/or {Y} symbols - } else if (Character.isDigit(symbol.charAt(0))) { - MonoHybridManaCost cost; - if (extractMonoHybridGenericValue) { - // for tests only, no usage in real game - cost = new MonoHybridManaCost(ColoredManaSymbol.lookup(symbol.charAt(2)), Integer.parseInt(symbol.substring(0, 1))); - } else { - cost = new MonoHybridManaCost(ColoredManaSymbol.lookup(symbol.charAt(2))); + return; + } + String[] symbols = mana.split("^\\{|}\\{|}$"); + int modifierForX = 0; + for (String symbol : symbols) { + if (symbol.isEmpty()) { + continue; + } + if (symbol.length() == 1 || isNumeric(symbol)) { + if (Character.isDigit(symbol.charAt(0))) { + this.add(new GenericManaCost(Integer.valueOf(symbol))); + } else if (symbol.equals("S")) { + this.add(new SnowManaCost()); + } else if (symbol.equals("C")) { + this.add(new ColorlessManaCost(1)); + } else if (!symbol.equals("X")) { + this.add(new ColoredManaCost(ColoredManaSymbol.lookup(symbol.charAt(0)))); + } else // check X wasn't added before + if (modifierForX == 0) { + // count X occurence + for (String s : symbols) { + if (s.equals("X")) { + modifierForX++; + } } - this.add(cost); - } else if (symbol.contains("P")) { - this.add(new PhyrexianManaCost(ColoredManaSymbol.lookup(symbol.charAt(0)))); - } else { - this.add(new HybridManaCost(ColoredManaSymbol.lookup(symbol.charAt(0)), ColoredManaSymbol.lookup(symbol.charAt(2)))); - } + this.add(new VariableManaCost(VariableCostType.NORMAL, modifierForX)); + } //TODO: handle multiple {X} and/or {Y} symbols + } else if (Character.isDigit(symbol.charAt(0))) { + MonoHybridManaCost cost; + if (extractMonoHybridGenericValue) { + // for tests only, no usage in real game + cost = new MonoHybridManaCost(ColoredManaSymbol.lookup(symbol.charAt(2)), Integer.parseInt(symbol.substring(0, 1))); + } else { + cost = new MonoHybridManaCost(ColoredManaSymbol.lookup(symbol.charAt(2))); } + this.add(cost); + } else { + boolean phyrexian = symbol.contains("/P"); + String without = symbol.replace("/P", ""); + ManaCost cost; + if (without.length() == 1) { + cost = new ColoredManaCost(ColoredManaSymbol.lookup(without.charAt(0))); + } else { + cost = new HybridManaCost(ColoredManaSymbol.lookup(without.charAt(0)), ColoredManaSymbol.lookup(without.charAt(2))); + } + cost.setPhyrexian(phyrexian); + this.add(cost); } - if (!extractMonoHybridGenericValue) { - costsCache.put(mana, this.copy()); - } + } + if (!extractMonoHybridGenericValue) { + costsCache.put(mana, this.copy()); } } @@ -597,6 +603,29 @@ public class ManaCostsImpl extends ArrayList implements M return new ManaCostsImpl<>(this); } + @Override + public boolean isPhyrexian() { + return phyrexian; + } + + @Override + public void setPhyrexian(boolean phyrexian) { + this.phyrexian = phyrexian; + for (T cost : this) { + cost.setPhyrexian(phyrexian); + } + } + + @Override + public void incrPhyrexianPaid() { + this.phyrexianPaid++; + } + + @Override + public int getPhyrexianPaid() { + return phyrexianPaid; + } + @Override public Filter getSourceFilter() { for (T cost : this) { diff --git a/Mage/src/main/java/mage/abilities/costs/mana/PhyrexianManaCost.java b/Mage/src/main/java/mage/abilities/costs/mana/PhyrexianManaCost.java deleted file mode 100644 index 60a196503dd..00000000000 --- a/Mage/src/main/java/mage/abilities/costs/mana/PhyrexianManaCost.java +++ /dev/null @@ -1,40 +0,0 @@ - -package mage.abilities.costs.mana; - -import mage.Mana; -import mage.constants.ColoredManaSymbol; - -/** - * - * @author nantuko - */ -public class PhyrexianManaCost extends ColoredManaCost { - - public PhyrexianManaCost(ColoredManaSymbol mana) { - super(mana); - options.add(Mana.GenericMana(0)); - } - - public PhyrexianManaCost(PhyrexianManaCost manaCost) { - super(manaCost); - } - - @Override - public String getText() { - return '{' + mana.toString() + "/P}"; - } - - public String getBaseText() { - return super.getText(); - } - - @Override - public ColoredManaCost getUnpaid() { - return new ColoredManaCost(this); - } - - @Override - public PhyrexianManaCost copy() { - return new PhyrexianManaCost(this); - } -} diff --git a/Mage/src/main/java/mage/abilities/decorator/ConditionalContinuousEffect.java b/Mage/src/main/java/mage/abilities/decorator/ConditionalContinuousEffect.java index d4904a82f60..d3600a01f88 100644 --- a/Mage/src/main/java/mage/abilities/decorator/ConditionalContinuousEffect.java +++ b/Mage/src/main/java/mage/abilities/decorator/ConditionalContinuousEffect.java @@ -104,8 +104,13 @@ public class ConditionalContinuousEffect extends ContinuousEffectImpl { if (!conditionState && effect.getDuration() == Duration.OneUse) { used = true; } - if (!conditionState && effect.getDuration() == Duration.Custom) { - this.discard(); + switch (effect.getDuration()) { + case OneUse: + used = true; + break; + case Custom: + case WhileControlled: + this.discard(); } return false; } @@ -123,11 +128,13 @@ public class ConditionalContinuousEffect extends ContinuousEffectImpl { otherwiseEffect.setTargetPointer(this.targetPointer); return otherwiseEffect.apply(game, source); } - if (effect.getDuration() == Duration.OneUse) { - used = true; - } - if (effect.getDuration() == Duration.Custom) { - this.discard(); + switch (effect.getDuration()) { + case OneUse: + used = true; + break; + case Custom: + case WhileControlled: + this.discard(); } return false; } @@ -176,7 +183,8 @@ public class ConditionalContinuousEffect extends ContinuousEffectImpl { /** * Return all effects list, for tests only - * @return + * + * @return */ public List getAllEffects() { List res = new ArrayList<>(); diff --git a/Mage/src/main/java/mage/abilities/decorator/ConditionalInterveningIfTriggeredAbility.java b/Mage/src/main/java/mage/abilities/decorator/ConditionalInterveningIfTriggeredAbility.java index 1982d401044..e31ca94d96c 100644 --- a/Mage/src/main/java/mage/abilities/decorator/ConditionalInterveningIfTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/decorator/ConditionalInterveningIfTriggeredAbility.java @@ -78,7 +78,8 @@ public class ConditionalInterveningIfTriggeredAbility extends TriggeredAbilityIm if (abilityText == null || abilityText.isEmpty()) { return ability.getRule(); } - return (abilityWord != null ? abilityWord.formatWord() : "") + abilityText; + return (abilityWord != null ? abilityWord.formatWord() : "") + abilityText + + (abilityText.endsWith(".") || abilityText.endsWith("\"") ? "" : "."); } @Override diff --git a/Mage/src/main/java/mage/abilities/decorator/ConditionalOneShotEffect.java b/Mage/src/main/java/mage/abilities/decorator/ConditionalOneShotEffect.java index 3dd4de8e89d..d6ea8e16aff 100644 --- a/Mage/src/main/java/mage/abilities/decorator/ConditionalOneShotEffect.java +++ b/Mage/src/main/java/mage/abilities/decorator/ConditionalOneShotEffect.java @@ -1,10 +1,9 @@ - - package mage.abilities.decorator; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.condition.Condition; +import mage.abilities.effects.Effects; import mage.abilities.effects.OneShotEffect; import mage.game.Game; @@ -15,9 +14,9 @@ import mage.game.Game; */ public class ConditionalOneShotEffect extends OneShotEffect { - private OneShotEffect effect; - private OneShotEffect otherwiseEffect; - private Condition condition; + private final Effects effects = new Effects(); + private final Effects otherwiseEffects = new Effects(); + private final Condition condition; public ConditionalOneShotEffect(OneShotEffect effect, Condition condition) { this(effect, null, condition, null); @@ -29,31 +28,43 @@ public class ConditionalOneShotEffect extends OneShotEffect { public ConditionalOneShotEffect(OneShotEffect effect, OneShotEffect otherwiseEffect, Condition condition, String text) { super(effect.getOutcome()); - this.effect = effect; - this.otherwiseEffect = otherwiseEffect; + if (effect != null) { + this.effects.add(effect); + } + if (otherwiseEffect != null) { + this.otherwiseEffects.add(otherwiseEffect); + } this.condition = condition; this.staticText = text; } public ConditionalOneShotEffect(ConditionalOneShotEffect effect) { super(effect); - this.effect = (OneShotEffect) effect.effect.copy(); - if (effect.otherwiseEffect != null) { - this.otherwiseEffect = (OneShotEffect) effect.otherwiseEffect.copy(); - } + this.effects.addAll(effect.effects.copy()); + this.otherwiseEffects.addAll(effect.otherwiseEffects.copy()); this.condition = effect.condition; } @Override public boolean apply(Game game, Ability source) { - if (condition.apply(game, source)) { - effect.setTargetPointer(this.targetPointer); - return effect.apply(game, source); - } else if (otherwiseEffect == null) { - return true; // nothing to do - no problem + // nothing to do - no problem + Effects toApply = condition.apply(game, source) ? effects : otherwiseEffects; + if (toApply.isEmpty()) { + return true; } - otherwiseEffect.setTargetPointer(this.targetPointer); - return otherwiseEffect.apply(game, source); + toApply.setTargetPointer(this.targetPointer); + toApply.stream().forEach(effect -> effect.apply(game, source)); + return true; + } + + public ConditionalOneShotEffect addEffect(OneShotEffect effect) { + this.effects.add(effect); + return this; + } + + public ConditionalOneShotEffect addOtherwiseEffect(OneShotEffect effect) { + this.otherwiseEffects.add(effect); + return this; } @Override @@ -66,10 +77,9 @@ public class ConditionalOneShotEffect extends OneShotEffect { if (staticText != null && !staticText.isEmpty()) { return staticText; } - if (otherwiseEffect == null) { - return "if " + condition.toString() + ", " + effect.getText(mode); + if (otherwiseEffects.isEmpty()) { + return "if " + condition.toString() + ", " + effects.getText(mode); } - return effect.getText(mode) + ". If " + condition.toString() + ", " + otherwiseEffect.getText(mode); + return effects.getText(mode) + ". If " + condition.toString() + ", " + otherwiseEffects.getText(mode); } - } diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/AbilityResolutionCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/AbilityResolutionCount.java index b2fcb80e221..97a607f09b8 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/AbilityResolutionCount.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/AbilityResolutionCount.java @@ -15,11 +15,7 @@ public enum AbilityResolutionCount implements DynamicValue { @Override public int calculate(Game game, Ability sourceAbility, Effect effect) { - AbilityResolvedWatcher watcher = game.getState().getWatcher(AbilityResolvedWatcher.class); - if (watcher != null) { - return watcher.getResolutionCount(game, sourceAbility); - } - return 0; + return AbilityResolvedWatcher.getResolutionCount(game, sourceAbility); } @Override diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/CardsInAllGraveyardsCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/CardsInAllGraveyardsCount.java index 4976affce1a..e54b7b16bfc 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/CardsInAllGraveyardsCount.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/CardsInAllGraveyardsCount.java @@ -8,7 +8,7 @@ import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; -import java.util.UUID; +import java.util.Objects; /** * @author North @@ -25,23 +25,22 @@ public class CardsInAllGraveyardsCount implements DynamicValue { this.filter = filter; } - public CardsInAllGraveyardsCount(final CardsInAllGraveyardsCount dynamicValue) { + private CardsInAllGraveyardsCount(final CardsInAllGraveyardsCount dynamicValue) { this.filter = dynamicValue.filter.copy(); } @Override public int calculate(Game game, Ability sourceAbility, Effect effect) { - int amount = 0; - Player controller = game.getPlayer(sourceAbility.getControllerId()); - if (controller != null) { - for (UUID playerUUID : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerUUID); - if (player != null) { - amount += player.getGraveyard().count(filter, sourceAbility.getSourceId(), sourceAbility.getControllerId(), game); - } - } - } - return amount; + return game.getState() + .getPlayersInRange(sourceAbility.getControllerId(), game) + .stream() + .map(game::getPlayer) + .filter(Objects::nonNull) + .map(Player::getGraveyard) + .mapToInt(graveyard -> graveyard.count( + filter, sourceAbility.getSourceId(), + sourceAbility.getControllerId(), game + )).sum(); } @Override diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/CountersSourceCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/CountersSourceCount.java index f33a28a140b..d974ff9c622 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/CountersSourceCount.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/CountersSourceCount.java @@ -9,27 +9,20 @@ import mage.game.permanent.Permanent; public class CountersSourceCount implements DynamicValue { - private final String counterName; + private final CounterType counterType; - public CountersSourceCount(CounterType counter) { - this.counterName = counter.getName(); - } - - public CountersSourceCount(String counterName) { - this.counterName = counterName; + public CountersSourceCount(CounterType counterType) { + this.counterType = counterType; } public CountersSourceCount(final CountersSourceCount countersCount) { - this.counterName = countersCount.counterName; + this.counterType = countersCount.counterType; } @Override public int calculate(Game game, Ability sourceAbility, Effect effect) { - Permanent permanent = game.getPermanentOrLKIBattlefield(sourceAbility.getSourceId()); - if (permanent != null) { - return permanent.getCounters(game).getCount(counterName); - } - return 0; + Permanent permanent = sourceAbility.getSourcePermanentOrLKI(game); + return permanent != null ? permanent.getCounters(game).getCount(counterType) : 0; } @Override @@ -44,6 +37,6 @@ public class CountersSourceCount implements DynamicValue { @Override public String getMessage() { - return counterName + " counter on {this}"; + return counterType + " counter on {this}"; } } diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/GreatestSharedCreatureTypeCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/GreatestSharedCreatureTypeCount.java index 3c830fc6666..3e03869ae29 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/GreatestSharedCreatureTypeCount.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/GreatestSharedCreatureTypeCount.java @@ -10,6 +10,7 @@ import mage.constants.SubTypeSet; import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.util.CardUtil; import java.util.*; @@ -25,9 +26,12 @@ public enum GreatestSharedCreatureTypeCount implements DynamicValue { @Override public int calculate(Game game, Ability sourceAbility, Effect effect) { + return getValue(sourceAbility.getControllerId(), sourceAbility.getSourceId(), game); + } + + public static int getValue(UUID playerId, UUID sourceId, Game game) { List permanentList = game.getBattlefield().getActivePermanents( - StaticFilters.FILTER_CONTROLLED_CREATURE, - sourceAbility.getControllerId(), sourceAbility.getSourceId(), game + StaticFilters.FILTER_CONTROLLED_CREATURE, playerId, sourceId, game ); permanentList.removeIf(Objects::isNull); int changelings = permanentList @@ -43,7 +47,7 @@ public enum GreatestSharedCreatureTypeCount implements DynamicValue { .map(permanent -> permanent.getSubtype(game)) .flatMap(Collection::stream) .filter(subType -> subType.getSubTypeSet() == SubTypeSet.CreatureType) - .forEach(subType -> typeMap.compute(subType, (s, i) -> i == null ? 1 : Integer.sum(i, 1))); + .forEach(subType -> typeMap.compute(subType, CardUtil::setOrIncrementValue)); return changelings + typeMap .values() diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/PermanentsOnBattlefieldCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/PermanentsOnBattlefieldCount.java index a9a85c54a34..ff959daaa48 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/PermanentsOnBattlefieldCount.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/PermanentsOnBattlefieldCount.java @@ -63,6 +63,6 @@ public class PermanentsOnBattlefieldCount implements DynamicValue { @Override public String getMessage() { - return filter.getMessage(); + return multiplier == null ? "the number of " + filter.getMessage() : filter.getMessage(); } } diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/RemovedCountersForCostValue.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/RemovedCountersForCostValue.java index 815f664b437..7fd6808010a 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/RemovedCountersForCostValue.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/RemovedCountersForCostValue.java @@ -25,7 +25,7 @@ public enum RemovedCountersForCostValue implements DynamicValue { @Override public String getMessage() { - return "number of removed counters"; + return ""; } @Override @@ -37,5 +37,4 @@ public enum RemovedCountersForCostValue implements DynamicValue { public String toString() { return "X"; } - } diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/SavedDamageValue.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/SavedDamageValue.java new file mode 100644 index 00000000000..8c8428fb2ce --- /dev/null +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/SavedDamageValue.java @@ -0,0 +1,28 @@ +package mage.abilities.dynamicvalue.common; + +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.game.Game; + +/** + * @author TheElk801 + */ +public enum SavedDamageValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + return (Integer) effect.getValue("damage"); + } + + @Override + public SavedDamageValue copy() { + return this; + } + + @Override + public String getMessage() { + return "that much"; + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java b/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java index 0e8ac85bcfc..31bd0688ecf 100644 --- a/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java +++ b/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java @@ -177,6 +177,7 @@ public class ContinuousEffects implements Serializable { for (ContinuousEffect effect : layeredEffects) { switch (effect.getDuration()) { case WhileOnBattlefield: + case WhileControlled: case WhileOnStack: case WhileInGraveyard: Set abilities = layeredEffects.getAbility(effect.getId()); @@ -1246,15 +1247,18 @@ public class ContinuousEffects implements Serializable { } private boolean isAbilityStillExists(final Game game, final Ability ability, ContinuousEffect effect) { - final Card card = game.getPermanentOrLKIBattlefield(ability.getSourceId()); - if (!(effect instanceof BecomesFaceDownCreatureEffect) - && (effect != null && !effect.getDuration().equals(Duration.Custom))) { // Custom effects do not depend on the creating permanent - if (card != null) { - return card.hasAbility(ability, game); - } + switch (effect.getDuration()) { // effects with fixed duration don't need an object with the source ability (e.g. a silence cast with isochronic Scepter has no more a card object + case EndOfCombat: + case EndOfGame: + case EndOfStep: + case EndOfTurn: + case OneUse: + case Custom: // custom duration means the effect ends itself if needed + return true; } - - return true; + final Card card = game.getPermanentOrLKIBattlefield(ability.getSourceId()); + return effect instanceof BecomesFaceDownCreatureEffect + || effect == null || card == null || card.hasAbility(ability, game); } public Set getLayeredEffectAbilities(ContinuousEffect effect) { @@ -1287,45 +1291,36 @@ public class ContinuousEffects implements Serializable { switch (effect.getEffectType()) { case REPLACEMENT: case REDIRECTION: - ReplacementEffect newReplacementEffect = (ReplacementEffect) effect; - replacementEffects.addEffect(newReplacementEffect, source); + replacementEffects.addEffect((ReplacementEffect) effect, source); break; case PREVENTION: - PreventionEffect newPreventionEffect = (PreventionEffect) effect; - preventionEffects.addEffect(newPreventionEffect, source); + preventionEffects.addEffect((PreventionEffect) effect, source); break; case RESTRICTION: - RestrictionEffect newRestrictionEffect = (RestrictionEffect) effect; - restrictionEffects.addEffect(newRestrictionEffect, source); + restrictionEffects.addEffect((RestrictionEffect) effect, source); break; case RESTRICTION_UNTAP_NOT_MORE_THAN: - RestrictionUntapNotMoreThanEffect newRestrictionUntapNotMoreThanEffect = (RestrictionUntapNotMoreThanEffect) effect; - restrictionUntapNotMoreThanEffects.addEffect(newRestrictionUntapNotMoreThanEffect, source); + restrictionUntapNotMoreThanEffects.addEffect((RestrictionUntapNotMoreThanEffect) effect, source); break; case REQUIREMENT: - RequirementEffect newRequirementEffect = (RequirementEffect) effect; - requirementEffects.addEffect(newRequirementEffect, source); + requirementEffects.addEffect((RequirementEffect) effect, source); break; case ASTHOUGH: AsThoughEffect newAsThoughEffect = (AsThoughEffect) effect; - if (!asThoughEffectsMap.containsKey(newAsThoughEffect.getAsThoughEffectType())) { + asThoughEffectsMap.computeIfAbsent(newAsThoughEffect.getAsThoughEffectType(), x -> { ContinuousEffectsList list = new ContinuousEffectsList<>(); allEffectsLists.add(list); - asThoughEffectsMap.put(newAsThoughEffect.getAsThoughEffectType(), list); - } - asThoughEffectsMap.get(newAsThoughEffect.getAsThoughEffectType()).addEffect(newAsThoughEffect, source); + return list; + }).addEffect(newAsThoughEffect, source); break; case COSTMODIFICATION: - CostModificationEffect newCostModificationEffect = (CostModificationEffect) effect; - costModificationEffects.addEffect(newCostModificationEffect, source); + costModificationEffects.addEffect((CostModificationEffect) effect, source); break; case SPLICE: - SpliceCardEffect newSpliceCardEffect = (SpliceCardEffect) effect; - spliceCardEffects.addEffect(newSpliceCardEffect, source); + spliceCardEffects.addEffect((SpliceCardEffect) effect, source); break; case CONTINUOUS_RULE_MODIFICATION: - ContinuousRuleModifyingEffect newContinuousRuleModifiyingEffect = (ContinuousRuleModifyingEffect) effect; - continuousRuleModifyingEffects.addEffect(newContinuousRuleModifiyingEffect, source); + continuousRuleModifyingEffects.addEffect((ContinuousRuleModifyingEffect) effect, source); break; default: layeredEffects.addEffect(effect, source); diff --git a/Mage/src/main/java/mage/abilities/effects/ContinuousEffectsList.java b/Mage/src/main/java/mage/abilities/effects/ContinuousEffectsList.java index a8e118d3cad..404401843cc 100644 --- a/Mage/src/main/java/mage/abilities/effects/ContinuousEffectsList.java +++ b/Mage/src/main/java/mage/abilities/effects/ContinuousEffectsList.java @@ -1,6 +1,5 @@ package mage.abilities.effects; -import java.util.*; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.MageSingleton; @@ -8,9 +7,12 @@ import mage.cards.Card; import mage.constants.Duration; import mage.constants.Zone; import mage.game.Game; +import mage.game.permanent.Permanent; import mage.players.Player; import org.apache.log4j.Logger; +import java.util.*; + /** * @param * @author BetaSteward_at_googlemail.com @@ -145,6 +147,12 @@ public class ContinuousEffectsList extends ArrayList } break; case Custom: + case UntilYourNextTurn: + case UntilEndOfYourNextTurn: + // until your turn effects continue until real turn reached, their used it's own inactive method + // 514.2 Second, the following actions happen simultaneously: all damage marked on permanents + // (including phased-out permanents) is removed and all "until end of turn" and "this turn" effects end. + // This turn-based action doesn’t use the stack. // custom effects must process it's own inactive method (override) // custom effects may not end, if the source permanent of the effect has left the game // 800.4a (only any effects which give that player control of any objects or players end) @@ -156,18 +164,14 @@ public class ContinuousEffectsList extends ArrayList // end of turn discards on cleanup steps // 514.2 break; - case UntilYourNextTurn: - case UntilEndOfYourNextTurn: - // until your turn effects continue until real turn reached, their used it's own inactive method - // 514.2 Second, the following actions happen simultaneously: all damage marked on permanents - // (including phased-out permanents) is removed and all "until end of turn" and "this turn" effects end. - // This turn-based action doesn’t use the stack. - if (effect.isInactive(ability, game)) { + case UntilSourceLeavesBattlefield: + if (hasOwnerLeftGame || game.getState().getZone(ability.getSourceId()) != Zone.BATTLEFIELD) { it.remove(); } break; - case UntilSourceLeavesBattlefield: - if (hasOwnerLeftGame || Zone.BATTLEFIELD != game.getState().getZone(ability.getSourceId())) { + case WhileControlled: + Permanent permanent = ability.getSourcePermanentIfItStillExists(game); + if (hasOwnerLeftGame || permanent == null || !permanent.isControlledBy(ability.getControllerId())) { it.remove(); } break; @@ -188,24 +192,19 @@ public class ContinuousEffectsList extends ArrayList * @param source - connected ability */ public void addEffect(T effect, Ability source) { - if (effectAbilityMap.containsKey(effect.getId())) { - Set set = effectAbilityMap.get(effect.getId()); - for (Ability ability : set) { - if (ability.getId().equals(source.getId()) && ability.getSourceId().equals(source.getSourceId())) { - return; - } - } - set.add(source); + Set set = effectAbilityMap.computeIfAbsent(effect.getId(), x -> new HashSet<>()); + if (set.stream() + .filter(ability -> ability.getSourceId().equals(source.getSourceId())) + .map(Ability::getId) + .anyMatch(source.getId()::equals)) { return; } - Set set = new HashSet<>(); set.add(source); - this.effectAbilityMap.put(effect.getId(), set); this.add(effect); } public Set getAbility(UUID effectId) { - return effectAbilityMap.getOrDefault(effectId, new HashSet<>()); + return effectAbilityMap.computeIfAbsent(effectId, x -> new HashSet<>()); } public void removeTemporaryEffects() { @@ -226,7 +225,7 @@ public class ContinuousEffectsList extends ArrayList @Override public boolean contains(Object object) { - if (object == null || !(object instanceof ContinuousEffect)) { + if (!(object instanceof ContinuousEffect)) { return false; } diff --git a/Mage/src/main/java/mage/abilities/effects/CreateTokenCopySourceEffect.java b/Mage/src/main/java/mage/abilities/effects/CreateTokenCopySourceEffect.java index 3ea68622117..94ad0b02f0a 100644 --- a/Mage/src/main/java/mage/abilities/effects/CreateTokenCopySourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/CreateTokenCopySourceEffect.java @@ -45,7 +45,7 @@ public class CreateTokenCopySourceEffect extends OneShotEffect { CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect( source.getControllerId(), null, false, number, tapped, false ); - effect.setTargetPointer(new FixedTarget(source.getSourceId())); + effect.setTargetPointer(new FixedTarget(source.getSourceId(), game)); return effect.apply(game, source); } diff --git a/Mage/src/main/java/mage/abilities/effects/Effects.java b/Mage/src/main/java/mage/abilities/effects/Effects.java index 2a6d8c05c77..5c11da1c4d0 100644 --- a/Mage/src/main/java/mage/abilities/effects/Effects.java +++ b/Mage/src/main/java/mage/abilities/effects/Effects.java @@ -73,6 +73,7 @@ public class Effects extends ArrayList { } else if (lastRule != null && lastRule.length() > 3) { //check if lastRule already has appropriate punctuation, if so, add a space. if (lastRule.endsWith(".\"") + || lastRule.endsWith(".]") || lastRule.endsWith(".)") || lastRule.endsWith(".)") || lastRule.endsWith(".")) { @@ -105,8 +106,10 @@ public class Effects extends ArrayList { if (lastRule != null && lastRule.length() > 3 && !lastRule.endsWith(".") && !lastRule.endsWith("\"") + && !lastRule.endsWith(".]") && !lastRule.startsWith("Level ") && !lastRule.endsWith(".)") + && !lastRule.endsWith("
") && !lastRule.endsWith("")) { sbText.append('.'); } diff --git a/Mage/src/main/java/mage/abilities/effects/EquipEffect.java b/Mage/src/main/java/mage/abilities/effects/EquipEffect.java deleted file mode 100644 index 392713bbf7b..00000000000 --- a/Mage/src/main/java/mage/abilities/effects/EquipEffect.java +++ /dev/null @@ -1,39 +0,0 @@ -package mage.abilities.effects; - -import mage.abilities.Ability; -import mage.abilities.effects.common.AttachEffect; -import mage.constants.Outcome; -import mage.constants.SubType; -import mage.game.Game; -import mage.game.permanent.Permanent; - -public class EquipEffect extends AttachEffect { - - public EquipEffect(Outcome outcome) { - super(outcome, "Equip"); - } - - public EquipEffect(EquipEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent sourcePermanent = game.getPermanent(source.getSourceId()); - //301.5c An Equipment that’s also a creature can’t equip a creature. An Equipment that loses the subtype - // “Equipment” can’t equip a creature. An Equipment can’t equip itself. An Equipment that equips an illegal or - // nonexistent permanent becomes unattached from that permanent but remains on the battlefield. (This is a - // state-based action. See rule 704.) An Equipment can’t equip more than one creature. If a spell or ability - // would cause an Equipment to equip more than one creature, the Equipment’s controller chooses which creature - // it equips. - if (sourcePermanent != null && sourcePermanent.hasSubtype(SubType.EQUIPMENT, game) && !sourcePermanent.isCreature(game)) { - return super.apply(game, source); - } - return false; - } - - @Override - public EquipEffect copy(){ - return new EquipEffect(this); - } -} diff --git a/Mage/src/main/java/mage/abilities/effects/common/AttachEffect.java b/Mage/src/main/java/mage/abilities/effects/common/AttachEffect.java index 1918bc1c6c5..9290eccbd74 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/AttachEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/AttachEffect.java @@ -10,6 +10,8 @@ import mage.players.Player; import mage.target.TargetCard; import mage.util.CardUtil; +import java.util.UUID; + /** * @author BetaSteward_at_googlemail.com */ @@ -36,30 +38,29 @@ public class AttachEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Permanent sourcePermanent = game.getPermanent(source.getSourceId()); - if (sourcePermanent != null) { - // if it activating on the stack then allow +1 zcc - int zcc = game.getState().getZoneChangeCounter(sourcePermanent.getId()); - if (zcc == CardUtil.getActualSourceObjectZoneChangeCounter(game, source) - || zcc == CardUtil.getActualSourceObjectZoneChangeCounter(game, source) + 1) { - Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (permanent != null) { - return permanent.addAttachment(source.getSourceId(), source, game); - } else { - Player player = game.getPlayer(getTargetPointer().getFirst(game, source)); - if (player != null) { - return player.addAttachment(source.getSourceId(), source, game); - } - if (!source.getTargets().isEmpty() && source.getTargets().get(0) instanceof TargetCard) { // e.g. Spellweaver Volute - Card card = game.getCard(getTargetPointer().getFirst(game, source)); - if (card != null) { - return card.addAttachment(source.getSourceId(), source, game); - } - } - } - } + if (sourcePermanent == null) { + return false; } - - return false; + // if it activating on the stack then allow +1 zcc + int zcc = game.getState().getZoneChangeCounter(sourcePermanent.getId()); + if (zcc != CardUtil.getActualSourceObjectZoneChangeCounter(game, source) + && zcc != CardUtil.getActualSourceObjectZoneChangeCounter(game, source) + 1) { + return false; + } + UUID targetId = getTargetPointer().getFirst(game, source); + Permanent permanent = game.getPermanent(targetId); + if (permanent != null) { + return permanent.addAttachment(source.getSourceId(), source, game); + } + Player player = game.getPlayer(targetId); + if (player != null) { + return player.addAttachment(source.getSourceId(), source, game); + } + if (source.getTargets().isEmpty() || !(source.getTargets().get(0) instanceof TargetCard)) { + return false; + } + Card card = game.getCard(targetId); + return card != null && card.addAttachment(source.getSourceId(), source, game); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/BalanceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/BalanceEffect.java index 8da6c252587..4b9a19756aa 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/BalanceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/BalanceEffect.java @@ -7,7 +7,6 @@ import mage.cards.Cards; import mage.cards.CardsImpl; import mage.constants.Outcome; import mage.filter.FilterCard; -import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterControlledLandPermanent; import mage.filter.common.FilterControlledPermanent; @@ -25,6 +24,10 @@ import java.util.stream.Collectors; */ public class BalanceEffect extends OneShotEffect { + private static final FilterControlledLandPermanent filterLand = new FilterControlledLandPermanent("lands to keep"); + private static final FilterControlledCreaturePermanent filterCreature = new FilterControlledCreaturePermanent("creatures to keep"); + private static final FilterCard filterCardHand = new FilterCard("cards to keep"); + public BalanceEffect() { super(Outcome.Sacrifice); staticText = "each player chooses a number of lands they control " @@ -49,8 +52,8 @@ public class BalanceEffect extends OneShotEffect { return false; } - choosePermanentsToKeep(game, source, controller, StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND, new FilterControlledLandPermanent("lands to keep")); - choosePermanentsToKeep(game, source, controller, StaticFilters.FILTER_CONTROLLED_CREATURE, new FilterControlledCreaturePermanent("creatures to keep")); + choosePermanentsToKeep(game, source, controller, filterLand); + choosePermanentsToKeep(game, source, controller, filterCreature); //Cards in hand int lowestHandSize = Integer.MAX_VALUE; @@ -70,17 +73,30 @@ public class BalanceEffect extends OneShotEffect { continue; } - TargetCardInHand target = new TargetCardInHand(lowestHandSize, new FilterCard("cards to keep")); - if (!target.choose(Outcome.Protect, player.getId(), source.getSourceId(), game)) { - continue; - } - Set allCardsInHand = player.getHand().getCards(game); Set cardsToKeep = new LinkedHashSet<>(); - for (Card card : allCardsInHand) { - if (card != null && target.getTargets().contains(card.getId())) { - cardsToKeep.add(card); + if (lowestHandSize > 0) { + TargetCardInHand target = new TargetCardInHand(lowestHandSize, filterCardHand); + if (target.choose(Outcome.Protect, player.getId(), source.getSourceId(), game)) { + for (Card card : allCardsInHand) { + if (card != null && target.getTargets().contains(card.getId())) { + cardsToKeep.add(card); + } + } + // Prevent possible cheat by disconnecting. If no targets are chosen, just pick the first in the list. + // https://github.com/magefree/mage/issues/4263 + } else { + int numCards = 0; + for (Card card : allCardsInHand) { + if (numCards >= lowestHandSize) { + break; + } + if (card != null) { + cardsToKeep.add(card); + numCards++; + } + } } } @@ -100,7 +116,7 @@ public class BalanceEffect extends OneShotEffect { } private void choosePermanentsToKeep(Game game, Ability source, Player controller, - FilterControlledPermanent filterPermanent, FilterControlledPermanent filterPermanentDialog) { + FilterControlledPermanent filterPermanent) { int lowestPermanentsCount = Integer.MAX_VALUE; for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { Player player = game.getPlayer(playerId); @@ -120,24 +136,37 @@ public class BalanceEffect extends OneShotEffect { continue; } - TargetControlledPermanent target = new TargetControlledPermanent(lowestPermanentsCount, lowestPermanentsCount, filterPermanentDialog, true); - if (!target.choose(Outcome.Protect, player.getId(), source.getSourceId(), game)) { - continue; - } - List allPermanentsOfType = game.getBattlefield().getActivePermanents(filterPermanent, player.getId(), source.getSourceId(), game); List permanentsToKeep = new ArrayList<>(); - for (Permanent permanent : allPermanentsOfType) { - if (permanent != null && target.getTargets().contains(permanent.getId())) { - permanentsToKeep.add(permanent); + if (lowestPermanentsCount > 0) { + TargetControlledPermanent target = new TargetControlledPermanent(lowestPermanentsCount, lowestPermanentsCount, filterPermanent, true); + if (target.choose(Outcome.Protect, player.getId(), source.getSourceId(), game)) { + for (Permanent permanent : allPermanentsOfType) { + if (permanent != null && target.getTargets().contains(permanent.getId())) { + permanentsToKeep.add(permanent); + } + } + // Prevent possible cheat by disconnecting. If no targets are chosen, just pick the first in the list. + // https://github.com/magefree/mage/issues/4263 + } else { + int numPermanents = 0; + for (Permanent permanent : allPermanentsOfType) { + if (numPermanents >= lowestPermanentsCount) { + break; + } + if (permanent != null) { + permanentsToKeep.add(permanent); + numPermanents++; + } + } } } List playerPermanentsToSacrifice = allPermanentsOfType.stream().filter(e -> !permanentsToKeep.contains(e)).collect(Collectors.toList()); permanentsToSacrifice.addAll(playerPermanentsToSacrifice); - if (playerPermanentsToSacrifice.isEmpty()) { + if (!playerPermanentsToSacrifice.isEmpty()) { game.informPlayers(player.getLogName() + " chose permanents to be sacrificed: " + playerPermanentsToSacrifice.stream().map(Permanent::getLogName).collect(Collectors.joining(", "))); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/ChooseACardNameEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ChooseACardNameEffect.java index 9bcb1fb6cd9..111761e43b3 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ChooseACardNameEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ChooseACardNameEffect.java @@ -25,7 +25,7 @@ public class ChooseACardNameEffect extends OneShotEffect { public enum TypeOfName { ALL("card name", CardRepository.instance::getNames), - NOT_BASIC_LAND_NAME("card name other than a basic land card", CardRepository.instance::getNotBasicLandNames), + NOT_BASIC_LAND_NAME("card name other than a basic land card name", CardRepository.instance::getNotBasicLandNames), NONBASIC_LAND_NAME("nonbasic land card name", CardRepository.instance::getNonbasicLandNames), NON_ARTIFACT_AND_NON_LAND_NAME("nonartifact, nonland card name", CardRepository.instance::getNonArtifactAndNonLandNames), NON_LAND_AND_NON_CREATURE_NAME("nonland and non creature name", CardRepository.instance::getNonLandAndNonCreatureNames), diff --git a/Mage/src/main/java/mage/abilities/effects/common/CipherEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CipherEffect.java index 19585e2a310..8dc9bdfbfd5 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CipherEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CipherEffect.java @@ -74,7 +74,7 @@ public class CipherEffect extends OneShotEffect { String ruleText = "you may cast a copy of " + sourceCard.getLogName() + " without paying its mana cost"; Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(new CipherStoreEffect(source.getSourceId(), ruleText), true); ContinuousEffect effect = new GainAbilityTargetEffect(ability, Duration.Custom); - effect.setTargetPointer(new FixedTarget(target.getFirstTarget())); + effect.setTargetPointer(new FixedTarget(target.getFirstTarget(), game)); game.addEffect(effect, source); if (!game.isSimulation()) { game.informPlayers(sourceCard.getLogName() + ": Spell ciphered to " + targetCreature.getLogName()); diff --git a/Mage/src/main/java/mage/abilities/effects/common/CopyEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CopyEffect.java index d71a124649c..f4a585a7610 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CopyEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CopyEffect.java @@ -10,20 +10,20 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.PermanentCard; import mage.game.permanent.PermanentToken; +import mage.util.CardUtil; import mage.util.functions.CopyApplier; import java.util.UUID; /** + * Make battlefield's permanent as a copy of the source object + * (source can be a card or another permanent) + * * @author BetaSteward_at_googlemail.com */ public class CopyEffect extends ContinuousEffectImpl { - /** - * Object we copy from - */ protected MageObject copyFromObject; - protected UUID copyToObjectId; protected CopyApplier applier; @@ -47,9 +47,13 @@ public class CopyEffect extends ContinuousEffectImpl { @Override public void init(Ability source, Game game) { super.init(source, game); + + // must copy the default side of the card (example: clone with mdf card) if (!(copyFromObject instanceof Permanent) && (copyFromObject instanceof Card)) { - this.copyFromObject = new PermanentCard((Card) copyFromObject, source.getControllerId(), game); + Card newBluePrint = CardUtil.getDefaultCardSideForBattlefield(game, (Card) copyFromObject); + this.copyFromObject = new PermanentCard(newBluePrint, source.getControllerId(), game); } + Permanent permanent = game.getPermanent(copyToObjectId); if (permanent != null) { affectedObjectList.add(new MageObjectReference(permanent, game)); @@ -98,7 +102,7 @@ public class CopyEffect extends ContinuousEffectImpl { permanent.setName(copyFromObject.getName()); permanent.getColor(game).setColor(copyFromObject.getColor(game)); permanent.getManaCost().clear(); - permanent.getManaCost().add(copyFromObject.getManaCost()); + permanent.getManaCost().add(copyFromObject.getManaCost().copy()); permanent.removeAllCardTypes(game); for (CardType type : copyFromObject.getCardType(game)) { permanent.addCardType(game, type); @@ -128,6 +132,7 @@ public class CopyEffect extends ContinuousEffectImpl { // and abilities that were chosen for this creature as it entered the battlefield. (2018-03-16) permanent.getPower().setValue(copyFromObject.getPower().getBaseValueModified()); permanent.getToughness().setValue(copyFromObject.getToughness().getBaseValueModified()); + permanent.setStartingLoyalty(copyFromObject.getStartingLoyalty()); if (copyFromObject instanceof Permanent) { Permanent targetPermanent = (Permanent) copyFromObject; permanent.setTransformed(targetPermanent.isTransformed()); diff --git a/Mage/src/main/java/mage/abilities/effects/common/CreateDelayedTriggeredAbilityEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CreateDelayedTriggeredAbilityEffect.java index 9fe41bcabaf..543ca0539da 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CreateDelayedTriggeredAbilityEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CreateDelayedTriggeredAbilityEffect.java @@ -48,9 +48,7 @@ public class CreateDelayedTriggeredAbilityEffect extends OneShotEffect { DelayedTriggeredAbility delayedAbility = ability.copy(); if (this.copyTargets) { if (source.getTargets().isEmpty()) { - for (Effect effect : delayedAbility.getEffects()) { - effect.setTargetPointer(targetPointer); - } + delayedAbility.getEffects().setTargetPointer(targetPointer); } else { delayedAbility.getTargets().addAll(source.getTargets()); for (Effect effect : delayedAbility.getEffects()) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java index 2e654033f6b..e46bb168a6f 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java @@ -3,53 +3,58 @@ package mage.abilities.effects.common; import mage.MageObject; import mage.ObjectColor; import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; import mage.abilities.Mode; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility; import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.HasteAbility; import mage.cards.Card; import mage.constants.*; +import mage.counters.CounterType; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.token.EmptyToken; import mage.target.targetpointer.FixedTarget; +import mage.target.targetpointer.FixedTargets; import mage.util.CardUtil; import mage.util.functions.CopyApplier; import mage.util.functions.EmptyCopyApplier; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.UUID; +import java.util.*; /** * @author LevelX2 */ public class CreateTokenCopyTargetEffect extends OneShotEffect { - private final UUID playerId; - private final CardType additionalCardType; - private boolean hasHaste; - private int number; + private final Set> abilityClazzesToRemove; private final List addedTokenPermanents; + private final List additionalAbilities; + private final CardType additionalCardType; private SubType additionalSubType; - private SubType onlySubType; - private final boolean tapped; - private final boolean attacking; private final UUID attackedPlayer; - private final int tokenPower; - private final int tokenToughness; - private final boolean gainsFlying; + private final boolean attacking; private boolean becomesArtifact; private ObjectColor color; - private boolean useLKI = false; + private CounterType counter; + private final boolean gainsFlying; + private boolean hasHaste; private boolean isntLegendary = false; - private int startingLoyalty = -1; - private final List additionalAbilities = new ArrayList(); + private int number; + private int numberOfCounters; + private SubType onlySubType; + private final UUID playerId; + private final boolean tapped; private Permanent savedPermanent = null; + private int startingLoyalty = -1; + private final int tokenPower; + private final int tokenToughness; + private boolean useLKI = false; + public CreateTokenCopyTargetEffect(boolean useLKI) { this(); @@ -60,6 +65,12 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect { this((UUID) null); } + public CreateTokenCopyTargetEffect(CounterType counter, int numberOfCounters) { + this((UUID) null); + this.counter = counter; + this.numberOfCounters = numberOfCounters; + } + public CreateTokenCopyTargetEffect(UUID playerId) { this(playerId, null, false); } @@ -73,8 +84,8 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect { } /** - * @param playerId null the token is controlled/owned by the - * controller of the source ability + * @param playerId null the token is controlled/owned by the controller of + * the source ability * @param additionalCardType the token gains this card type in addition * @param hasHaste the token gains haste * @param number number of tokens to put into play @@ -102,27 +113,37 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect { this.tokenPower = power; this.tokenToughness = toughness; this.gainsFlying = gainsFlying; + + this.abilityClazzesToRemove = new HashSet<>(); + this.additionalAbilities = new ArrayList<>(); } public CreateTokenCopyTargetEffect(final CreateTokenCopyTargetEffect effect) { super(effect); - this.playerId = effect.playerId; - this.additionalCardType = effect.additionalCardType; - this.hasHaste = effect.hasHaste; + + this.abilityClazzesToRemove = new HashSet<>(effect.abilityClazzesToRemove); this.addedTokenPermanents = new ArrayList<>(effect.addedTokenPermanents); - this.number = effect.number; + this.additionalAbilities = new ArrayList<>(effect.additionalAbilities); + this.additionalCardType = effect.additionalCardType; this.additionalSubType = effect.additionalSubType; - this.onlySubType = effect.onlySubType; - this.tapped = effect.tapped; - this.attacking = effect.attacking; this.attackedPlayer = effect.attackedPlayer; - this.tokenPower = effect.tokenPower; - this.tokenToughness = effect.tokenToughness; - this.gainsFlying = effect.gainsFlying; + this.attacking = effect.attacking; this.becomesArtifact = effect.becomesArtifact; this.color = effect.color; - this.useLKI = effect.useLKI; + this.counter = effect.counter; + this.gainsFlying = effect.gainsFlying; + this.hasHaste = effect.hasHaste; this.isntLegendary = effect.isntLegendary; + this.number = effect.number; + this.numberOfCounters = effect.numberOfCounters; + this.onlySubType = effect.onlySubType; + this.playerId = effect.playerId; + this.savedPermanent = effect.savedPermanent; + this.startingLoyalty = effect.startingLoyalty; + this.tapped = effect.tapped; + this.tokenPower = effect.tokenPower; + this.tokenToughness = effect.tokenToughness; + this.useLKI = effect.useLKI; } @Override @@ -215,11 +236,35 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect { } additionalAbilities.stream().forEach(token::addAbility); + if (!this.abilityClazzesToRemove.isEmpty()) { + List abilitiesToRemoveTmp = new ArrayList<>(); + + // Find the ones to remove + for (Ability ability : token.getAbilities()) { + if (this.abilityClazzesToRemove.contains(ability.getClass())) { + abilitiesToRemoveTmp.add(ability); + } + } + + // Remove them + for (Ability ability : abilitiesToRemoveTmp) { + // Remove subabilities + token.removeAbilities(ability.getSubAbilities()); + // Remove the ability + token.removeAbility(ability); + } + } + token.putOntoBattlefield(number, game, source, playerId == null ? source.getControllerId() : playerId, tapped, attacking, attackedPlayer); for (UUID tokenId : token.getLastAddedTokenIds()) { // by cards like Doubling Season multiple tokens can be added to the battlefield Permanent tokenPermanent = game.getPermanent(tokenId); if (tokenPermanent != null) { addedTokenPermanents.add(tokenPermanent); + // add counters if necessary ie Ochre Jelly + if (counter != null + && numberOfCounters > 0) { + tokenPermanent.addCounters(counter.createInstance(numberOfCounters), source.getControllerId(), source, game); + } } } return true; @@ -266,7 +311,7 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect { return sb.toString(); } - public List getAddedPermanent() { + public List getAddedPermanents() { return addedTokenPermanents; } @@ -306,28 +351,58 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect { this.number = number; } + public void sacrificeTokensCreatedAtNextEndStep(Game game, Ability source) { + this.removeTokensCreatedAtEndOf(game, source, PhaseStep.END_TURN, false); + } + public void exileTokensCreatedAtNextEndStep(Game game, Ability source) { - for (Permanent tokenPermanent : addedTokenPermanents) { - ExileTargetEffect exileEffect = new ExileTargetEffect(null, "", Zone.BATTLEFIELD); - exileEffect.setTargetPointer(new FixedTarget(tokenPermanent, game)); - game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect), source); - } + this.removeTokensCreatedAtEndOf(game, source, PhaseStep.END_TURN, true); + } + + public void sacrificeTokensCreatedAtEndOfCombat(Game game, Ability source) { + this.removeTokensCreatedAtEndOf(game, source, PhaseStep.END_COMBAT, false); } public void exileTokensCreatedAtEndOfCombat(Game game, Ability source) { - for (Permanent tokenPermanent : addedTokenPermanents) { + this.removeTokensCreatedAtEndOf(game, source, PhaseStep.END_COMBAT, true); + } - ExileTargetEffect exileEffect = new ExileTargetEffect(null, "", Zone.BATTLEFIELD); - exileEffect.setTargetPointer(new FixedTarget(tokenPermanent, game)); - game.addDelayedTriggeredAbility(new AtTheEndOfCombatDelayedTriggeredAbility(exileEffect), source); + private void removeTokensCreatedAtEndOf(Game game, Ability source, PhaseStep phaseStepToExileCards, boolean exile) { + Effect effect; + if (exile) { + effect = new ExileTargetEffect(null, "", Zone.BATTLEFIELD).setText("exile the token copies"); + } else { + effect = new SacrificeTargetEffect("sacrifice the token copies"); } + effect.setTargetPointer(new FixedTargets(addedTokenPermanents, game)); + + DelayedTriggeredAbility exileAbility; + + switch (phaseStepToExileCards) { + case END_TURN: + exileAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect); + break; + case END_COMBAT: + exileAbility = new AtTheEndOfCombatDelayedTriggeredAbility(effect); + break; + default: + return; + } + + game.addDelayedTriggeredAbility(exileAbility, source); + } + + public void addAbilityClassesToRemoveFromTokens(Class clazz) { + this.abilityClazzesToRemove.add(clazz); } public void addAdditionalAbilities(Ability... abilities) { - Arrays.stream(abilities).forEach(this.additionalAbilities::add); + this.additionalAbilities.addAll(Arrays.asList(abilities)); } - public void setSavedPermanent(Permanent savedPermanent) { + + public CreateTokenCopyTargetEffect setSavedPermanent(Permanent savedPermanent) { this.savedPermanent = savedPermanent; + return this; } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/CrewsVehicleSourceTriggeredAbility.java b/Mage/src/main/java/mage/abilities/effects/common/CrewsVehicleSourceTriggeredAbility.java index 36cbab3a334..35b2f0842dd 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CrewsVehicleSourceTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CrewsVehicleSourceTriggeredAbility.java @@ -34,7 +34,7 @@ public class CrewsVehicleSourceTriggeredAbility extends TriggeredAbilityImpl { if (event.getTargetId().equals(getSourceId())) { for (Effect effect : getEffects()) { // set the vehicle id as target - effect.setTargetPointer(new FixedTarget(event.getSourceId())); + effect.setTargetPointer(new FixedTarget(event.getSourceId(), game)); } return true; } @@ -43,6 +43,6 @@ public class CrewsVehicleSourceTriggeredAbility extends TriggeredAbilityImpl { @Override public String getTriggerPhrase() { - return "When {this} crews a Vehicle, " ; + return "Whenever {this} crews a Vehicle, " ; } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/DamageMultiEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DamageMultiEffect.java index 34d92901d2b..ce3d09365b0 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DamageMultiEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DamageMultiEffect.java @@ -12,9 +12,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.Target; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; +import java.util.*; /** * @author BetaSteward_at_googlemail.com @@ -79,10 +77,21 @@ public class DamageMultiEffect extends OneShotEffect { if (staticText != null && !staticText.isEmpty()) { return staticText; } - if (amount.toString().equals("3")) { - return this.sourceName + " deals 3 damage divided as you choose among one, two, or three targets"; + StringBuilder sb = new StringBuilder(sourceName); + sb.append(" deals "); + + String amountString = amount.toString(); + sb.append(amountString); + sb.append(" damage divided as you choose among "); + sb.append(amountString.equals("2") ? "one or two " : amountString.equals("3") ? "one, two, or three " : "any number of "); + + String targetName = mode.getTargets().get(0).getTargetName(); + if (!targetName.contains("target")) { + sb.append("target "); } - return this.sourceName + " deals " + amount.toString() + " damage divided as you choose among any number of " + mode.getTargets().get(0).getTargetName(); + sb.append(targetName); + + return sb.toString(); } public String getSourceName() { diff --git a/Mage/src/main/java/mage/abilities/effects/common/DamagePlayersEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DamagePlayersEffect.java index c2d8dd83cb1..7504fc3ebd1 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DamagePlayersEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DamagePlayersEffect.java @@ -1,6 +1,7 @@ package mage.abilities.effects.common; import mage.abilities.Ability; +import mage.abilities.Mode; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.OneShotEffect; @@ -9,15 +10,17 @@ import mage.constants.TargetController; import mage.game.Game; import mage.players.Player; +import java.util.Collection; import java.util.UUID; /** * @author BetaSteward_at_googlemail.com */ public class DamagePlayersEffect extends OneShotEffect { - private DynamicValue amount; - private TargetController controller; - private String sourceName = "{this}"; + + private final DynamicValue amount; + private final TargetController controller; + private final String sourceName; public DamagePlayersEffect(int amount) { this(Outcome.Damage, StaticValue.get(amount)); @@ -28,10 +31,7 @@ public class DamagePlayersEffect extends OneShotEffect { } public DamagePlayersEffect(int amount, TargetController controller, String whoDealDamageName) { - this(Outcome.Damage, StaticValue.get(amount), controller); - - this.sourceName = whoDealDamageName; - setText(); // TODO: replace to @Override public String getText() + this(Outcome.Damage, StaticValue.get(amount), controller, whoDealDamageName); } public DamagePlayersEffect(Outcome outcome, DynamicValue amount) { @@ -39,10 +39,14 @@ public class DamagePlayersEffect extends OneShotEffect { } public DamagePlayersEffect(Outcome outcome, DynamicValue amount, TargetController controller) { + this(outcome, amount, controller, "{this}"); + } + + public DamagePlayersEffect(Outcome outcome, DynamicValue amount, TargetController controller, String whoDealDamageName) { super(outcome); this.amount = amount; this.controller = controller; - setText(); + this.sourceName = whoDealDamageName; } @@ -55,26 +59,25 @@ public class DamagePlayersEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { + Collection players; switch (controller) { case ANY: - for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - player.damage(amount.calculate(game, source, this), source.getSourceId(), source, game); - } - } + case EACH_PLAYER: + players = game.getState().getPlayersInRange(source.getControllerId(), game); break; case OPPONENT: - for (UUID playerId : game.getOpponents(source.getControllerId())) { - Player player = game.getPlayer(playerId); - if (player != null) { - player.damage(amount.calculate(game, source, this), source.getSourceId(), source, game); - } - } + players = game.getOpponents(source.getControllerId()); break; default: throw new UnsupportedOperationException("TargetController type not supported."); } + int damage = amount.calculate(game, source, this); + for (UUID playerId : players) { + Player player = game.getPlayer(playerId); + if (player != null) { + player.damage(damage, source.getSourceId(), source, game); + } + } return true; } @@ -83,19 +86,20 @@ public class DamagePlayersEffect extends OneShotEffect { return new DamagePlayersEffect(this); } - private void setText() { - StringBuilder sb = new StringBuilder().append(this.sourceName).append(" deals ").append(amount.toString()); + @Override + public String getText(Mode mode) { + if (staticText != null && !staticText.isEmpty()) { + return staticText; + } + String message = sourceName + " deals " + amount.toString() + " damage to each "; switch (controller) { case ANY: - sb.append(" damage to each player"); - break; + case EACH_PLAYER: + return message + "player"; case OPPONENT: - sb.append(" damage to each opponent"); - break; + return message + "opponent"; default: throw new UnsupportedOperationException("TargetController type not supported."); } - staticText = sb.toString(); } - } diff --git a/Mage/src/main/java/mage/abilities/effects/common/DamageTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DamageTargetEffect.java index 55f24bf7cd3..bb4608f7bf3 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DamageTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DamageTargetEffect.java @@ -11,6 +11,7 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.Target; +import mage.util.CardUtil; import java.util.UUID; @@ -159,10 +160,21 @@ public class DamageTargetEffect extends OneShotEffect { sb.append(targetDescription); } else { if (!mode.getTargets().isEmpty()) { - String targetName = mode.getTargets().get(0).getTargetName(); + Target firstTarget = mode.getTargets().get(0); + String targetName = firstTarget.getTargetName(); if (targetName.contains("any")) { sb.append(targetName); } else { + if (firstTarget.getMinNumberOfTargets() == 0) { + int maxTargets = firstTarget.getMaxNumberOfTargets(); + if (maxTargets == Integer.MAX_VALUE) { + sb.append("any number of "); + } else { + sb.append("up to "); + sb.append(CardUtil.numberToText(maxTargets)); + sb.append(' '); + } + } sb.append("target ").append(targetName); } } else { diff --git a/Mage/src/main/java/mage/abilities/effects/common/DamageWithPowerFromOneToAnotherTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DamageWithPowerFromOneToAnotherTargetEffect.java index 7fba58ba9fd..0a3500533ed 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DamageWithPowerFromOneToAnotherTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DamageWithPowerFromOneToAnotherTargetEffect.java @@ -64,10 +64,10 @@ public class DamageWithPowerFromOneToAnotherTargetEffect extends OneShotEffect { throw new IllegalStateException("It must have two targets, but found " + mode.getTargets().size()); } + String targetName = mode.getTargets().get(1).getTargetName(); // Target creature you control deals damage equal to its power to target creature you don't control String sb = (this.firstTargetName != null ? this.firstTargetName : "Target " + mode.getTargets().get(0).getTargetName()) + - " deals damage equal to its power to target " + - mode.getTargets().get(1).getTargetName(); + " deals damage equal to its power to " + (targetName.contains("other") ? "" : "target ") + targetName; return sb; } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/DestroyTargetAtBeginningOfNextEndStepEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DestroyTargetAtBeginningOfNextEndStepEffect.java index 2fe56edee70..bf15998a409 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DestroyTargetAtBeginningOfNextEndStepEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DestroyTargetAtBeginningOfNextEndStepEffect.java @@ -31,7 +31,7 @@ public class DestroyTargetAtBeginningOfNextEndStepEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { DestroyTargetEffect effect = new DestroyTargetEffect(); - effect.setTargetPointer(new FixedTarget(source.getFirstTarget())); + effect.setTargetPointer(new FixedTarget(source.getFirstTarget(), game)); AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect); game.addDelayedTriggeredAbility(delayedAbility, source); return true; diff --git a/Mage/src/main/java/mage/abilities/effects/common/DoIfCostPaid.java b/Mage/src/main/java/mage/abilities/effects/common/DoIfCostPaid.java index 418b3e0eaa4..6ef58bb99b5 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DoIfCostPaid.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DoIfCostPaid.java @@ -17,7 +17,7 @@ public class DoIfCostPaid extends OneShotEffect { protected Effects executingEffects = new Effects(); protected Effects otherwiseEffects = new Effects(); // used for Imprison - private final Cost cost; + protected final Cost cost; private final String chooseUseText; private final boolean optional; @@ -88,7 +88,7 @@ public class DoIfCostPaid extends OneShotEffect { if (!effectText.isEmpty() && effectText.charAt(effectText.length() - 1) == '.') { effectText = effectText.substring(0, effectText.length() - 1); } - message = getCostText() + (effectText.isEmpty() ? "" : " and " + effectText) + "?"; + message = CardUtil.addCostVerb(cost.getText()) + (effectText.isEmpty() ? "" : " and " + effectText) + "?"; message = Character.toUpperCase(message.charAt(0)) + message.substring(1); } else { message = chooseUseText; @@ -155,19 +155,13 @@ public class DoIfCostPaid extends OneShotEffect { if (!staticText.isEmpty()) { return staticText; } - return (optional ? "you may " : "") + getCostText() + ". If you do, " + executingEffects.getText(mode) + return (optional ? "you may " : "") + + CardUtil.addCostVerb(cost.getText()) + + ". If you do, " + + executingEffects.getText(mode) + (!otherwiseEffects.isEmpty() ? " If you don't, " + otherwiseEffects.getText(mode) : ""); } - protected String getCostText() { - StringBuilder sb = new StringBuilder(); - String costText = cost.getText(); - if (!CardUtil.checkCostWords(costText)) { - sb.append("pay "); - } - return sb.append(costText).toString(); - } - @Override public void setValue(String key, Object value) { super.setValue(key, value); diff --git a/Mage/src/main/java/mage/abilities/effects/common/DoWhenCostPaid.java b/Mage/src/main/java/mage/abilities/effects/common/DoWhenCostPaid.java index b07db7a53cc..b44e018038b 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DoWhenCostPaid.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DoWhenCostPaid.java @@ -54,7 +54,9 @@ public class DoWhenCostPaid extends OneShotEffect { cost.clearPaid(); int bookmark = game.bookmarkState(); if (cost.pay(source, game, source, player.getId(), false)) { - ability.getEffects().setTargetPointer(getTargetPointer()); + if (ability.getTargets().isEmpty()) { + ability.getEffects().setTargetPointer(getTargetPointer()); + } game.fireReflexiveTriggeredAbility(ability, source); player.resetStoredBookmark(game); return true; @@ -72,16 +74,10 @@ public class DoWhenCostPaid extends OneShotEffect { if (!staticText.isEmpty()) { return staticText; } - return (optional ? "you may " : "") + getCostText() + ". When you do, " + ability.getText(); - } - - private String getCostText() { - StringBuilder sb = new StringBuilder(); - String costText = cost.getText(); - if (!CardUtil.checkCostWords(costText)) { - sb.append("pay "); - } - return sb.append(costText).toString(); + return (optional ? "you may " : "") + + CardUtil.addCostVerb(cost.getText()) + + ". When you do, " + + CardUtil.getTextWithFirstCharLowerCase(ability.getRule()); } @Override diff --git a/Mage/src/main/java/mage/abilities/effects/common/DontUntapAsLongAsSourceTappedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DontUntapAsLongAsSourceTappedEffect.java index 1bee2394880..4fdc5473843 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DontUntapAsLongAsSourceTappedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DontUntapAsLongAsSourceTappedEffect.java @@ -17,7 +17,7 @@ import mage.game.events.GameEvent; public class DontUntapAsLongAsSourceTappedEffect extends ConditionalContinuousRuleModifyingEffect { public DontUntapAsLongAsSourceTappedEffect() { - super(new DontUntapInControllersUntapStepTargetEffect(Duration.Custom), SourceTappedCondition.instance); + super(new DontUntapInControllersUntapStepTargetEffect(Duration.Custom), SourceTappedCondition.TAPPED); staticText = "It doesn't untap during its controller's untap step for as long as {this} remains tapped."; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/DontUntapInControllersUntapStepAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DontUntapInControllersUntapStepAllEffect.java index 72fa0bee8b3..075d5614c4e 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DontUntapInControllersUntapStepAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DontUntapInControllersUntapStepAllEffect.java @@ -93,7 +93,7 @@ public class DontUntapInControllersUntapStepAllEffect extends ContinuousRuleModi StringBuilder sb = new StringBuilder(filter.getMessage()).append(" don't untap during "); switch(targetController) { case ANY: - sb.append("their controller's "); + sb.append("their controllers' "); break; default: throw new RuntimeException("Type of TargetController not supported yet!"); diff --git a/Mage/src/main/java/mage/abilities/effects/common/DontUntapInControllersUntapStepTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DontUntapInControllersUntapStepTargetEffect.java index ea1aaf6a449..c7c264c6805 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DontUntapInControllersUntapStepTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DontUntapInControllersUntapStepTargetEffect.java @@ -1,7 +1,5 @@ package mage.abilities.effects.common; -import java.util.UUID; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; @@ -12,18 +10,27 @@ import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; +import java.util.UUID; + /** - * * @author BetaSteward_at_googlemail.com */ public class DontUntapInControllersUntapStepTargetEffect extends ContinuousRuleModifyingEffectImpl { + private final String targetName; + public DontUntapInControllersUntapStepTargetEffect(Duration duration) { + this(duration, "That creature"); + } + + public DontUntapInControllersUntapStepTargetEffect(Duration duration, String targetName) { super(duration, Outcome.Detriment); + this.targetName = targetName; } public DontUntapInControllersUntapStepTargetEffect(final DontUntapInControllersUntapStepTargetEffect effect) { super(effect); + this.targetName = effect.targetName; } @Override @@ -36,16 +43,6 @@ public class DontUntapInControllersUntapStepTargetEffect extends ContinuousRuleM return false; } - @Override - public String getInfoMessage(Ability source, GameEvent event, Game game) { - MageObject mageObject = game.getObject(source.getSourceId()); - Permanent permanentToUntap = game.getPermanent((event.getTargetId())); - if (permanentToUntap != null && mageObject != null) { - return permanentToUntap.getIdName() + " doesn't untap (" + mageObject.getIdName() + ')'; - } - return null; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.UNTAP; @@ -53,14 +50,16 @@ public class DontUntapInControllersUntapStepTargetEffect extends ContinuousRuleM @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (game.getTurn().getStepType() == PhaseStep.UNTAP) { - for (UUID targetId : targetPointer.getTargets(game, source)) { - if (event.getTargetId().equals(targetId)) { - Permanent permanent = game.getPermanent(targetId); - if (permanent != null && game.isActivePlayer(permanent.getControllerId())) { - return true; - } - } + if (game.getTurn().getStepType() != PhaseStep.UNTAP) { + return false; + } + for (UUID targetId : targetPointer.getTargets(game, source)) { + if (!event.getTargetId().equals(targetId)) { + continue; + } + Permanent permanent = game.getPermanent(targetId); + if (permanent != null && game.isActivePlayer(permanent.getControllerId())) { + return true; } } return false; @@ -68,11 +67,11 @@ public class DontUntapInControllersUntapStepTargetEffect extends ContinuousRuleM @Override public String getText(Mode mode) { - if (staticText != null) { + if (staticText != null && !staticText.isEmpty()) { return staticText; } - return "target " + mode.getTargets().get(0).getTargetName() - + " doesn't untap during its controller's untap step" + (getDuration().toString().isEmpty() ? "" : " " + getDuration()); + return targetName + " doesn't untap during its controller's untap step" + + (getDuration().toString().isEmpty() ? "" : " ") + getDuration(); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/DraftFromSpellbookEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DraftFromSpellbookEffect.java new file mode 100644 index 00000000000..fdc4a0d286d --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/DraftFromSpellbookEffect.java @@ -0,0 +1,78 @@ +package mage.abilities.effects.common; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.cards.repository.CardCriteria; +import mage.cards.repository.CardInfo; +import mage.cards.repository.CardRepository; +import mage.choices.Choice; +import mage.choices.ChoiceHintType; +import mage.choices.ChoiceImpl; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; +import mage.util.RandomUtil; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * @author TheElk801 + */ +public class DraftFromSpellbookEffect extends OneShotEffect { + + private final List spellbook; + + public DraftFromSpellbookEffect(List spellbook) { + super(Outcome.DrawCard); + this.spellbook = spellbook; + staticText = "draft a card from {this}'s spellbook"; + } + + private DraftFromSpellbookEffect(final DraftFromSpellbookEffect effect) { + super(effect); + this.spellbook = effect.spellbook; + } + + @Override + public DraftFromSpellbookEffect copy() { + return new DraftFromSpellbookEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Set toSelect = new HashSet<>(); + while (toSelect.size() < 3) { + toSelect.add(RandomUtil.randomFromCollection(spellbook)); + } + Choice choice = new ChoiceImpl(true, ChoiceHintType.CARD); + choice.setMessage("Choose a card to draft"); + choice.setChoices(toSelect); + player.choose(outcome, choice, game); + String cardName = choice.getChoice(); + if (cardName == null) { + return false; + } + CardInfo cardInfo = CardRepository + .instance + .findCards(new CardCriteria().nameExact(cardName)) + .stream() + .findFirst() + .orElse(null); + if (cardInfo == null) { + return false; + } + Set cards = new HashSet<>(); + cards.add(cardInfo.getCard()); + game.loadCards(cards, player.getId()); + player.moveCards(cards, Zone.HAND, source, game); + return true; + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/DrawDiscardControllerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DrawDiscardControllerEffect.java index 280d1107689..9f80cb3b893 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DrawDiscardControllerEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DrawDiscardControllerEffect.java @@ -1,7 +1,7 @@ - package mage.abilities.effects.common; import mage.abilities.Ability; +import mage.abilities.Mode; import mage.abilities.effects.OneShotEffect; import mage.constants.Outcome; import mage.game.Game; @@ -9,19 +9,22 @@ import mage.players.Player; import mage.util.CardUtil; /** - * * @author BetaSteward_at_googlemail.com */ public class DrawDiscardControllerEffect extends OneShotEffect { - private int cardsToDraw; - private int cardsToDiscard; - private boolean optional; + private final int cardsToDraw; + private final int cardsToDiscard; + private final boolean optional; public DrawDiscardControllerEffect() { this(1, 1); } + public DrawDiscardControllerEffect(boolean optional) { + this(1, 1, optional); + } + public DrawDiscardControllerEffect(int cardsToDraw, int cardsToDiscard) { this(cardsToDraw, cardsToDiscard, false); } @@ -31,14 +34,6 @@ public class DrawDiscardControllerEffect extends OneShotEffect { this.cardsToDraw = cardsToDraw; this.cardsToDiscard = cardsToDiscard; this.optional = optional; - staticText = new StringBuilder(optional ? "you may " : "") - .append("draw ") - .append(cardsToDraw == 1 ? "a" : CardUtil.numberToText(cardsToDraw)) - .append(" card").append(cardsToDraw == 1 ? "" : "s") - .append(optional ? ", if you do" : ", then") - .append(" discard ") - .append(cardsToDiscard == 1 ? "a" : CardUtil.numberToText(cardsToDiscard)) - .append(" card").append(cardsToDiscard == 1 ? "" : "s").toString(); } public DrawDiscardControllerEffect(final DrawDiscardControllerEffect effect) { @@ -56,14 +51,31 @@ public class DrawDiscardControllerEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - if (!optional || player.chooseUse(outcome, "Use draw, then discard effect?", source, game)) { - player.drawCards(cardsToDraw, source, game); - player.discard(cardsToDiscard, false, false, source, game); - } + if (player == null) { + return false; + } + if (optional && !player.chooseUse(outcome, "Draw, then discard?", source, game)) { return true; } - return false; + player.drawCards(cardsToDraw, source, game); + player.discard(cardsToDiscard, false, false, source, game); + return true; } + @Override + public String getText(Mode mode) { + if (staticText != null && !staticText.isEmpty()) { + return staticText; + } + return (optional ? "you may " : "") + + "draw " + + (cardsToDraw == 1 ? "a" : CardUtil.numberToText(cardsToDraw)) + + " card" + + (cardsToDraw == 1 ? "" : "s") + + (optional ? ". If you do," : ", then") + + " discard " + + (cardsToDiscard == 1 ? "a" : CardUtil.numberToText(cardsToDiscard)) + + " card" + + (cardsToDiscard == 1 ? "" : "s"); + } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileAllEffect.java index ca1e864d1dd..b6852c2c89c 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ExileAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ExileAllEffect.java @@ -1,6 +1,5 @@ package mage.abilities.effects.common; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.Cards; @@ -45,8 +44,7 @@ public class ExileAllEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = source.getSourceObject(game); - if (controller == null || sourceObject == null) { + if (controller == null) { return false; } Cards cards = new CardsImpl(); @@ -54,7 +52,7 @@ public class ExileAllEffect extends OneShotEffect { filter, source.getControllerId(), source.getSourceId(), game ).stream().forEach(cards::add); if (forSource) { - return controller.moveCardsToExile(cards.getCards(game), source, game, true, CardUtil.getExileZoneId(game, source), sourceObject.getName()); + return controller.moveCardsToExile(cards.getCards(game), source, game, true, CardUtil.getExileZoneId(game, source), CardUtil.getSourceName(game, source)); } return controller.moveCards(cards, Zone.EXILED, source, game); diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileAndReturnTransformedSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileAndReturnTransformedSourceEffect.java index 075e7910e04..dd6210a9521 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ExileAndReturnTransformedSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ExileAndReturnTransformedSourceEffect.java @@ -1,7 +1,8 @@ package mage.abilities.effects.common; import mage.abilities.Ability; -import mage.abilities.Gender; +import mage.abilities.Mode; +import mage.abilities.Pronoun; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; @@ -18,37 +19,33 @@ import mage.players.Player; */ public class ExileAndReturnTransformedSourceEffect extends OneShotEffect { - protected Effect additionalEffect; - protected boolean returnUnderYourControl; + protected final Pronoun pronoun; + protected final Effect additionalEffect; + protected final boolean returnUnderYourControl; - public ExileAndReturnTransformedSourceEffect() { - this(Gender.NEUTRAL); + public ExileAndReturnTransformedSourceEffect(Pronoun pronoun) { + this(pronoun, null); } - public ExileAndReturnTransformedSourceEffect(Gender gender) { - this(gender, null); - } - - public ExileAndReturnTransformedSourceEffect(Gender gender, Effect additionalEffect) { - this(gender, additionalEffect, false); + public ExileAndReturnTransformedSourceEffect(Pronoun pronoun, Effect additionalEffect) { + this(pronoun, additionalEffect, false); } /** - * @param gender + * @param pronoun * @param additionalEffect that effect is applies as source is exiled * @param returnUnderYourControl return under your or owner control */ - public ExileAndReturnTransformedSourceEffect(Gender gender, Effect additionalEffect, boolean returnUnderYourControl) { + public ExileAndReturnTransformedSourceEffect(Pronoun pronoun, Effect additionalEffect, boolean returnUnderYourControl) { super(Outcome.Benefit); + this.pronoun = pronoun; this.additionalEffect = additionalEffect; this.returnUnderYourControl = returnUnderYourControl; - this.staticText = "exile {this}, then return " + gender.getPersonalPronoun() - + " to the battlefield transformed under " + gender.getPossesivePronoun() - + " " + (this.returnUnderYourControl ? "your" : "owner's") + " control"; } - public ExileAndReturnTransformedSourceEffect(final ExileAndReturnTransformedSourceEffect effect) { + protected ExileAndReturnTransformedSourceEffect(final ExileAndReturnTransformedSourceEffect effect) { super(effect); + this.pronoun = effect.pronoun; this.additionalEffect = effect.additionalEffect; this.returnUnderYourControl = effect.returnUnderYourControl; } @@ -61,24 +58,38 @@ public class ExileAndReturnTransformedSourceEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { // Creature has to be on the battlefield to get exiled and be able to return transformed - Permanent sourceObject = game.getPermanent(source.getSourceId()); + Permanent sourceObject = source.getSourcePermanentIfItStillExists(game); Player controller = game.getPlayer(source.getControllerId()); - if (sourceObject != null && controller != null && sourceObject.getZoneChangeCounter(game) == source.getSourceObjectZoneChangeCounter()) { - if (controller.moveCards(sourceObject, Zone.EXILED, source, game)) { - game.getState().setValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + source.getSourceId(), Boolean.TRUE); - Card cardFromExile = game.getCard(source.getSourceId()); - if (cardFromExile != null) { - controller.moveCards(cardFromExile, Zone.BATTLEFIELD, source, game, false, false, !this.returnUnderYourControl, null); - if (additionalEffect != null) { - if (additionalEffect instanceof ContinuousEffect) { - game.addEffect((ContinuousEffect) additionalEffect, source); - } else { - additionalEffect.apply(game, source); - } - } - } - } + if (sourceObject == null || controller == null) { + return true; + } + if (!controller.moveCards(sourceObject, Zone.EXILED, source, game)) { + return true; + } + game.getState().setValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + source.getSourceId(), Boolean.TRUE); + Card cardFromExile = game.getCard(source.getSourceId()); + if (cardFromExile == null) { + return true; + } + controller.moveCards(cardFromExile, Zone.BATTLEFIELD, source, game, false, false, !this.returnUnderYourControl, null); + if (additionalEffect == null) { + return true; + } + if (additionalEffect instanceof ContinuousEffect) { + game.addEffect((ContinuousEffect) additionalEffect, source); + } else { + additionalEffect.apply(game, source); } return true; } + + @Override + public String getText(Mode mode) { + if (staticText != null && !staticText.isEmpty()) { + return staticText; + } + return "exile {this}, then return " + pronoun.getObjective() + + " to the battlefield transformed under " + pronoun.getPossessive() + + ' ' + (this.returnUnderYourControl ? "your" : "owner's") + " control"; + } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileFromZoneTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileFromZoneTargetEffect.java index ef354c75f44..7007493a089 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ExileFromZoneTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ExileFromZoneTargetEffect.java @@ -1,7 +1,6 @@ package mage.abilities.effects.common; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.effects.OneShotEffect; @@ -55,7 +54,6 @@ public class ExileFromZoneTargetEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(targetPointer.getFirst(game, source)); - MageObject mageObject = source.getSourceObject(game); if (player == null) { return false; } @@ -75,7 +73,7 @@ public class ExileFromZoneTargetEffect extends OneShotEffect { target.chooseTarget(Outcome.Exile, player.getId(), source, game); Cards cards = new CardsImpl(target.getTargets()); if (withSource) { - return player.moveCardsToExile(cards.getCards(game), source, game, true, CardUtil.getExileZoneId(game, source), mageObject.getName()); + return player.moveCardsToExile(cards.getCards(game), source, game, true, CardUtil.getExileZoneId(game, source), CardUtil.getSourceName(game, source)); } return player.moveCards(cards, Zone.EXILED, source, game); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileGraveyardAllPlayersEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileGraveyardAllPlayersEffect.java index 0af1d8339bc..58d185cbffb 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ExileGraveyardAllPlayersEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ExileGraveyardAllPlayersEffect.java @@ -1,7 +1,5 @@ - package mage.abilities.effects.common; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.Cards; @@ -14,8 +12,9 @@ import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; +import java.util.UUID; + /** - * * @author Jgod */ public class ExileGraveyardAllPlayersEffect extends OneShotEffect { @@ -35,7 +34,7 @@ public class ExileGraveyardAllPlayersEffect extends OneShotEffect { super(Outcome.Exile); this.filter = filter; this.targetController = targetController; - staticText = "exile all " + filter.getMessage() + " from all " + staticText = "exile all " + (filter.getMessage().equals("cards") ? "" : filter.getMessage() + " from all ") + (targetController.equals(TargetController.OPPONENT) ? "opponents' " : "") + "graveyards"; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileGraveyardAllTargetPlayerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileGraveyardAllTargetPlayerEffect.java index aa5e7e589b1..eefa9afc9cf 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ExileGraveyardAllTargetPlayerEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ExileGraveyardAllTargetPlayerEffect.java @@ -16,7 +16,7 @@ public class ExileGraveyardAllTargetPlayerEffect extends OneShotEffect { public ExileGraveyardAllTargetPlayerEffect() { super(Outcome.Exile); - staticText = "exile all cards from target player's graveyard"; + staticText = "exile target player's graveyard"; } private ExileGraveyardAllTargetPlayerEffect(final ExileGraveyardAllTargetPlayerEffect effect) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileSagaAndReturnTransformedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileSagaAndReturnTransformedEffect.java new file mode 100644 index 00000000000..e213bfc1c42 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/ExileSagaAndReturnTransformedEffect.java @@ -0,0 +1,23 @@ +package mage.abilities.effects.common; + +import mage.abilities.Pronoun; + +/** + * @author TheElk801 + */ +public class ExileSagaAndReturnTransformedEffect extends ExileAndReturnTransformedSourceEffect { + + public ExileSagaAndReturnTransformedEffect() { + super(Pronoun.IT, null, true); + staticText = "exile this Saga, then return it to the battlefield transformed under your control"; + } + + private ExileSagaAndReturnTransformedEffect(final ExileSagaAndReturnTransformedEffect effect) { + super(effect); + } + + @Override + public ExileSagaAndReturnTransformedEffect copy() { + return new ExileSagaAndReturnTransformedEffect(this); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileTopXMayPlayUntilEndOfTurnEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileTopXMayPlayUntilEndOfTurnEffect.java index 2f7bf62e9f9..14ceac54731 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ExileTopXMayPlayUntilEndOfTurnEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ExileTopXMayPlayUntilEndOfTurnEffect.java @@ -1,6 +1,5 @@ package mage.abilities.effects.common; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.effects.OneShotEffect; @@ -52,15 +51,14 @@ public class ExileTopXMayPlayUntilEndOfTurnEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = game.getObject(source.getSourceId()); - if (controller == null || sourceObject == null) { + if (controller == null) { return false; } Set cards = controller.getLibrary().getTopCards(game, amount); if (cards.isEmpty()) { return true; } - controller.moveCardsToExile(cards, source, game, true, source.getSourceId(), sourceObject.getIdName()); + controller.moveCardsToExile(cards, source, game, true, CardUtil.getExileZoneId(game, source), CardUtil.getSourceName(game, source)); // remove cards that could not be moved to exile cards.removeIf(card -> !Zone.EXILED.equals(game.getState().getZone(card.getId()))); if (!cards.isEmpty()) { @@ -75,18 +73,17 @@ public class ExileTopXMayPlayUntilEndOfTurnEffect extends OneShotEffect { if (staticText != null && !staticText.isEmpty()) { return staticText; } - StringBuilder sb = new StringBuilder(); + StringBuilder sb = new StringBuilder("exile the top "); if (amount == 1) { - sb.append("exile the top card of your library. "); - sb.append(CardUtil.getTextWithFirstCharUpperCase(duration.toString())); - sb.append(", you may play that card"); + sb.append("card of your library. "); } else { - sb.append("exile the top "); sb.append(CardUtil.numberToText(amount)); sb.append(" cards of your library. "); - sb.append(CardUtil.getTextWithFirstCharUpperCase(duration.toString())); - sb.append(", you may play cards exiled this way"); } + sb.append(CardUtil.getTextWithFirstCharUpperCase(duration.toString())); + sb.append(", you may play "); + sb.append(amount == 1 ? "that card" : amount == 2 ? "those cards" : "cards exiled this way"); + if (showHint) { sb.append(". (You still pay its costs. You can play a land this way only if you have an available land play remaining.)"); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/FightTargetSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/FightTargetSourceEffect.java index 045dd07ce4e..ad0858af482 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/FightTargetSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/FightTargetSourceEffect.java @@ -1,4 +1,3 @@ - package mage.abilities.effects.common; import mage.MageObject; @@ -8,9 +7,9 @@ import mage.abilities.effects.OneShotEffect; import mage.constants.Outcome; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.target.Target; /** - * * @author LevelX2 */ public class FightTargetSourceEffect extends OneShotEffect { @@ -52,7 +51,15 @@ public class FightTargetSourceEffect extends OneShotEffect { if (staticText != null && !staticText.isEmpty()) { return staticText; } - return new StringBuilder("{this} fights another target ").append(mode.getTargets().get(0).getTargetName()).toString(); + Target target = mode.getTargets().get(0); + StringBuilder sb = new StringBuilder("{this} fights "); + if (target.getMinNumberOfTargets() == 0 && target.getMaxNumberOfTargets() == 1) { + sb.append("up to one "); + } + if (!target.getTargetName().contains("other")) { + sb.append("target "); + } + sb.append(target.getTargetName()); + return sb.toString(); } - } diff --git a/Mage/src/main/java/mage/abilities/effects/common/FightTargetsEffect.java b/Mage/src/main/java/mage/abilities/effects/common/FightTargetsEffect.java index deaccfb06ba..51dd7545109 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/FightTargetsEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/FightTargetsEffect.java @@ -7,6 +7,7 @@ import mage.cards.Card; import mage.constants.Outcome; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.target.Target; import java.util.UUID; @@ -15,17 +16,20 @@ import java.util.UUID; */ public class FightTargetsEffect extends OneShotEffect { + protected boolean showEffectHint; + public FightTargetsEffect() { - super(Outcome.Damage); + this(true); } - public FightTargetsEffect(String effectText) { - this(); - this.staticText = effectText; + public FightTargetsEffect(boolean showEffectHint) { + super(Outcome.Benefit); + this.showEffectHint = showEffectHint; } public FightTargetsEffect(final FightTargetsEffect effect) { super(effect); + this.showEffectHint = effect.showEffectHint; } @Override @@ -70,18 +74,26 @@ public class FightTargetsEffect extends OneShotEffect { @Override public FightTargetsEffect copy() { return new FightTargetsEffect(this); - } @Override public String getText(Mode mode) { if (staticText != null && !staticText.isEmpty()) { return staticText; - } - return "target " + mode - .getTargets().get(0).getTargetName() + " fights another target " + mode - .getTargets().get(1).getTargetName(); - } + Target target=mode.getTargets().get(1); + StringBuilder sb=new StringBuilder("target "); + sb.append(mode.getTargets().get(0).getTargetName()); + sb.append(" fights "); + if(!target.getTargetName().contains("other")){ + sb.append("target "); + } + sb.append(target.getTargetName()); -} \ No newline at end of file + if (showEffectHint) { + sb.append(". (Each deals damage equal to its power to the other.)"); + } + + return sb.toString(); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/FortifyEffect.java b/Mage/src/main/java/mage/abilities/effects/common/FortifyEffect.java deleted file mode 100644 index f62836ecc9f..00000000000 --- a/Mage/src/main/java/mage/abilities/effects/common/FortifyEffect.java +++ /dev/null @@ -1,38 +0,0 @@ -package mage.abilities.effects.common; - -import mage.abilities.Ability; -import mage.constants.Outcome; -import mage.constants.SubType; -import mage.game.Game; -import mage.game.permanent.Permanent; - -public class FortifyEffect extends AttachEffect{ - - public FortifyEffect(Outcome outcome) { - super(outcome, "Fortify"); - } - - public FortifyEffect(FortifyEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent sourcePermanent = game.getPermanent(source.getSourceId()); - //Some artifacts have the subtype “Fortification.” A Fortification can be attached to a land. It can’t legally - // be attached to an object that isn’t a land. Fortification’s analog to the equip keyword ability is the - // fortify keyword ability. Rules 301.5a–e apply to Fortifications in relation to lands just as they apply to - // Equipment in relation to creatures, with one clarification relating to rule 301.5c: a Fortification that’s - // also a creature (not a land) can’t fortify a land. (See rule 702.66, “Fortify.”) - if (sourcePermanent != null && sourcePermanent.hasSubtype(SubType.FORTIFICATION, game) && !sourcePermanent.isCreature(game) - && !sourcePermanent.isLand(game)) { - return super.apply(game, source); - } - return false; - } - - @Override - public FortifyEffect copy(){ - return new FortifyEffect(this); - } -} diff --git a/Mage/src/main/java/mage/abilities/effects/common/GetEmblemEffect.java b/Mage/src/main/java/mage/abilities/effects/common/GetEmblemEffect.java index 527a1f1c901..907dfdf70ad 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/GetEmblemEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/GetEmblemEffect.java @@ -45,7 +45,7 @@ public class GetEmblemEffect extends OneShotEffect { public String getText() { StringBuilder sb = new StringBuilder(); - sb.append("You get an emblem with \""); + sb.append("you get an emblem with \""); List rules = emblem.getAbilities().getRules(null); if (rules.size() == 1) { for (String s : rules) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/IfAbilityHasResolvedXTimesEffect.java b/Mage/src/main/java/mage/abilities/effects/common/IfAbilityHasResolvedXTimesEffect.java index 21814f17ec3..b4dca380e03 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/IfAbilityHasResolvedXTimesEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/IfAbilityHasResolvedXTimesEffect.java @@ -38,15 +38,13 @@ public class IfAbilityHasResolvedXTimesEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - AbilityResolvedWatcher watcher = game.getState().getWatcher(AbilityResolvedWatcher.class); - if (watcher != null && watcher.getResolutionCount(game, source) == resolutionNumber) { - if (effect instanceof OneShotEffect) { - return effect.apply(game, source); - } else { - game.addEffect((ContinuousEffect) effect, source); - return true; - } + if (AbilityResolvedWatcher.getResolutionCount(game, source) != resolutionNumber) { + return true; } + if (effect instanceof OneShotEffect) { + return effect.apply(game, source); + } + game.addEffect((ContinuousEffect) effect, source); return true; } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/LookLibraryAndPickControllerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/LookLibraryAndPickControllerEffect.java index 3427d5c3e61..ddcb26dd4d3 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/LookLibraryAndPickControllerEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/LookLibraryAndPickControllerEffect.java @@ -290,11 +290,15 @@ public class LookLibraryAndPickControllerEffect extends LookLibraryControllerEff return staticText; } StringBuilder sb = new StringBuilder(); - if (numberToPick.calculate(null, null, this) > 0) { - + int pickCount=numberToPick.calculate(null, null, this); + int cardCount=numberOfCards.calculate(null, null, this); + if (pickCount > 0) { if (revealPickedCards) { sb.append(". You may reveal "); - sb.append(filter.getMessage()).append(" from among them and put it into your "); + sb.append(filter.getMessage()); + sb.append(" from among them and put "); + sb.append(pickCount>1?"the revealed cards":"it"); + sb.append(" into your "); } else if (targetPickedCards == Zone.BATTLEFIELD) { sb.append(". "); if (optional) { @@ -305,15 +309,15 @@ public class LookLibraryAndPickControllerEffect extends LookLibraryControllerEff sb.append("ut ").append(filter.getMessage()).append(" from among them onto the "); } else { sb.append(". Put "); - if (numberToPick.calculate(null, null, this) > 1) { + if (pickCount > 1) { if (upTo) { - if (numberToPick.calculate(null, null, this) == (numberOfCards.calculate(null, null, this))) { + if (pickCount == (cardCount)) { sb.append("any number"); } else { - sb.append("up to ").append(CardUtil.numberToText(numberToPick.calculate(null, null, this))); + sb.append("up to ").append(CardUtil.numberToText(pickCount)); } } else { - sb.append(CardUtil.numberToText(numberToPick.calculate(null, null, this))); + sb.append(CardUtil.numberToText(pickCount)); } } else { sb.append("one"); @@ -324,19 +328,23 @@ public class LookLibraryAndPickControllerEffect extends LookLibraryControllerEff sb.append(targetPickedCards.toString().toLowerCase(Locale.ENGLISH)); if (targetZoneLookedCards == Zone.LIBRARY) { - sb.append(". Put the rest "); + sb.append(revealPickedCards?". Put ":" and "); + sb.append(cardCount-pickCount==1?"the other ":"the rest "); if (putOnTop) { sb.append("back on top"); } else { sb.append("on the bottom"); } - sb.append(" of your library in "); - if (anyOrder && !backInRandomOrder) { - sb.append("any"); - } else { - sb.append("a random"); + sb.append(" of your library"); + if (cardCount-pickCount>1) { + sb.append(" in "); + if (anyOrder && !backInRandomOrder) { + sb.append("any"); + } else { + sb.append("a random"); + } + sb.append(" order"); } - sb.append(" order"); } else if (targetZoneLookedCards == Zone.GRAVEYARD) { sb.append(" and the"); if (numberOfCards instanceof StaticValue && numberToPick instanceof StaticValue diff --git a/Mage/src/main/java/mage/abilities/effects/common/MistmeadowWitchEffect.java b/Mage/src/main/java/mage/abilities/effects/common/MistmeadowWitchEffect.java index 66e98bc9b78..24bec4ae22e 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/MistmeadowWitchEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/MistmeadowWitchEffect.java @@ -1,6 +1,5 @@ package mage.abilities.effects.common; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.effects.OneShotEffect; @@ -29,11 +28,10 @@ public class MistmeadowWitchEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); Permanent permanent = game.getPermanent(source.getFirstTarget()); - MageObject sourceObject = source.getSourceObject(game); - if (player == null || permanent == null || sourceObject == null) { + if (player == null || permanent == null) { return false; } - player.moveCardsToExile(permanent, source, game, true, CardUtil.getExileZoneId(game, source), sourceObject.getName()); + player.moveCardsToExile(permanent, source, game, true, CardUtil.getExileZoneId(game, source), CardUtil.getSourceName(game, source)); game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new ReturnFromExileEffect(Zone.BATTLEFIELD, "return the exiled card to the battlefield under its owner's control")), source); return true; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/PermanentsEnterBattlefieldTappedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PermanentsEnterBattlefieldTappedEffect.java index 65e815fdd31..dd46edbfbe1 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/PermanentsEnterBattlefieldTappedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/PermanentsEnterBattlefieldTappedEffect.java @@ -1,6 +1,7 @@ package mage.abilities.effects.common; import mage.abilities.Ability; +import mage.abilities.Mode; import mage.abilities.effects.ReplacementEffectImpl; import mage.constants.Duration; import mage.constants.Outcome; @@ -11,38 +12,32 @@ import mage.game.events.GameEvent; import mage.game.permanent.Permanent; /** - * * @author Eirkei */ public class PermanentsEnterBattlefieldTappedEffect extends ReplacementEffectImpl { - protected FilterPermanent filter; - - public PermanentsEnterBattlefieldTappedEffect() { - this(new FilterPermanent()); + + protected final FilterPermanent filter; + + public PermanentsEnterBattlefieldTappedEffect(FilterPermanent filter) { + this(filter, Duration.WhileOnBattlefield); } - - public PermanentsEnterBattlefieldTappedEffect(FilterPermanent filter) { - super(Duration.WhileOnBattlefield, Outcome.Tap); + + public PermanentsEnterBattlefieldTappedEffect(FilterPermanent filter, Duration duration) { + super(duration, Outcome.Tap, false); this.filter = filter; - this.setText(); } public PermanentsEnterBattlefieldTappedEffect(final PermanentsEnterBattlefieldTappedEffect effect) { super(effect); - - if (effect.filter != null){ - this.filter = effect.filter.copy(); - } + this.filter = effect.filter; } @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { Permanent target = ((EntersTheBattlefieldEvent) event).getTarget(); - if (target != null) { target.tap(source, game); } - return false; } @@ -54,19 +49,21 @@ public class PermanentsEnterBattlefieldTappedEffect extends ReplacementEffectImp @Override public boolean applies(GameEvent event, Ability source, Game game) { Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget(); - - return filter.match(permanent, source.getSourceId(), event.getPlayerId(), game); + return filter.match(permanent, source.getSourceId(), source.getControllerId(), game); } @Override public PermanentsEnterBattlefieldTappedEffect copy() { return new PermanentsEnterBattlefieldTappedEffect(this); } - - private void setText() { - StringBuilder sb = new StringBuilder(); - sb.append(filter.getMessage()); - sb.append(" enter the battlefield tapped"); - staticText = sb.toString(); + + @Override + public String getText(Mode mode) { + if (staticText != null && !staticText.isEmpty()) { + return staticText; + } + return filter.getMessage() + + " enter the battlefield tapped" + + (duration == Duration.EndOfTurn ? " this turn" : ""); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/PhaseOutTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PhaseOutTargetEffect.java index 7d062fd4570..5874c34a81c 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/PhaseOutTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/PhaseOutTargetEffect.java @@ -64,7 +64,7 @@ public class PhaseOutTargetEffect extends OneShotEffect { @Override public String getText(Mode mode) { if (staticText != null && !staticText.isEmpty()) { - return staticText + " phases out"; + return staticText; } StringBuilder sb = new StringBuilder(); diff --git a/Mage/src/main/java/mage/abilities/effects/common/PopulateEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PopulateEffect.java index bd47815c0ed..26da099df0b 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/PopulateEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/PopulateEffect.java @@ -78,7 +78,7 @@ public class PopulateEffect extends OneShotEffect { Effect effect = new CreateTokenCopyTargetEffect( null, null, false, 1, tappedAndAttacking, tappedAndAttacking ); - effect.setTargetPointer(new FixedTarget(target.getFirstTarget())); + effect.setTargetPointer(new FixedTarget(target.getFirstTarget(), game)); return effect.apply(game, source); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/PreventAllDamageToPlayersEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PreventAllDamageToPlayersEffect.java index 9817e4e3754..56fe441c809 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/PreventAllDamageToPlayersEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/PreventAllDamageToPlayersEffect.java @@ -51,7 +51,7 @@ public class PreventAllDamageToPlayersEffect extends PreventionEffectImpl { } sb.append("damage that would be dealt to players"); if (duration == Duration.EndOfTurn) { - sb.append(" this turn"); + sb.append(" this turn"); } return sb.toString(); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/PreventDamageToAttachedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PreventDamageToAttachedEffect.java index d696229bc96..b7e5f8503b5 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/PreventDamageToAttachedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/PreventDamageToAttachedEffect.java @@ -69,7 +69,7 @@ public class PreventDamageToAttachedEffect extends PreventionEffectImpl { } sb.append("damage to "); sb.append(attachmentType.verb()); - sb.append(" creature, prevent ").append(amountToPrevent);; + sb.append(" creature, prevent ").append(amountToPrevent); sb.append(" of that damage"); } return sb.toString(); diff --git a/Mage/src/main/java/mage/abilities/effects/common/RevealCardsFromLibraryUntilEffect.java b/Mage/src/main/java/mage/abilities/effects/common/RevealCardsFromLibraryUntilEffect.java index dddb343176c..65043712988 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/RevealCardsFromLibraryUntilEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/RevealCardsFromLibraryUntilEffect.java @@ -1,8 +1,8 @@ - package mage.abilities.effects.common; import mage.MageObject; import mage.abilities.Ability; +import mage.abilities.Mode; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; import mage.cards.Cards; @@ -13,18 +13,18 @@ import mage.filter.FilterCard; import mage.game.Game; import mage.players.Library; import mage.players.Player; +import mage.util.CardUtil; /** - * * @author Styxo */ public class RevealCardsFromLibraryUntilEffect extends OneShotEffect { - private FilterCard filter; - private Zone zoneToPutRest; - private Zone zoneToPutCard; - private boolean shuffleRestInto; - private boolean anyOrder; + private final FilterCard filter; + private final Zone zoneToPutRest; + private final Zone zoneToPutCard; + private final boolean shuffleRestInto; + private final boolean anyOrder; public RevealCardsFromLibraryUntilEffect(FilterCard filter, Zone zoneToPutCard, Zone zoneToPutRest) { this(filter, zoneToPutCard, zoneToPutRest, false, false); @@ -41,17 +41,15 @@ public class RevealCardsFromLibraryUntilEffect extends OneShotEffect { this.zoneToPutRest = zoneToPutRest; this.shuffleRestInto = shuffleRestInto; this.anyOrder = anyOrder; - setText(); } - public RevealCardsFromLibraryUntilEffect(final RevealCardsFromLibraryUntilEffect effect) { + private RevealCardsFromLibraryUntilEffect(final RevealCardsFromLibraryUntilEffect effect) { super(effect); this.filter = effect.filter; this.zoneToPutCard = effect.zoneToPutCard; this.zoneToPutRest = effect.zoneToPutRest; this.shuffleRestInto = effect.shuffleRestInto; this.anyOrder = effect.anyOrder; - setText(); } @Override @@ -63,50 +61,54 @@ public class RevealCardsFromLibraryUntilEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); MageObject sourceObject = game.getObject(source.getSourceId()); - if (controller != null && controller.getLibrary().hasCards()) { - Cards cards = new CardsImpl(); - Library library = controller.getLibrary(); - Card card = null; - do { - card = library.removeFromTop(game); - if (card != null) { - cards.add(card); - } - } while (library.hasCards() && !filter.match(card, game)); - // reveal cards - if (!cards.isEmpty()) { - controller.revealCards(sourceObject.getIdName(), cards, game); - if (filter.match(card, game)) { - // put card in correct zone - controller.moveCards(card, zoneToPutCard, source, game); - // remove it from revealed card list - cards.remove(card); - } - // Put the rest in correct zone - switch (zoneToPutRest) { - case LIBRARY: { - if (!cards.isEmpty()) { - if (shuffleRestInto) { - library.addAll(cards.getCards(game), game); - } else { - controller.putCardsOnBottomOfLibrary(cards, game, source, anyOrder); - } - } - break; - } - default: - if (!cards.isEmpty()) { - controller.moveCards(cards, zoneToPutRest, source, game); - } - } + if (controller == null || !controller.getLibrary().hasCards()) { + return false; + } + Cards cards = new CardsImpl(); + Library library = controller.getLibrary(); + Card card = null; + do { + card = library.removeFromTop(game); + if (card != null) { + cards.add(card); } + } while (library.hasCards() && !filter.match(card, game)); + // reveal cards + if (cards.isEmpty()) { return true; } - return false; + controller.revealCards(sourceObject.getIdName(), cards, game); + if (filter.match(card, game)) { + // put card in correct zone + controller.moveCards(card, zoneToPutCard, source, game); + // remove it from revealed card list + cards.remove(card); + } + // Put the rest in correct zone + if (zoneToPutRest == Zone.LIBRARY) { + if (!cards.isEmpty()) { + if (shuffleRestInto) { + library.addAll(cards.getCards(game), game); + } else { + controller.putCardsOnBottomOfLibrary(cards, game, source, anyOrder); + } + } + } else { + if (!cards.isEmpty()) { + controller.moveCards(cards, zoneToPutRest, source, game); + } + } + return true; } - private void setText() { - StringBuilder sb = new StringBuilder("reveal cards from the top of your library until you reveal a " + filter.getMessage() + ". Put that card "); + @Override + public String getText(Mode mode) { + if (staticText != null && !staticText.isEmpty()) { + return staticText; + } + StringBuilder sb = new StringBuilder("reveal cards from the top of your library until you reveal "); + sb.append(CardUtil.addArticle(filter.getMessage())); + sb.append(". Put that card "); switch (zoneToPutCard) { case HAND: { @@ -144,6 +146,6 @@ public class RevealCardsFromLibraryUntilEffect extends OneShotEffect { break; } } - staticText = sb.toString(); + return sb.toString(); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/RollDieWithResultTableEffect.java b/Mage/src/main/java/mage/abilities/effects/common/RollDieWithResultTableEffect.java index b198f7daf85..1e65c1f8d08 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/RollDieWithResultTableEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/RollDieWithResultTableEffect.java @@ -27,6 +27,7 @@ public class RollDieWithResultTableEffect extends OneShotEffect { private final String prefixText; private final List resultsTable = new ArrayList<>(); private final DynamicValue modifier; + private final int toIgnore; public RollDieWithResultTableEffect() { this(20); @@ -37,14 +38,15 @@ public class RollDieWithResultTableEffect extends OneShotEffect { } public RollDieWithResultTableEffect(int sides, String prefixText) { - this(sides, prefixText, StaticValue.get(0)); + this(sides, prefixText, StaticValue.get(0), 0); } - public RollDieWithResultTableEffect(int sides, String prefixText, DynamicValue modifier) { + public RollDieWithResultTableEffect(int sides, String prefixText, DynamicValue modifier, int toIgnore) { super(Outcome.Benefit); this.sides = sides; this.prefixText = prefixText; this.modifier = modifier; + this.toIgnore = toIgnore; } protected RollDieWithResultTableEffect(final RollDieWithResultTableEffect effect) { @@ -55,6 +57,7 @@ public class RollDieWithResultTableEffect extends OneShotEffect { this.resultsTable.add(tableEntry.copy()); } this.modifier = effect.modifier.copy(); + this.toIgnore = effect.toIgnore; } @Override @@ -68,7 +71,9 @@ public class RollDieWithResultTableEffect extends OneShotEffect { if (player == null) { return false; } - int result = player.rollDice(outcome, source, game, sides) + modifier.calculate(game, source, this); + int result = player.rollDice( + outcome, source, game, sides, 1 + toIgnore, toIgnore + ).get(0) + modifier.calculate(game, source, this); this.applyResult(result, game, source); return true; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/SacrificeEffect.java b/Mage/src/main/java/mage/abilities/effects/common/SacrificeEffect.java index 9115e6ba862..5d210b7086b 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/SacrificeEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/SacrificeEffect.java @@ -98,12 +98,12 @@ public class SacrificeEffect extends OneShotEffect { sb.append(" sacrifice "); } } - if (!filter.getMessage().startsWith("another") - && !filter.getMessage().startsWith("a ") - && !filter.getMessage().startsWith("an ")) { - sb.append(CardUtil.numberToText(count.toString(), "a")).append(' '); + if (count.toString().equals("1")) { + sb.append(CardUtil.addArticle(filter.getMessage())); + } else { + sb.append(CardUtil.numberToText(count.toString(), "a")); + sb.append(filter.getMessage()); } - sb.append(filter.getMessage()); staticText = sb.toString(); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/SacrificeOpponentsUnlessPayEffect.java b/Mage/src/main/java/mage/abilities/effects/common/SacrificeOpponentsUnlessPayEffect.java index f7d199659af..9eea1cc657b 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/SacrificeOpponentsUnlessPayEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/SacrificeOpponentsUnlessPayEffect.java @@ -1,6 +1,7 @@ package mage.abilities.effects.common; import mage.abilities.Ability; +import mage.abilities.Mode; import mage.abilities.costs.Cost; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.costs.mana.ManaCost; @@ -60,7 +61,6 @@ public class SacrificeOpponentsUnlessPayEffect extends OneShotEffect { this.cost = cost; this.amount = amount; this.filter = filter; - setText(); } public SacrificeOpponentsUnlessPayEffect(DynamicValue genericMana, FilterPermanent filter, DynamicValue amount) { @@ -68,7 +68,6 @@ public class SacrificeOpponentsUnlessPayEffect extends OneShotEffect { this.genericMana = genericMana; this.amount = amount; this.filter = filter; - setText(); } public SacrificeOpponentsUnlessPayEffect(final SacrificeOpponentsUnlessPayEffect effect) { @@ -150,31 +149,35 @@ public class SacrificeOpponentsUnlessPayEffect extends OneShotEffect { return true; } - private void setText() { + @Override + public String getText(Mode mode) { + if (staticText != null && !staticText.isEmpty()) { + return staticText; + } StringBuilder sb = new StringBuilder(); sb.append("each opponent sacrifices "); - if (amount.toString().equals("X")) { - sb.append(amount.toString()); - } else { - if (amount.toString().equals("1")) { - if (!filter.getMessage().startsWith("a ") && !filter.getMessage().startsWith("an ")) { - sb.append('a'); - } - } else { + switch (amount.toString()) { + case "1": + sb.append(CardUtil.addArticle(filter.getMessage())); + break; + case "X": + sb.append("X "); + sb.append(filter.getMessage()); + break; + default: sb.append(CardUtil.numberToText(amount.toString())); - } + sb.append(' '); + sb.append(filter.getMessage()); } - sb.append(' '); - sb.append(filter.getMessage()); - sb.append(" unless they pay "); + sb.append(" unless they "); if (cost != null) { - sb.append(cost.getText()); + sb.append(CardUtil.addCostVerb(cost.getText())); } else { - sb.append("{X}"); + sb.append("pay {X}"); } if (genericMana != null && !genericMana.getMessage().isEmpty()) { @@ -182,6 +185,6 @@ public class SacrificeOpponentsUnlessPayEffect extends OneShotEffect { sb.append(genericMana.getMessage()); } - staticText = sb.toString(); + return sb.toString(); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/SacrificeSourceUnlessPaysEffect.java b/Mage/src/main/java/mage/abilities/effects/common/SacrificeSourceUnlessPaysEffect.java index 488c627d097..8b62985126e 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/SacrificeSourceUnlessPaysEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/SacrificeSourceUnlessPaysEffect.java @@ -14,8 +14,6 @@ import mage.players.Player; import mage.util.CardUtil; import mage.util.ManaUtil; -import java.util.Locale; - /** * Created by IntelliJ IDEA. User: Loki Date: 21.12.10 Time: 9:21 */ @@ -92,17 +90,6 @@ public class SacrificeSourceUnlessPaysEffect extends OneShotEffect { if (staticText != null && !staticText.isEmpty()) { return staticText; } - - StringBuilder sb = new StringBuilder("sacrifice {this} unless you "); - String costText = cost != null ? cost.getText() : "{X}"; - - if (CardUtil.checkCostWords(costText)) { - sb.append(costText.substring(0, 1).toLowerCase(Locale.ENGLISH)); - sb.append(costText.substring(1)); - } else { - sb.append("pay ").append(costText); - } - - return sb.toString(); + return "sacrifice {this} unless you " + CardUtil.addCostVerb(cost != null ? cost.getText() : "{X}"); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/TapTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/TapTargetEffect.java index 9afaa42dd2a..5173c65fcfe 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/TapTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/TapTargetEffect.java @@ -58,11 +58,11 @@ public class TapTargetEffect extends OneShotEffect { } Target target = mode.getTargets().get(0); - if (target.getMaxNumberOfTargets() > 1) { + if (target.getMaxNumberOfTargets() > 1 || target.getNumberOfTargets() == 0) { if (target.getMaxNumberOfTargets() == target.getNumberOfTargets()) { return "tap " + CardUtil.numberToText(target.getNumberOfTargets()) + " target " + target.getTargetName() + 's'; } else { - return "tap up to " + CardUtil.numberToText(target.getMaxNumberOfTargets()) + " target " + target.getTargetName() + 's'; + return "tap up to " + CardUtil.numberToText(target.getMaxNumberOfTargets()) + " target " + target.getTargetName() + (target.getMaxNumberOfTargets() > 1 ? "s" : ""); } } else if (target.getMaxNumberOfTargets() == 0) { return "tap X target " + mode.getTargets().get(0).getTargetName(); diff --git a/Mage/src/main/java/mage/abilities/effects/common/TransformSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/TransformSourceEffect.java index 13e8b95d1f3..329cab439e9 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/TransformSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/TransformSourceEffect.java @@ -1,43 +1,23 @@ - package mage.abilities.effects.common; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.constants.Outcome; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.game.permanent.PermanentCard; /** - * * @author nantuko */ public class TransformSourceEffect extends OneShotEffect { - private boolean withoutTrigger; - private boolean fromDayToNight; - - /** - * @param fromDayToNight Defines whether we transform from "day" side to - * "night" or vice versa. - */ - public TransformSourceEffect(boolean fromDayToNight) { - this(fromDayToNight, false); - } - - public TransformSourceEffect(boolean fromDayToNight, boolean withoutTrigger) { + public TransformSourceEffect() { super(Outcome.Transform); - this.withoutTrigger = withoutTrigger; - this.fromDayToNight = fromDayToNight; staticText = "transform {this}"; } public TransformSourceEffect(final TransformSourceEffect effect) { super(effect); - this.withoutTrigger = effect.withoutTrigger; - this.fromDayToNight = effect.fromDayToNight; } @Override @@ -47,37 +27,8 @@ public class TransformSourceEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - MageObject sourceObject = source.getSourceObjectIfItStillExists(game); // Transform only if it's the same object as the effect was put on the stack - if (sourceObject instanceof Permanent) { - Permanent sourcePermanent = (Permanent) sourceObject; - if (sourcePermanent.canTransform(source, game)) { - // check not to transform twice the same side - if (sourcePermanent.isTransformed() != fromDayToNight) { - if (withoutTrigger) { - sourcePermanent.setTransformed(fromDayToNight); - } else { - if (sourcePermanent.isTransformed()) { - Card orgCard = game.getCard(source.getSourceId()); - sourcePermanent.getPower().modifyBaseValue(orgCard.getPower().getValue()); - sourcePermanent.getToughness().modifyBaseValue(orgCard.getToughness().getValue()); - } - sourcePermanent.transform(game); - } - if (!game.isSimulation()) { - if (fromDayToNight) { - if (sourcePermanent.getSecondCardFace() != null) { - if (sourcePermanent instanceof PermanentCard) { - game.informPlayers(((PermanentCard) sourcePermanent).getCard().getLogName() + " transforms into " + sourcePermanent.getSecondCardFace().getLogName()); - } - } - } else { - game.informPlayers(sourcePermanent.getSecondCardFace().getLogName() + " transforms into " + sourcePermanent.getLogName()); - } - } - } - } - } - return true; + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + // check not to transform twice the same side + return permanent != null && permanent.transform(source, game); } - } diff --git a/Mage/src/main/java/mage/abilities/effects/common/TransformTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/TransformTargetEffect.java deleted file mode 100644 index b128ae01081..00000000000 --- a/Mage/src/main/java/mage/abilities/effects/common/TransformTargetEffect.java +++ /dev/null @@ -1,88 +0,0 @@ -package mage.abilities.effects.common; - -import mage.abilities.Ability; -import mage.abilities.Mode; -import mage.abilities.effects.OneShotEffect; -import mage.constants.Outcome; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.game.permanent.PermanentCard; -import mage.target.Target; -import mage.util.CardUtil; - -/** - * - * @author LevelX2 - */ -public class TransformTargetEffect extends OneShotEffect { - - private boolean withoutTrigger; - - public TransformTargetEffect() { - this(true); - } - - public TransformTargetEffect(boolean withoutTrigger) { - super(Outcome.Transform); - this.withoutTrigger = withoutTrigger; - } - - public TransformTargetEffect(final TransformTargetEffect effect) { - super(effect); - this.withoutTrigger = effect.withoutTrigger; - } - - @Override - public TransformTargetEffect copy() { - return new TransformTargetEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (permanent != null) { - if (permanent.canTransform(source, game)) { - // check not to transform twice the same side - if (withoutTrigger) { - permanent.setTransformed(!permanent.isTransformed()); - } else { - permanent.transform(game); - } - if (!game.isSimulation()) { - if (permanent.isTransformed()) { - if (permanent.getSecondCardFace() != null) { - if (permanent instanceof PermanentCard) { - game.informPlayers(((PermanentCard) permanent).getCard().getLogName() + " transforms into " + permanent.getSecondCardFace().getLogName()); - } - } - } else { - game.informPlayers(permanent.getSecondCardFace().getLogName() + " transforms into " + permanent.getLogName()); - } - } - } - - return true; - } - return false; - } - - @Override - public String getText(Mode mode) { - if (staticText != null && !staticText.isEmpty()) { - return staticText; - } - if (mode.getTargets().isEmpty()) { - return "transform target"; - } - Target target = mode.getTargets().get(0); - if (target.getMaxNumberOfTargets() > 1) { - if (target.getMaxNumberOfTargets() == target.getNumberOfTargets()) { - return "transform " + CardUtil.numberToText(target.getNumberOfTargets()) + " target " + target.getTargetName(); - } else { - return "transform up to " + CardUtil.numberToText(target.getMaxNumberOfTargets()) + " target " + target.getTargetName(); - } - } else { - return "transform target " + mode.getTargets().get(0).getTargetName(); - } - } -} diff --git a/Mage/src/main/java/mage/abilities/effects/common/UnlessPaysDelayedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/UnlessPaysDelayedEffect.java index 9d7e6037440..c17a7038b57 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/UnlessPaysDelayedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/UnlessPaysDelayedEffect.java @@ -1,4 +1,3 @@ - package mage.abilities.effects.common; import java.util.UUID; @@ -13,7 +12,6 @@ import mage.constants.Outcome; import mage.constants.PhaseStep; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.target.targetpointer.FixedTarget; /** diff --git a/Mage/src/main/java/mage/abilities/effects/common/UntapLandsEffect.java b/Mage/src/main/java/mage/abilities/effects/common/UntapLandsEffect.java index f920dc06a9e..c518b69802e 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/UntapLandsEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/UntapLandsEffect.java @@ -4,6 +4,7 @@ import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.constants.Outcome; +import mage.constants.TargetController; import mage.filter.common.FilterLandPermanent; import mage.filter.predicate.permanent.TappedPredicate; import mage.game.Game; @@ -17,11 +18,15 @@ import mage.util.CardUtil; */ public class UntapLandsEffect extends OneShotEffect { - private static final FilterLandPermanent filter = new FilterLandPermanent("land(s) to untap"); + private static final FilterLandPermanent filterAll = new FilterLandPermanent("land(s) to untap"); + private static final FilterLandPermanent filterControlled = new FilterLandPermanent("land(s) to untap"); static { - filter.add(TappedPredicate.TAPPED); + filterAll.add(TappedPredicate.TAPPED); + filterControlled.add(TappedPredicate.TAPPED); + filterControlled.add(TargetController.YOU.getControllerPredicate()); } + private final FilterLandPermanent filter; private final int amount; private final boolean upTo; @@ -30,16 +35,22 @@ public class UntapLandsEffect extends OneShotEffect { } public UntapLandsEffect(int amount, boolean upTo) { + this(amount, upTo, false); + } + + public UntapLandsEffect(int amount, boolean upTo, boolean onlyControlled) { super(Outcome.Untap); this.amount = amount; this.upTo = upTo; - staticText = "untap " + (upTo ? "up to " : "") + CardUtil.numberToText(amount, staticText) + " lands"; + this.filter = onlyControlled ? filterControlled : filterAll; + staticText = "untap " + (upTo ? "up to " : "") + CardUtil.numberToText(amount, staticText) + " lands" + (onlyControlled ? " you control" : ""); } public UntapLandsEffect(final UntapLandsEffect effect) { super(effect); this.amount = effect.amount; this.upTo = effect.upTo; + this.filter = effect.filter; } @Override diff --git a/Mage/src/main/java/mage/abilities/effects/common/WishEffect.java b/Mage/src/main/java/mage/abilities/effects/common/WishEffect.java index e40c35439c1..7c6e46de9b1 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/WishEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/WishEffect.java @@ -9,9 +9,11 @@ import mage.cards.CardsImpl; import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; import mage.target.TargetCard; +import mage.util.CardUtil; import java.util.List; import java.util.Set; @@ -24,35 +26,41 @@ public class WishEffect extends OneShotEffect { private final FilterCard filter; private final boolean reveal; private final boolean alsoFromExile; - private final String choiceText; private final boolean topOfLibrary; + private final String choiceText; + + public WishEffect() { + this(false); + } + + public WishEffect(boolean topOfLibrary) { + super(Outcome.DrawCard); + this.filter = StaticFilters.FILTER_CARD; + this.reveal = false; + this.alsoFromExile = false; + this.topOfLibrary = topOfLibrary; + choiceText = "Put a card you own from outside the game " + + (topOfLibrary ? "on top of your library?" : "into your hand?"); + staticText = "You may " + Character.toLowerCase(choiceText.charAt(0)) + choiceText.substring(1, choiceText.length() - 1); + } public WishEffect(FilterCard filter) { - this(filter, true); + this(filter, false); } - public WishEffect(FilterCard filter, boolean reveal) { - this(filter, reveal, false); + public WishEffect(FilterCard filter, boolean alsoFromExile) { + this(filter, alsoFromExile, false); } - public WishEffect(FilterCard filter, boolean reveal, boolean alsoFromExile) { - this(filter, reveal, alsoFromExile, false); - } - - public WishEffect(FilterCard filter, boolean reveal, boolean alsoFromExile, boolean topOfLibrary) { + public WishEffect(FilterCard filter, boolean alsoFromExile, boolean topOfLibrary) { super(Outcome.DrawCard); this.filter = filter; + this.reveal = true; this.alsoFromExile = alsoFromExile; - this.reveal = reveal; this.topOfLibrary = topOfLibrary; - if (!reveal) { - choiceText = "Put a card you own from outside the game " - + (topOfLibrary ? "on top of your library." : "into your hand."); - } else { - choiceText = (topOfLibrary ? "Put " : "Reveal ") + filter.getMessage() + " you own from outside the game" - + (alsoFromExile ? " or choose " + makeExileText(filter) - + " you own in exile. Put that card into your hand." : " and put it into your hand."); - } + choiceText = "Reveal " + CardUtil.addArticle(filter.getMessage()) + " you own from outside the game " + + (alsoFromExile ? "or choose " + makeExileText(filter) + " you own in exile. Put that card" : "and put it") + + (topOfLibrary ? " on top of your library?" : " into your hand?"); staticText = "You may " + Character.toLowerCase(choiceText.charAt(0)) + choiceText.substring(1, choiceText.length() - 1); } @@ -69,10 +77,10 @@ public class WishEffect extends OneShotEffect { public WishEffect(final WishEffect effect) { super(effect); this.filter = effect.filter; - this.alsoFromExile = effect.alsoFromExile; this.reveal = effect.reveal; - this.choiceText = effect.choiceText; + this.alsoFromExile = effect.alsoFromExile; this.topOfLibrary = effect.topOfLibrary; + this.choiceText = effect.choiceText; } @Override diff --git a/Mage/src/main/java/mage/abilities/effects/common/asthought/CanPlayCardControllerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/asthought/CanPlayCardControllerEffect.java index 77c269c29a6..4826c1d419b 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/asthought/CanPlayCardControllerEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/asthought/CanPlayCardControllerEffect.java @@ -23,23 +23,26 @@ import java.util.UUID; */ public class CanPlayCardControllerEffect extends AsThoughEffectImpl { - private final MageObjectReference mor; - private final Condition condition; + protected final MageObjectReference mor; + protected final UUID playerId; + protected final Condition condition; public CanPlayCardControllerEffect(Game game, UUID cardId, int cardZCC, Duration duration) { - this(game, cardId, cardZCC, duration, null); + this(game, cardId, cardZCC, duration, null, null); } - public CanPlayCardControllerEffect(Game game, UUID cardId, int cardZCC, Duration duration, Condition condition) { + public CanPlayCardControllerEffect(Game game, UUID cardId, int cardZCC, Duration duration, UUID playerId, Condition condition) { super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, duration, Outcome.Benefit); this.staticText = "You may play those card"; this.mor = new MageObjectReference(cardId, cardZCC, game); + this.playerId = playerId; this.condition = condition; } public CanPlayCardControllerEffect(final CanPlayCardControllerEffect effect) { super(effect); this.mor = effect.mor; + this.playerId = effect.playerId; this.condition = effect.condition; } @@ -65,6 +68,7 @@ public class CanPlayCardControllerEffect extends AsThoughEffectImpl { } UUID objectIdToCast = CardUtil.getMainCardId(game, sourceId); // affected to all card's parts - return mor.refersTo(objectIdToCast, game) && source.isControlledBy(affectedControllerId); + return mor.refersTo(objectIdToCast, game) + && (playerId == null ? source.isControlledBy(affectedControllerId) : playerId.equals(affectedControllerId)); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/asthought/YouMaySpendManaAsAnyColorToCastTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/asthought/YouMaySpendManaAsAnyColorToCastTargetEffect.java index 69aef22e597..f4b06f3a367 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/asthought/YouMaySpendManaAsAnyColorToCastTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/asthought/YouMaySpendManaAsAnyColorToCastTargetEffect.java @@ -15,27 +15,29 @@ import java.util.UUID; /** * Spend mana as any color to cast targeted card. Will not affected after any card movements or blinks. - * * Affects to all card's parts * * @author JayDi85 */ public class YouMaySpendManaAsAnyColorToCastTargetEffect extends AsThoughEffectImpl implements AsThoughManaEffect { + private final UUID playerId; private final Condition condition; public YouMaySpendManaAsAnyColorToCastTargetEffect(Duration duration) { - this(duration, null); + this(duration, null, null); } - public YouMaySpendManaAsAnyColorToCastTargetEffect(Duration duration, Condition condition) { + public YouMaySpendManaAsAnyColorToCastTargetEffect(Duration duration, UUID playerId, Condition condition) { super(AsThoughEffectType.SPEND_OTHER_MANA, duration, Outcome.Benefit); this.staticText = "You may spend mana as though it were mana of any color to cast it"; + this.playerId = playerId; this.condition = condition; } public YouMaySpendManaAsAnyColorToCastTargetEffect(final YouMaySpendManaAsAnyColorToCastTargetEffect effect) { super(effect); + this.playerId = effect.playerId; this.condition = effect.condition; } @@ -58,7 +60,7 @@ public class YouMaySpendManaAsAnyColorToCastTargetEffect extends AsThoughEffectI objectId = CardUtil.getMainCardId(game, objectId); // for split cards FixedTarget fixedTarget = ((FixedTarget) getTargetPointer()); UUID targetId = CardUtil.getMainCardId(game, fixedTarget.getTarget()); // Affects to all card's parts (example: Hostage Taker exile mdf card) - return source.isControlledBy(affectedControllerId) + return (playerId == null ? source.isControlledBy(affectedControllerId) : playerId.equals(affectedControllerId)) && Objects.equals(objectId, targetId) && game.getState().getZoneChangeCounter(objectId) <= fixedTarget.getZoneChangeCounter() + 1 && (game.getState().getZone(objectId) == Zone.STACK || game.getState().getZone(objectId) == Zone.EXILED); diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/CanAttackAsThoughItDidntHaveDefenderAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/CanAttackAsThoughItDidntHaveDefenderAllEffect.java index a7c1dbffe2f..f3a1114bae2 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/combat/CanAttackAsThoughItDidntHaveDefenderAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/combat/CanAttackAsThoughItDidntHaveDefenderAllEffect.java @@ -1,4 +1,3 @@ - package mage.abilities.effects.common.combat; import mage.abilities.Ability; @@ -7,7 +6,6 @@ import mage.constants.AsThoughEffectType; import mage.constants.Duration; import mage.constants.Outcome; import mage.filter.FilterPermanent; -import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; @@ -20,10 +18,6 @@ public class CanAttackAsThoughItDidntHaveDefenderAllEffect extends AsThoughEffec private final FilterPermanent filter; - public CanAttackAsThoughItDidntHaveDefenderAllEffect(Duration duration) { - this(duration, StaticFilters.FILTER_PERMANENT_CREATURE); - } - public CanAttackAsThoughItDidntHaveDefenderAllEffect(Duration duration, FilterPermanent filter) { super(AsThoughEffectType.ATTACK, duration, Outcome.Benefit); this.filter = filter; diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/CanAttackAsThoughItDidntHaveDefenderSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/CanAttackAsThoughItDidntHaveDefenderSourceEffect.java index 40e19ce7260..3ff4271d8ef 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/combat/CanAttackAsThoughItDidntHaveDefenderSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/combat/CanAttackAsThoughItDidntHaveDefenderSourceEffect.java @@ -16,8 +16,12 @@ import mage.game.Game; public class CanAttackAsThoughItDidntHaveDefenderSourceEffect extends AsThoughEffectImpl { public CanAttackAsThoughItDidntHaveDefenderSourceEffect(Duration duration) { + this(duration, "{this}"); + } + + public CanAttackAsThoughItDidntHaveDefenderSourceEffect(Duration duration, String description) { super(AsThoughEffectType.ATTACK, duration, Outcome.Benefit); - staticText = "{this} can attack " + staticText = description + " can attack " + (duration == Duration.EndOfTurn ? "this turn " : "") + "as though it didn't have defender"; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackControllerDueToGoadEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackControllerDueToGoadEffect.java deleted file mode 100644 index 964bcf9a597..00000000000 --- a/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackControllerDueToGoadEffect.java +++ /dev/null @@ -1,46 +0,0 @@ -package mage.abilities.effects.common.combat; - -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.effects.RestrictionEffect; -import mage.constants.Duration; -import mage.game.Game; -import mage.game.permanent.Permanent; - -/** - * @author TheElk801 - */ -public class CantAttackControllerDueToGoadEffect extends RestrictionEffect { - - public CantAttackControllerDueToGoadEffect(Duration duration) { - super(duration); - } - - public CantAttackControllerDueToGoadEffect(final CantAttackControllerDueToGoadEffect effect) { - super(effect); - } - - @Override - public CantAttackControllerDueToGoadEffect copy() { - return new CantAttackControllerDueToGoadEffect(this); - } - - @Override - public boolean applies(Permanent permanent, Ability source, Game game) { - return this.getTargetPointer().getTargets(game, source).contains(permanent.getId()); - } - - @Override - public boolean canAttack(Permanent attacker, UUID defenderId, Ability source, Game game, boolean canUseChooseDialogs) { - if (defenderId == null - || game.getState().getPlayersInRange(attacker.getControllerId(), game).size() == 2) { // just 2 players left, so it may attack you - return true; - } - // A planeswalker controlled by the controller is the defender - if (game.getPermanent(defenderId) != null) { - return !game.getPermanent(defenderId).getControllerId().equals(source.getControllerId()); - } - // The controller is the defender - return !defenderId.equals(source.getControllerId()); - } -} diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/CantBeBlockedByAllTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/CantBeBlockedByAllTargetEffect.java index 1448aacf277..af0518dc931 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/combat/CantBeBlockedByAllTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/combat/CantBeBlockedByAllTargetEffect.java @@ -1,6 +1,7 @@ package mage.abilities.effects.common.combat; import mage.abilities.Ability; +import mage.abilities.Mode; import mage.abilities.effects.RestrictionEffect; import mage.constants.Duration; import mage.filter.common.FilterCreaturePermanent; @@ -19,11 +20,6 @@ public class CantBeBlockedByAllTargetEffect extends RestrictionEffect { public CantBeBlockedByAllTargetEffect(FilterCreaturePermanent filterBlockedBy, Duration duration) { super(duration); this.filterBlockedBy = filterBlockedBy; - staticText = "target creature" - + " can't be blocked " - + (duration == EndOfTurn ? "this turn " : "") - + (filterBlockedBy.getMessage().startsWith("except by") ? "" : "by ") - + filterBlockedBy.getMessage(); } public CantBeBlockedByAllTargetEffect(final CantBeBlockedByAllTargetEffect effect) { @@ -45,4 +41,17 @@ public class CantBeBlockedByAllTargetEffect extends RestrictionEffect { public CantBeBlockedByAllTargetEffect copy() { return new CantBeBlockedByAllTargetEffect(this); } + + @Override + public String getText(Mode mode) { + if (staticText != null && !staticText.isEmpty()) { + return staticText; + } + return "target " + + mode.getTargets().get(0).getTargetName() + + " can't be blocked " + + (duration == EndOfTurn ? "this turn " : "") + + (filterBlockedBy.getMessage().startsWith("except by") ? "" : "by ") + + filterBlockedBy.getMessage(); + } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/CantBlockAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/CantBlockAllEffect.java index a75abc49006..dea9bc16ecd 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/combat/CantBlockAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/combat/CantBlockAllEffect.java @@ -42,6 +42,9 @@ public class CantBlockAllEffect extends RestrictionEffect { @Override public String getText(Mode mode) { + if (staticText != null && !staticText.isEmpty()) { + return staticText; + } StringBuilder sb = new StringBuilder(); sb.append(filter.getMessage()).append(" can't block"); if (this.duration == Duration.EndOfTurn) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/ChooseBlockersEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/ChooseBlockersEffect.java new file mode 100644 index 00000000000..07d1dfee53b --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/combat/ChooseBlockersEffect.java @@ -0,0 +1,80 @@ +package mage.abilities.effects.common.combat; + +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.players.Player; +import mage.watchers.common.ControlCombatRedundancyWatcher; + +/** + * @author L_J, TheElk801 + */ +public class ChooseBlockersEffect extends ContinuousRuleModifyingEffectImpl { + + public ChooseBlockersEffect(Duration duration) { + super(duration, Outcome.Benefit, false, false); + } + + private ChooseBlockersEffect(final ChooseBlockersEffect effect) { + super(effect); + } + + @Override + public ChooseBlockersEffect copy() { + return new ChooseBlockersEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DECLARING_BLOCKERS; + } + + @Override + public void init(Ability source, Game game) { + super.init(source, game); + ControlCombatRedundancyWatcher.addBlockingController(source.getControllerId(), this.duration, game); + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (!ControlCombatRedundancyWatcher.checkBlockingController(source.getControllerId(), game)) { + game.informPlayers(source.getSourceObject(game).getIdName() + " didn't apply"); + return false; + } + Player blockController = game.getPlayer(source.getControllerId()); + if (blockController != null) { + game.getCombat().selectBlockers(blockController, source, game); + return true; + } + return false; + } + + @Override + public String getText(Mode mode) { + if (staticText != null && !staticText.isEmpty()) { + return staticText; + } + StringBuilder sb = new StringBuilder("you choose which creatures block this "); + switch (duration) { + case EndOfTurn: + sb.append("turn"); + break; + case EndOfCombat: + sb.append("combat"); + break; + default: + throw new IllegalArgumentException("duration type not supported"); + } + sb.append(" and how those creatures block"); + return sb.toString(); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/GoadAttachedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/GoadAttachedEffect.java new file mode 100644 index 00000000000..899aaa166c9 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/combat/GoadAttachedEffect.java @@ -0,0 +1,44 @@ +package mage.abilities.effects.common.combat; + +import mage.abilities.Ability; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.constants.Duration; +import mage.constants.Layer; +import mage.constants.Outcome; +import mage.constants.SubLayer; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * @author TheElk801 + */ +public class GoadAttachedEffect extends ContinuousEffectImpl { + + public GoadAttachedEffect() { + super(Duration.WhileOnBattlefield, Layer.RulesEffects, SubLayer.NA, Outcome.Detriment); + staticText = "and is goaded"; + } + + private GoadAttachedEffect(final GoadAttachedEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (permanent == null) { + return false; + } + Permanent attached = game.getPermanent(permanent.getAttachedTo()); + if (attached == null) { + return false; + } + attached.addGoadingPlayer(source.getControllerId()); + return true; + } + + @Override + public GoadAttachedEffect copy() { + return new GoadAttachedEffect(this); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/GoadTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/GoadTargetEffect.java index 578536f1d70..a61b27ca12e 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/combat/GoadTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/combat/GoadTargetEffect.java @@ -2,19 +2,19 @@ package mage.abilities.effects.common.combat; import mage.abilities.Ability; import mage.abilities.Mode; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.ContinuousEffectImpl; import mage.constants.Duration; +import mage.constants.Layer; import mage.constants.Outcome; +import mage.constants.SubLayer; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; -import mage.target.targetpointer.FixedTarget; /** * @author TheElk801 */ -public class GoadTargetEffect extends OneShotEffect { +public class GoadTargetEffect extends ContinuousEffectImpl { /** * 701.36. Goad @@ -24,10 +24,10 @@ public class GoadTargetEffect extends OneShotEffect { * each combat if able and attacks a player other than that player if able. */ public GoadTargetEffect() { - super(Outcome.Detriment); + super(Duration.UntilYourNextTurn, Layer.RulesEffects, SubLayer.NA, Outcome.Detriment); } - public GoadTargetEffect(final GoadTargetEffect effect) { + private GoadTargetEffect(final GoadTargetEffect effect) { super(effect); } @@ -37,26 +37,22 @@ public class GoadTargetEffect extends OneShotEffect { } @Override - public boolean apply(Game game, Ability source) { + public void init(Ability source, Game game) { + super.init(source, game); Permanent targetCreature = game.getPermanent(getTargetPointer().getFirst(game, source)); Player controller = game.getPlayer(source.getControllerId()); if (targetCreature != null && controller != null) { - // TODO: Allow goad to target controller, current AttacksIfAbleTargetEffect does not support it - // https://github.com/magefree/mage/issues/5283 - /* - If the creature doesn’t meet any of the above exceptions and can attack, it must attack a player other than - the controller of the spell or ability that goaded it if able. If the creature can’t attack any of those - players but could otherwise attack, it must attack an opposing planeswalker (controlled by any opponent) - or the player that goaded it. (2016-08-23) - */ - ContinuousEffect effect = new AttacksIfAbleTargetEffect(Duration.UntilYourNextTurn); - effect.setTargetPointer(new FixedTarget(getTargetPointer().getFirst(game, source))); - game.addEffect(effect, source); - effect = new CantAttackControllerDueToGoadEffect(Duration.UntilYourNextTurn); // remember current controller - effect.setTargetPointer(new FixedTarget(getTargetPointer().getFirst(game, source))); - game.addEffect(effect, source); game.informPlayers(controller.getLogName() + " is goading " + targetCreature.getLogName()); } + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent targetCreature = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (targetCreature == null) { + return false; + } + targetCreature.addGoadingPlayer(source.getControllerId()); return true; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/AddCardTypeSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/AddCardTypeSourceEffect.java index 0384c4b2523..384787e6a27 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/AddCardTypeSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/AddCardTypeSourceEffect.java @@ -80,7 +80,7 @@ public class AddCardTypeSourceEffect extends ContinuousEffectImpl { } sb.append(cardType.toString().toLowerCase(Locale.ENGLISH)).append(" "); } - sb.append(" in addition to its other types ").append(this.getDuration().toString()); + sb.append("in addition to its other types ").append(this.getDuration().toString()); return sb.toString(); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesBasicLandEnchantedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesBasicLandEnchantedEffect.java index d73b816f046..33d5cddc00e 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesBasicLandEnchantedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesBasicLandEnchantedEffect.java @@ -6,10 +6,12 @@ import mage.abilities.mana.*; import mage.constants.*; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.util.CardUtil; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; public class BecomesBasicLandEnchantedEffect extends ContinuousEffectImpl { @@ -18,7 +20,11 @@ public class BecomesBasicLandEnchantedEffect extends ContinuousEffectImpl { public BecomesBasicLandEnchantedEffect(SubType... landNames) { super(Duration.WhileOnBattlefield, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Detriment); landTypes.addAll(Arrays.asList(landNames)); - this.staticText = setText(); + this.staticText = "enchanted land is " + CardUtil.addArticle(CardUtil.concatWithAnd(landTypes + .stream() + .map(SubType::getDescription) + .collect(Collectors.toList()) + )); } public BecomesBasicLandEnchantedEffect(final BecomesBasicLandEnchantedEffect effect) { @@ -76,21 +82,4 @@ public class BecomesBasicLandEnchantedEffect extends ContinuousEffectImpl { } return true; } - - private String setText() { - StringBuilder sb = new StringBuilder("Enchanted land is a "); - int i = 1; - for (SubType landType : landTypes) { - if (i > 1) { - if (i == landTypes.size()) { - sb.append(" and "); - } else { - sb.append(", "); - } - } - i++; - sb.append(landType); - } - return sb.toString(); - } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesBasicLandTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesBasicLandTargetEffect.java index 2c4adcb1d6e..a571909ee99 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesBasicLandTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesBasicLandTargetEffect.java @@ -1,5 +1,6 @@ package mage.abilities.effects.common.continuous; +import mage.abilities.Abilities; import mage.abilities.Ability; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.mana.*; @@ -9,11 +10,13 @@ import mage.constants.*; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; +import mage.util.CardUtil; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.UUID; +import java.util.stream.Collectors; /** * http://mtgsalvation.gamepedia.com/Land_changers @@ -24,7 +27,6 @@ public class BecomesBasicLandTargetEffect extends ContinuousEffectImpl { protected boolean chooseLandType; protected List landTypes = new ArrayList<>(); - private final List landTypesToAdd = new ArrayList<>(); private final boolean loseOther; // loses all other abilities, card types, and creature types public BecomesBasicLandTargetEffect(Duration duration) { @@ -58,15 +60,13 @@ public class BecomesBasicLandTargetEffect extends ContinuousEffectImpl { dependencyTypes.add(DependencyType.BecomePlains); } this.chooseLandType = chooseLandType; - this.staticText = setText(); this.loseOther = loseOther; - + this.staticText = setText(); } public BecomesBasicLandTargetEffect(final BecomesBasicLandTargetEffect effect) { super(effect); this.landTypes.addAll(effect.landTypes); - this.landTypesToAdd.addAll(effect.landTypesToAdd); this.chooseLandType = effect.chooseLandType; this.loseOther = effect.loseOther; } @@ -90,9 +90,6 @@ public class BecomesBasicLandTargetEffect extends ContinuousEffectImpl { return; } } - if (loseOther) { - landTypesToAdd.addAll(landTypes); - } } @Override @@ -112,33 +109,37 @@ public class BecomesBasicLandTargetEffect extends ContinuousEffectImpl { land.removeAllAbilities(source.getSourceId(), game); // 305.7 land.removeAllSubTypes(game, SubTypeSet.NonBasicLandType); - land.addSubType(game, landTypes); - } else { - landTypesToAdd.clear(); - for (SubType subtype : landTypes) { - if (!land.hasSubtype(subtype, game)) { - land.addSubType(game, subtype); - landTypesToAdd.add(subtype); - } - } } + land.addSubType(game, landTypes); + // add intrinsic land abilities here not in layer 6 - for (SubType landType : landTypesToAdd) { + Abilities landAbilities = land.getAbilities(game); + for (SubType landType : landTypes) { switch (landType) { case PLAINS: - land.addAbility(new WhiteManaAbility(), source.getSourceId(), game); + if (!landAbilities.containsClass(WhiteManaAbility.class)) { + land.addAbility(new WhiteManaAbility(), source.getSourceId(), game); + } break; case ISLAND: - land.addAbility(new BlueManaAbility(), source.getSourceId(), game); + if (!landAbilities.containsClass(BlueManaAbility.class)) { + land.addAbility(new BlueManaAbility(), source.getSourceId(), game); + } break; case SWAMP: - land.addAbility(new BlackManaAbility(), source.getSourceId(), game); + if (!landAbilities.containsClass(BlackManaAbility.class)) { + land.addAbility(new BlackManaAbility(), source.getSourceId(), game); + } break; case MOUNTAIN: - land.addAbility(new RedManaAbility(), source.getSourceId(), game); + if (!landAbilities.containsClass(RedManaAbility.class)) { + land.addAbility(new RedManaAbility(), source.getSourceId(), game); + } break; case FOREST: - land.addAbility(new GreenManaAbility(), source.getSourceId(), game); + if (!landAbilities.containsClass(GreenManaAbility.class)) { + land.addAbility(new GreenManaAbility(), source.getSourceId(), game); + } break; } } @@ -147,23 +148,18 @@ public class BecomesBasicLandTargetEffect extends ContinuousEffectImpl { } private String setText() { - StringBuilder sb = new StringBuilder(); + StringBuilder sb = new StringBuilder("target land becomes "); if (chooseLandType) { - sb.append("Target land becomes the basic land type of your choice"); + sb.append("the basic land type of your choice"); } else { - sb.append("Target land becomes a "); - int i = 1; - for (SubType landType : landTypes) { - if (i > 1) { - if (i == landTypes.size()) { - sb.append(" and "); - } else { - sb.append(", "); - } - } - i++; - sb.append(landType); - } + sb.append(CardUtil.addArticle(CardUtil.concatWithAnd(landTypes + .stream() + .map(SubType::getDescription) + .collect(Collectors.toList()) + ))); + } + if (!loseOther) { + sb.append(" in addition to its other types"); } if (!duration.toString().isEmpty() && duration != Duration.EndOfGame) { sb.append(' ').append(duration.toString()); diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesChosenCreatureTypeTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesChosenCreatureTypeTargetEffect.java index ef3759eaa90..8d0620cbee6 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesChosenCreatureTypeTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesChosenCreatureTypeTargetEffect.java @@ -71,7 +71,7 @@ public class BecomesChosenCreatureTypeTargetEffect extends OneShotEffect { if (chosenType != null && !chosenType.isEmpty()) { // ADD TYPE TO TARGET ContinuousEffect effect = new BecomesCreatureTypeTargetEffect(duration, SubType.byDescription(chosenType)); - effect.setTargetPointer(new FixedTarget(getTargetPointer().getFirst(game, source))); + effect.setTargetPointer(new FixedTarget(getTargetPointer().getFirst(game, source), game)); game.addEffect(effect, source); return true; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesColorOrColorsTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesColorOrColorsTargetEffect.java index 752659b5013..1ebf39d2543 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesColorOrColorsTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesColorOrColorsTargetEffect.java @@ -74,7 +74,7 @@ public class BecomesColorOrColorsTargetEffect extends OneShotEffect { String colors = new String(sb); ObjectColor chosenColors = new ObjectColor(colors); ContinuousEffect effect = new BecomesColorTargetEffect(chosenColors, duration); - effect.setTargetPointer(new FixedTarget(source.getFirstTarget())); + effect.setTargetPointer(new FixedTarget(source.getFirstTarget(), game)); game.addEffect(effect, source); return true; diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostAllEffect.java index bf1261f9266..f7a92d70af3 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostAllEffect.java @@ -1,7 +1,5 @@ - package mage.abilities.effects.common.continuous; -import java.util.Iterator; import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.dynamicvalue.DynamicValue; @@ -11,9 +9,14 @@ import mage.constants.Duration; import mage.constants.Layer; import mage.constants.Outcome; import mage.constants.SubLayer; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.util.CardUtil; + +import java.util.Iterator; +import java.util.Locale; /** * @@ -32,11 +35,11 @@ public class BoostAllEffect extends ContinuousEffectImpl { } public BoostAllEffect(DynamicValue power, DynamicValue toughness, Duration duration) { - this(power, toughness, duration, new FilterCreaturePermanent("all creatures"), false); + this(power, toughness, duration, StaticFilters.FILTER_PERMANENT_ALL_CREATURES, false); } public BoostAllEffect(int power, int toughness, Duration duration, boolean excludeSource) { - this(power, toughness, duration, new FilterCreaturePermanent("all creatures"), excludeSource); + this(power, toughness, duration, StaticFilters.FILTER_PERMANENT_ALL_CREATURES, excludeSource); } public BoostAllEffect(int power, int toughness, Duration duration, FilterCreaturePermanent filter, boolean excludeSource) { @@ -146,45 +149,13 @@ public class BoostAllEffect extends ContinuousEffectImpl { protected void setText() { StringBuilder sb = new StringBuilder(); - if (excludeSource) { + boolean each = filter.getMessage().toLowerCase(Locale.ENGLISH).startsWith("each"); + if (excludeSource && !each) { sb.append("other "); } - sb.append(filter.getMessage()).append(" get "); - String p = power.toString(); - if (!p.startsWith("-")) { - sb.append('+'); - } - sb.append(p).append('/'); - String t = toughness.toString(); - if (!t.startsWith("-")) { - if (p.startsWith("-")) { - sb.append('-'); - } else { - sb.append('+'); - } - } - sb.append(t); - if (duration == Duration.EndOfTurn) { - sb.append(" until end of turn"); - } - String message = null; - String fixedPart = null; - if (t.contains("X")) { - message = toughness.getMessage(); - fixedPart = ", where X is "; - } else if (p.contains("X")) { - message = power.getMessage(); - fixedPart = ", where X is "; - } else if (!power.getMessage().isEmpty()) { - message = power.getMessage(); - fixedPart = " for each "; - } else if (!toughness.getMessage().isEmpty()) { - message = toughness.getMessage(); - fixedPart = " for each "; - } - if (message != null && !message.isEmpty() && fixedPart != null) { - sb.append(fixedPart).append(message); - } + sb.append(filter.getMessage()); + sb.append(each ? " gets " : " get "); + sb.append(CardUtil.getBoostText(power, toughness, duration)); staticText = sb.toString(); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostControlledEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostControlledEffect.java index 8466445ba7d..f6e1f823de9 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostControlledEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostControlledEffect.java @@ -13,8 +13,10 @@ import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.util.CardUtil; import java.util.Iterator; +import java.util.Locale; /** * @author BetaSteward_at_googlemail.com @@ -126,50 +128,13 @@ public class BoostControlledEffect extends ContinuousEffectImpl { private void setText() { StringBuilder sb = new StringBuilder(); - if (excludeSource) { + boolean each = filter.getMessage().toLowerCase(Locale.ENGLISH).startsWith("each"); + if (excludeSource && !each) { sb.append("other "); } - sb.append(filter.getMessage()); - sb.append(" you control get "); - - String p = power.toString(); - if (!p.startsWith("-")) { - sb.append('+'); - } - sb.append(p).append('/'); - String t = toughness.toString(); - if (!t.startsWith("-")) { - if (p.startsWith("-")) { - sb.append('-'); - } else { - sb.append('+'); - } - } - sb.append(t); - - sb.append((duration == Duration.EndOfTurn ? " until end of turn" : "")); - - // where X - String message = null; - if (t.equals("X")) { - message = toughness.getMessage(); - } else if (p.equals("X")) { - message = power.getMessage(); - } - if (message != null && !message.isEmpty()) { - sb.append(", where X is ").append(message); - } - - // for each - if (message == null) { - message = toughness.getMessage(); - if (message.isEmpty()) { - message = power.getMessage(); - } - if (!message.isEmpty()) { - sb.append(" for each " + message); - } - } + sb.append(filter.getMessage()).append(" you control "); + sb.append(each ? "gets " : "get "); + sb.append(CardUtil.getBoostText(power, toughness, duration)); staticText = sb.toString(); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostEnchantedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostEnchantedEffect.java index 8dcdd7f70ad..d64751de07d 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostEnchantedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostEnchantedEffect.java @@ -12,6 +12,7 @@ import mage.constants.SubLayer; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; /** * @@ -39,7 +40,7 @@ public class BoostEnchantedEffect extends ContinuousEffectImpl { super(duration, Layer.PTChangingEffects_7, SubLayer.ModifyPT_7c, isCanKill(toughness) ? Outcome.UnboostCreature : Outcome.BoostCreature); this.power = power; this.toughness = toughness; - setText(); + this.staticText = "enchanted creature gets " + CardUtil.getBoostText(power, toughness, duration); } public BoostEnchantedEffect(final BoostEnchantedEffect effect) { @@ -96,46 +97,4 @@ public class BoostEnchantedEffect extends ContinuousEffectImpl { public void setLockedIn(boolean lockedIn) { this.lockedIn = lockedIn; } - - private void setText() { - StringBuilder sb = new StringBuilder(); - sb.append("enchanted creature gets "); - String p = power.toString(); - if (!p.startsWith("-")) { - sb.append('+'); - } - sb.append(p).append('/'); - String t = toughness.toString(); - if (!t.startsWith("-")) { - if (p.startsWith("-")) { - sb.append('-'); - } else { - sb.append('+'); - } - } - sb.append(t); - if (duration != Duration.WhileOnBattlefield) { - sb.append(' ').append(duration.toString()); - } - String message = null; - String fixedPart = null; - if (t.contains("X")) { - message = toughness.getMessage(); - fixedPart = ", where X is "; - } else if (p.contains("X")) { - message = power.getMessage(); - fixedPart = ", where X is "; - } else if (!power.getMessage().isEmpty()) { - message = power.getMessage(); - fixedPart = " for each "; - } else if (!toughness.getMessage().isEmpty()) { - message = toughness.getMessage(); - fixedPart = " for each "; - } - if (message != null && !message.isEmpty() && fixedPart != null) { - sb.append(fixedPart).append(message); - } - staticText = sb.toString(); - } - } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostEquippedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostEquippedEffect.java index 1e9c895a254..a06917ea8bc 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostEquippedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostEquippedEffect.java @@ -12,6 +12,7 @@ import mage.abilities.effects.ContinuousEffectImpl; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; /** * @author BetaSteward_at_googlemail.com @@ -41,7 +42,7 @@ public class BoostEquippedEffect extends ContinuousEffectImpl { if (duration == Duration.EndOfTurn) { fixedTarget = true; } - setText(); + this.staticText = "equipped creature gets " + CardUtil.getBoostText(power, toughness, duration); } public BoostEquippedEffect(final BoostEquippedEffect effect) { @@ -61,7 +62,7 @@ public class BoostEquippedEffect extends ContinuousEffectImpl { if (fixedTarget) { Permanent equipment = game.getPermanent(source.getSourceId()); if (equipment != null && equipment.getAttachedTo() != null) { - this.setTargetPointer(new FixedTarget(equipment.getAttachedTo())); + this.setTargetPointer(new FixedTarget(equipment.getAttachedTo(), game)); } } super.init(source, game); // inits the target pointer so call it after setting the targetPointer @@ -86,32 +87,4 @@ public class BoostEquippedEffect extends ContinuousEffectImpl { return true; } - - private void setText() { - StringBuilder sb = new StringBuilder(); - sb.append("equipped creature gets "); - String p = power.toString(); - if (!p.startsWith("-")) { - sb.append('+'); - } - sb.append(p).append('/'); - String t = toughness.toString(); - if (!t.startsWith("-")) { - if (p.startsWith("-")) { - sb.append('-'); - } else { - sb.append('+'); - } - } - sb.append(t); - if (duration != Duration.WhileOnBattlefield) { - sb.append(' ').append(duration.toString()); - } - String message = power.getMessage(); - if (!message.isEmpty()) { - sb.append(" for each "); - } - sb.append(message); - staticText = sb.toString(); - } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostSourceEffect.java index bf389e93439..2523c515157 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostSourceEffect.java @@ -11,6 +11,7 @@ import mage.constants.Outcome; import mage.constants.SubLayer; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.util.CardUtil; import org.apache.log4j.Logger; /** @@ -29,18 +30,23 @@ public class BoostSourceEffect extends ContinuousEffectImpl implements SourceEff this(power, toughness, duration, false); } + public BoostSourceEffect(DynamicValue power, DynamicValue toughness, Duration duration, boolean lockedIn) { + this(power, toughness, duration, lockedIn, "{this}"); + } + /** * @param power * @param toughness * @param duration * @param lockedIn if true, power and toughness will be calculated only once, when the ability resolves + * @param description */ - public BoostSourceEffect(DynamicValue power, DynamicValue toughness, Duration duration, boolean lockedIn) { + public BoostSourceEffect(DynamicValue power, DynamicValue toughness, Duration duration, boolean lockedIn, String description) { super(duration, Layer.PTChangingEffects_7, SubLayer.ModifyPT_7c, Outcome.BoostCreature); this.power = power; this.toughness = toughness; this.lockedIn = lockedIn; - setText(); + this.staticText = description + " gets " + CardUtil.getBoostText(power, toughness, duration); } public BoostSourceEffect(final BoostSourceEffect effect) { @@ -86,42 +92,4 @@ public class BoostSourceEffect extends ContinuousEffectImpl implements SourceEff } return false; } - - private void setText() { - StringBuilder sb = new StringBuilder(); - sb.append("{this} gets "); - String p = power.toString(); - if (!p.startsWith("-")) { - sb.append('+'); - } - sb.append(p).append('/'); - String t = toughness.toString(); - if (!t.startsWith("-")) { - sb.append('+'); - } - sb.append(t); - if (duration != Duration.WhileOnBattlefield) { - sb.append(' ').append(duration.toString()); - } - String message = null; - String fixedPart = null; - if (t.contains("X")) { - message = toughness.getMessage(); - fixedPart = ", where X is "; - } else if (p.contains("X")) { - message = power.getMessage(); - fixedPart = ", where X is "; - } else if (!power.getMessage().isEmpty()) { - message = power.getMessage(); - fixedPart = " for each "; - } else if (!toughness.getMessage().isEmpty()) { - message = toughness.getMessage(); - fixedPart = " for each "; - } - if (message != null && !message.isEmpty() && fixedPart != null) { - sb.append(fixedPart).append(message); - } - staticText = sb.toString(); - } - } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostTargetEffect.java index 3f772aa5523..269a8b169b9 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostTargetEffect.java @@ -111,41 +111,7 @@ public class BoostTargetEffect extends ContinuousEffectImpl { } sb.append(target.getTargetName()).append(" gets "); } - String p = power.toString(); - if (!p.startsWith("-")) { - sb.append('+'); - } - sb.append(p).append('/'); - String t = toughness.toString(); - if (!t.startsWith("-")) { - if (t.equals("0") && p.startsWith("-")) { - sb.append('-'); - } else { - sb.append('+'); - } - } - sb.append(t); - if (duration != Duration.WhileOnBattlefield) { - sb.append(' ').append(duration.toString()); - } - String message = null; - String fixedPart = null; - if (t.contains("X")) { - message = toughness.getMessage(); - fixedPart = ", where X is "; - } else if (p.contains("X")) { - message = power.getMessage(); - fixedPart = ", where X is "; - } else if (!power.getMessage().isEmpty()) { - message = power.getMessage(); - fixedPart = " for each "; - } else if (!toughness.getMessage().isEmpty()) { - message = toughness.getMessage(); - fixedPart = " for each "; - } - if (message != null && !message.isEmpty() && fixedPart != null) { - sb.append(fixedPart).append(message); - } + sb.append(CardUtil.getBoostText(power, toughness, duration)); return sb.toString(); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAllEffect.java index 5f576165121..52c35b7c411 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAllEffect.java @@ -7,6 +7,8 @@ import mage.abilities.Mode; import mage.abilities.TriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.mana.ActivatedManaAbilityImpl; +import mage.abilities.mana.SimpleManaAbility; import mage.constants.*; import mage.filter.FilterPermanent; import mage.game.Game; @@ -136,21 +138,19 @@ public class GainAbilityAllEffect extends ContinuousEffectImpl { StringBuilder sb = new StringBuilder(); - boolean quotes = forceQuotes || (ability instanceof SimpleActivatedAbility) || (ability instanceof TriggeredAbility); - if (excludeSource) { - sb.append("Other "); + boolean quotes = forceQuotes + || ability instanceof SimpleActivatedAbility + ||ability instanceof ActivatedManaAbilityImpl + || ability instanceof TriggeredAbility; + boolean each = filter.getMessage().toLowerCase(Locale.ENGLISH).startsWith("each"); + if (excludeSource && !each) { + sb.append("other "); } sb.append(filter.getMessage()); if (duration == Duration.WhileOnBattlefield) { - if (filter.getMessage().toLowerCase(Locale.ENGLISH).startsWith("each")) { - sb.append(" has "); - } else { - sb.append(" have "); - } - } else if (filter.getMessage().toLowerCase(Locale.ENGLISH).startsWith("each")) { - sb.append(" gains "); + sb.append(each ? " has " : " have "); } else { - sb.append(" gain "); + sb.append(each ? " gains " : " gain "); } if (quotes) { sb.append('"'); diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAttachedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAttachedEffect.java index 437d4a8b9db..c601bb23a05 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAttachedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAttachedEffect.java @@ -1,7 +1,11 @@ package mage.abilities.effects.common.continuous; import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.TriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.keyword.ProtectionAbility; import mage.constants.*; import mage.game.Game; import mage.game.permanent.Permanent; @@ -16,6 +20,7 @@ public class GainAbilityAttachedEffect extends ContinuousEffectImpl { protected AttachmentType attachmentType; protected boolean independentEffect; protected String targetObjectName; + protected boolean doesntRemoveItself = false; public GainAbilityAttachedEffect(Ability ability, AttachmentType attachmentType) { this(ability, attachmentType, Duration.WhileOnBattlefield); @@ -45,9 +50,7 @@ public class GainAbilityAttachedEffect extends ContinuousEffectImpl { independentEffect = true; } - if (rule == null) { - setText(); - } else { + if (rule != null) { this.staticText = rule; } @@ -61,6 +64,7 @@ public class GainAbilityAttachedEffect extends ContinuousEffectImpl { this.attachmentType = effect.attachmentType; this.independentEffect = effect.independentEffect; this.targetObjectName = effect.targetObjectName; + this.doesntRemoveItself = effect.doesntRemoveItself; } @Override @@ -81,7 +85,7 @@ public class GainAbilityAttachedEffect extends ContinuousEffectImpl { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = null; + Permanent permanent; if (affectedObjectsSet) { permanent = game.getPermanent(targetPointer.getFirst(game, source)); if (permanent == null) { @@ -92,9 +96,14 @@ public class GainAbilityAttachedEffect extends ContinuousEffectImpl { Permanent equipment = game.getPermanent(source.getSourceId()); if (equipment != null && equipment.getAttachedTo() != null) { permanent = game.getPermanentOrLKIBattlefield(equipment.getAttachedTo()); + } else { + permanent = null; } } if (permanent != null) { + if (doesntRemoveItself && ability instanceof ProtectionAbility) { + ((ProtectionAbility) ability).setAuraIdNotToBeRemoved(source.getSourceId()); + } permanent.addAbility(ability, source.getSourceId(), game); afterGain(game, source, permanent, ability); } @@ -113,20 +122,38 @@ public class GainAbilityAttachedEffect extends ContinuousEffectImpl { // } - private void setText() { + public GainAbilityAttachedEffect setDoesntRemoveItself(boolean doesntRemoveItself) { + this.doesntRemoveItself = doesntRemoveItself; + return this; + } + + @Override + public String getText(Mode mode) { + if (staticText != null && !staticText.isEmpty()) { + return staticText; + } StringBuilder sb = new StringBuilder(); - sb.append(attachmentType.verb()); + sb.append(attachmentType.verb().toLowerCase()); sb.append(" " + targetObjectName + " "); if (duration == Duration.WhileOnBattlefield) { sb.append("has "); } else { sb.append("gains "); } - sb.append(ability.getRule("this " + targetObjectName)); - if (!duration.toString().isEmpty()) { - sb.append(' ').append(duration.toString()); + boolean quotes = (ability instanceof SimpleActivatedAbility) || (ability instanceof TriggeredAbility); + if (quotes) { + sb.append('"'); } - staticText = sb.toString(); + sb.append(ability.getRule("this " + targetObjectName)); + if (quotes) { + sb.append('"'); + } + if (!duration.toString().isEmpty()) { + sb.append(' ').append(duration); + } + if (doesntRemoveItself) { + sb.append(" This effect doesn't remove {this}."); + } + return sb.toString(); } - } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityTargetEffect.java index a6a39f14206..a11fdc53250 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityTargetEffect.java @@ -28,6 +28,10 @@ public class GainAbilityTargetEffect extends ContinuousEffectImpl { private UUID durationPlayerId; private boolean sameStep; + public GainAbilityTargetEffect(Ability ability) { + this(ability, Duration.EndOfTurn); + } + public GainAbilityTargetEffect(Ability ability, Duration duration) { this(ability, duration, null); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainControlAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainControlAllEffect.java index 66c82fdab3f..40742c11582 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainControlAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainControlAllEffect.java @@ -30,7 +30,7 @@ public class GainControlAllEffect extends OneShotEffect { this.filter = filter; this.duration = duration; this.controllingPlayerId = controllingPlayerId; - this.staticText = "Gain control of " + filter.getMessage(); + this.staticText = "gain control of " + filter.getMessage(); } public GainControlAllEffect(final GainControlAllEffect effect) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainControlAllOwnedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainControlAllOwnedEffect.java new file mode 100644 index 00000000000..acac92fa1e4 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainControlAllOwnedEffect.java @@ -0,0 +1,66 @@ +package mage.abilities.effects.common.continuous; + +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.constants.Duration; +import mage.constants.Layer; +import mage.constants.Outcome; +import mage.constants.SubLayer; +import mage.filter.FilterPermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; + +import java.util.Iterator; + +/** + * @author TheElk801 + */ +public class GainControlAllOwnedEffect extends ContinuousEffectImpl { + + private final FilterPermanent filter; + + public GainControlAllOwnedEffect(FilterPermanent filter) { + super(Duration.EndOfGame, Layer.ControlChangingEffects_2, SubLayer.NA, Outcome.GainControl); + this.filter = filter; + this.staticText = "each player gains control of all " + filter + " they own"; + } + + public GainControlAllOwnedEffect(final GainControlAllOwnedEffect effect) { + super(effect); + this.filter = effect.filter; + } + + @Override + public GainControlAllOwnedEffect copy() { + return new GainControlAllOwnedEffect(this); + } + + @Override + public void init(Ability source, Game game) { + super.init(source, game); + for (Permanent permanent : game.getBattlefield().getActivePermanents( + filter, source.getControllerId(), source.getSourceId(), game + )) { + affectedObjectList.add(new MageObjectReference(permanent, game)); + } + } + + @Override + public boolean apply(Game game, Ability source) { + for (Iterator it = affectedObjectList.iterator(); it.hasNext(); ) { + Permanent permanent = it.next().getPermanent(game); + if (permanent == null) { + it.remove(); + continue; + } + if (!permanent.isControlledBy(permanent.getOwnerId())) { + permanent.changeControllerId(permanent.getOwnerId(), game, source); + } + } + if (affectedObjectList.isEmpty()) { + this.discard(); + } + return true; + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/SetPowerToughnessTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/SetPowerToughnessTargetEffect.java index 31b08e6b2d6..a888c15697a 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/SetPowerToughnessTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/SetPowerToughnessTargetEffect.java @@ -70,9 +70,11 @@ public class SetPowerToughnessTargetEffect extends ContinuousEffectImpl { } StringBuilder sb = new StringBuilder(); if (mode.getTargets().get(0).getMinNumberOfTargets() == 0) { - sb.append("up to "); - sb.append(CardUtil.numberToText(mode.getTargets().get(0).getMaxNumberOfTargets())); - sb.append(' '); + if (!mode.getTargets().get(0).getTargetName().startsWith("any")) { + sb.append("up to "); + sb.append(CardUtil.numberToText(mode.getTargets().get(0).getMaxNumberOfTargets())); + sb.append(' '); + } } if (!mode.getTargets().get(0).getTargetName().contains("target")) { sb.append("target "); diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostReductionControllerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostReductionControllerEffect.java index b2367505fb0..1f73f4cbffb 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostReductionControllerEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostReductionControllerEffect.java @@ -110,7 +110,7 @@ public class SpellsCostReductionControllerEffect extends CostModificationEffectI public boolean applies(Ability abilityToModify, Ability source, Game game) { if (abilityToModify instanceof SpellAbility) { if (abilityToModify.isControlledBy(source.getControllerId())) { - Card spellCard = ((SpellAbility) abilityToModify).getCharacteristics(game);; + Card spellCard = ((SpellAbility) abilityToModify).getCharacteristics(game); if (spellCard != null) { return this.filter.match(spellCard, source.getSourceId(), source.getControllerId(), game); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/counter/AddCountersAttachedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/counter/AddCountersAttachedEffect.java index 5448c45350b..0cea9db26ed 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/counter/AddCountersAttachedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/counter/AddCountersAttachedEffect.java @@ -74,12 +74,21 @@ public class AddCountersAttachedEffect extends OneShotEffect { StringBuilder sb = new StringBuilder(); // put a +1/+1 counter on it sb.append("put "); - if (counter.getCount() > 1) { + boolean plural = true; + if (amount.toString().equals("X")) { + sb.append("X "); + } else if (counter.getCount() > 1) { sb.append(CardUtil.numberToText(counter.getCount())).append(' '); } else { sb.append(CounterType.findArticle(counter.getName())).append(' '); + plural = false; + } + sb.append(counter.getName().toLowerCase(Locale.ENGLISH)); + if (plural) { + sb.append(" counters on "); + } else { + sb.append(" counter on "); } - sb.append(counter.getName().toLowerCase(Locale.ENGLISH)).append(" counter on "); sb.append(textEnchanted); if (!amount.getMessage().isEmpty()) { sb.append(" for each ").append(amount.getMessage()); diff --git a/Mage/src/main/java/mage/abilities/effects/common/counter/AddCountersTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/counter/AddCountersTargetEffect.java index fa16864aa84..ac618fb9e62 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/counter/AddCountersTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/counter/AddCountersTargetEffect.java @@ -119,6 +119,9 @@ public class AddCountersTargetEffect extends OneShotEffect { Target target = mode.getTargets().getEffectTarget(this.targetPointer); if (target != null) { if (target.getNumberOfTargets() == 0) { + if (target.getMaxNumberOfTargets() > 1) { + sb.append("each of "); + } sb.append("up to "); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/counter/RemoveCounterSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/counter/RemoveCounterSourceEffect.java index 4050cf49d90..6b604c58d3c 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/counter/RemoveCounterSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/counter/RemoveCounterSourceEffect.java @@ -8,6 +8,7 @@ import mage.counters.Counter; import mage.counters.CounterType; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.util.CardUtil; /** * @author Loki @@ -66,7 +67,7 @@ public class RemoveCounterSourceEffect extends OneShotEffect { private void setText() { if (counter.getCount() > 1) { StringBuilder sb = new StringBuilder(); - sb.append("remove ").append(Integer.toString(counter.getCount())).append(' ').append(counter.getName()).append(" counters from {this}"); + sb.append("remove ").append(CardUtil.numberToText(counter.getCount())).append(' ').append(counter.getName()).append(" counters from {this}"); staticText = sb.toString(); } else { staticText = "remove " + CounterType.findArticle(counter.getName()) + " " + counter.getName() + " counter from {this}"; diff --git a/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardCardYouChooseTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardCardYouChooseTargetEffect.java index 803343dc731..02432352abc 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardCardYouChooseTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardCardYouChooseTargetEffect.java @@ -31,8 +31,6 @@ public class DiscardCardYouChooseTargetEffect extends OneShotEffect { private final DynamicValue numberCardsToDiscard; private boolean revealAllCards; - private static final FilterCard filterOneCard = new FilterCard("one card"); - public DiscardCardYouChooseTargetEffect() { this(StaticFilters.FILTER_CARD_A); } @@ -41,38 +39,22 @@ public class DiscardCardYouChooseTargetEffect extends OneShotEffect { this(StaticFilters.FILTER_CARD_A, targetController); } - public DiscardCardYouChooseTargetEffect(DynamicValue numberCardsToDiscard, TargetController targetController) { - this(numberCardsToDiscard, StaticFilters.FILTER_CARD_CARDS, targetController); - } - public DiscardCardYouChooseTargetEffect(FilterCard filter) { this(filter, TargetController.OPPONENT); } - public DiscardCardYouChooseTargetEffect(TargetController targetController, int numberCardsToReveal) { - this(filterOneCard, targetController, StaticValue.get(numberCardsToReveal)); - } - - public DiscardCardYouChooseTargetEffect(TargetController targetController, DynamicValue numberCardsToReveal) { - this(filterOneCard, targetController, numberCardsToReveal); - } - - public DiscardCardYouChooseTargetEffect(FilterCard filter, TargetController targetController, DynamicValue numberCardsToReveal) { - super(Outcome.Discard); - this.targetController = targetController; - this.filter = filter; - - this.revealAllCards = false; - this.numberCardsToReveal = numberCardsToReveal; - this.numberCardsToDiscard = StaticValue.get(1); - - staticText = this.setText(); - } - public DiscardCardYouChooseTargetEffect(FilterCard filter, TargetController targetController) { this(StaticValue.get(1), filter, targetController); } + public DiscardCardYouChooseTargetEffect(int numberCardsToDiscard, TargetController targetController) { + this(StaticValue.get(numberCardsToDiscard), targetController); + } + + public DiscardCardYouChooseTargetEffect(DynamicValue numberCardsToDiscard, TargetController targetController) { + this(numberCardsToDiscard, StaticFilters.FILTER_CARD_CARDS, targetController); + } + public DiscardCardYouChooseTargetEffect(DynamicValue numberCardsToDiscard, FilterCard filter, TargetController targetController) { super(Outcome.Discard); @@ -86,6 +68,30 @@ public class DiscardCardYouChooseTargetEffect extends OneShotEffect { staticText = this.setText(); } + public DiscardCardYouChooseTargetEffect(TargetController targetController, int numberCardsToReveal) { + this(targetController, StaticValue.get(numberCardsToReveal)); + } + + public DiscardCardYouChooseTargetEffect(TargetController targetController, DynamicValue numberCardsToReveal) { + this(StaticValue.get(1), StaticFilters.FILTER_CARD_A, targetController, numberCardsToReveal); + } + + public DiscardCardYouChooseTargetEffect(int numberCardsToDiscard, TargetController targetController, int numberCardsToReveal) { + this(StaticValue.get(numberCardsToDiscard), StaticFilters.FILTER_CARD_CARDS, targetController, StaticValue.get(numberCardsToReveal)); + } + + public DiscardCardYouChooseTargetEffect(DynamicValue numberCardsToDiscard, FilterCard filter, TargetController targetController, DynamicValue numberCardsToReveal) { + super(Outcome.Discard); + this.targetController = targetController; + this.filter = filter; + + this.revealAllCards = false; + this.numberCardsToReveal = numberCardsToReveal; + this.numberCardsToDiscard = numberCardsToDiscard; + + staticText = this.setText(); + } + public DiscardCardYouChooseTargetEffect(final DiscardCardYouChooseTargetEffect effect) { super(effect); this.filter = effect.filter; @@ -155,6 +161,7 @@ public class DiscardCardYouChooseTargetEffect extends OneShotEffect { } private String setText() { + boolean discardMultipleCards = !numberCardsToDiscard.toString().equals("1"); StringBuilder sb = new StringBuilder("target "); switch (targetController) { case OPPONENT: @@ -166,32 +173,37 @@ public class DiscardCardYouChooseTargetEffect extends OneShotEffect { default: throw new UnsupportedOperationException("target controller not supported"); } + sb.append(" reveals "); if (revealAllCards) { - sb.append(" reveals their hand"); + sb.append("their hand. You choose "); + if (discardMultipleCards) { + sb.append(numberCardsToDiscard).append(' ').append(filter.getMessage()); + } else { + sb.append(CardUtil.addArticle(filter.getMessage())); + } + if (!filter.getMessage().contains("from it")) { + sb.append(" from it"); + } } else { if (numberCardsToReveal instanceof StaticValue) { - sb.append(" reveals "); - sb.append(CardUtil.numberToText(((StaticValue) numberCardsToReveal).getValue())).append(" cards"); - sb.append(" from their hand"); + sb.append(CardUtil.numberToText(((StaticValue) numberCardsToReveal).getValue())); + sb.append(" cards from their hand"); + } else if (numberCardsToReveal.getMessage().isEmpty()) { + sb.append("X cards from their hand"); } else { - sb.append(" reveals a number of cards from their hand equal to "); + sb.append("a number of cards from their hand equal to "); sb.append(numberCardsToReveal.getMessage()); } - } - sb.append(". You choose "); - boolean discardMultipleCards = !numberCardsToDiscard.toString().equals("1"); - if (discardMultipleCards) { - sb.append(numberCardsToDiscard).append(' ').append(filter.getMessage()); - } else { - sb.append(CardUtil.addArticle(filter.getMessage())); - } - if (revealAllCards) { - sb.append(" from it."); - } else { - sb.append(" of them."); + sb.append(". You choose "); + if (numberCardsToDiscard instanceof StaticValue) { + sb.append(CardUtil.numberToText(((StaticValue) numberCardsToDiscard).getValue())); + } else { + sb.append(numberCardsToDiscard); + } + sb.append(" of them"); } - sb.append(" That player discards ").append(discardMultipleCards ? "those cards" : "that card"); + sb.append(". That player discards ").append(discardMultipleCards ? "those cards" : "that card"); return sb.toString(); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/enterAttribute/EnterAttributeAddChosenSubtypeEffect.java b/Mage/src/main/java/mage/abilities/effects/common/enterAttribute/EnterAttributeAddChosenSubtypeEffect.java index 84a0bc07514..a2f288faff3 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/enterAttribute/EnterAttributeAddChosenSubtypeEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/enterAttribute/EnterAttributeAddChosenSubtypeEffect.java @@ -18,7 +18,6 @@ public class EnterAttributeAddChosenSubtypeEffect extends OneShotEffect { public EnterAttributeAddChosenSubtypeEffect() { super(Outcome.Benefit); - this.staticText = "{this} is the chosen type in addition to its other types"; } public EnterAttributeAddChosenSubtypeEffect(final EnterAttributeAddChosenSubtypeEffect effect) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/replacement/CreateTwiceThatManyTokensEffect.java b/Mage/src/main/java/mage/abilities/effects/common/replacement/CreateTwiceThatManyTokensEffect.java index ef77c25eec4..428c3cd34fa 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/replacement/CreateTwiceThatManyTokensEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/replacement/CreateTwiceThatManyTokensEffect.java @@ -1,6 +1,7 @@ package mage.abilities.effects.common.replacement; import mage.abilities.Ability; +import mage.abilities.Mode; import mage.abilities.effects.ReplacementEffectImpl; import mage.constants.Duration; import mage.constants.Outcome; @@ -14,9 +15,11 @@ import mage.game.events.GameEvent; public class CreateTwiceThatManyTokensEffect extends ReplacementEffectImpl { public CreateTwiceThatManyTokensEffect() { - super(Duration.WhileOnBattlefield, Outcome.Copy); - staticText = "if one or more tokens would be created under your control, " + - "twice that many of those tokens are created instead"; + this(Duration.WhileOnBattlefield); + } + + public CreateTwiceThatManyTokensEffect(Duration duration) { + super(duration, Outcome.Copy); } private CreateTwiceThatManyTokensEffect(final CreateTwiceThatManyTokensEffect effect) { @@ -46,4 +49,13 @@ public class CreateTwiceThatManyTokensEffect extends ReplacementEffectImpl { return false; } + @Override + public String getText(Mode mode) { + if (staticText != null && !staticText.isEmpty()) { + return staticText; + } + return (duration.toString().isEmpty() ? "" : duration + ", ") + + "if one or more tokens would be created under your control, " + + "twice that many of those tokens are created instead"; + } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/ruleModifying/CombatDamageByToughnessEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ruleModifying/CombatDamageByToughnessEffect.java index a72ec5c1e6f..55f3369ef1f 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ruleModifying/CombatDamageByToughnessEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ruleModifying/CombatDamageByToughnessEffect.java @@ -2,6 +2,7 @@ package mage.abilities.effects.common.ruleModifying; import mage.abilities.Ability; +import mage.abilities.Mode; import mage.abilities.effects.ContinuousEffectImpl; import mage.constants.Duration; import mage.constants.Layer; @@ -20,11 +21,9 @@ public class CombatDamageByToughnessEffect extends ContinuousEffectImpl { private final boolean onlyControlled; public CombatDamageByToughnessEffect(FilterCreaturePermanent filter, boolean onlyControlled) { - super(Duration.WhileOnBattlefield, Outcome.Detriment); + super(Duration.WhileOnBattlefield, Layer.RulesEffects, SubLayer.NA, Outcome.Detriment); this.filter = filter; this.onlyControlled = onlyControlled; - staticText = "Each " + filter.getMessage() + (onlyControlled ? " you control" : "") + - " assigns combat damage equal to its toughness rather than its power"; } private CombatDamageByToughnessEffect(final CombatDamageByToughnessEffect effect) { @@ -39,11 +38,14 @@ public class CombatDamageByToughnessEffect extends ContinuousEffectImpl { } @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + public boolean apply(Game game, Ability source) { // Change the rule - FilterCreaturePermanent filterPermanent = filter.copy(); + FilterCreaturePermanent filterPermanent; if (onlyControlled) { + filterPermanent = filter.copy(); filterPermanent.add(new ControllerIdPredicate(source.getControllerId())); + } else { + filterPermanent = filter; } game.getCombat().setUseToughnessForDamage(true); game.getCombat().addUseToughnessForDamageFilter(filterPermanent); @@ -51,12 +53,16 @@ public class CombatDamageByToughnessEffect extends ContinuousEffectImpl { } @Override - public boolean apply(Game game, Ability source) { - return false; - } - - @Override - public boolean hasLayer(Layer layer) { - return layer == Layer.RulesEffects; + public String getText(Mode mode) { + if (staticText != null && !staticText.isEmpty()) { + return staticText; + } + StringBuilder sb = new StringBuilder("Each "); + sb.append(filter.getMessage()); + if (onlyControlled && !filter.getMessage().contains("you control")) { + sb.append(" you control"); + } + sb.append(" assigns combat damage equal to its toughness rather than its power"); + return sb.toString(); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/turn/AddExtraTurnControllerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/turn/AddExtraTurnControllerEffect.java index c37e36b4665..bdbc57dde3f 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/turn/AddExtraTurnControllerEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/turn/AddExtraTurnControllerEffect.java @@ -1,42 +1,51 @@ package mage.abilities.effects.common.turn; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.Mode; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.LoseGameSourceControllerEffect; import mage.constants.Duration; import mage.constants.Outcome; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.turn.TurnMod; import mage.players.Player; +import java.util.UUID; + /** * @author noxx */ public class AddExtraTurnControllerEffect extends OneShotEffect { + @FunctionalInterface + public static interface TurnModApplier { + void apply(UUID turnId, Ability source, Game game); + } + private final boolean loseGameAtEnd; + private final TurnModApplier turnModApplier; public AddExtraTurnControllerEffect() { this(false); } public AddExtraTurnControllerEffect(boolean loseGameAtEnd) { + this(loseGameAtEnd, null); + } + + public AddExtraTurnControllerEffect(boolean loseGameAtEnd, TurnModApplier turnModApplier) { super(loseGameAtEnd ? Outcome.AIDontUseIt : Outcome.ExtraTurn); this.loseGameAtEnd = loseGameAtEnd; - staticText = "take an extra turn after this one"; - if (loseGameAtEnd) { - staticText += ". At the beginning of that turn's end step, you lose the game"; - } + this.turnModApplier = turnModApplier; } public AddExtraTurnControllerEffect(final AddExtraTurnControllerEffect effect) { super(effect); this.loseGameAtEnd = effect.loseGameAtEnd; + this.turnModApplier = effect.turnModApplier; } @Override @@ -47,26 +56,37 @@ public class AddExtraTurnControllerEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - TurnMod extraTurn = new TurnMod(player.getId(), false); - game.getState().getTurnMods().add(extraTurn); - if (loseGameAtEnd) { - LoseGameDelayedTriggeredAbility delayedTriggeredAbility = new LoseGameDelayedTriggeredAbility(); - delayedTriggeredAbility.setConnectedTurnMod(extraTurn.getId()); - game.addDelayedTriggeredAbility(delayedTriggeredAbility, source); - } + if (player == null) { + return true; + } + TurnMod extraTurn = new TurnMod(player.getId(), false); + game.getState().getTurnMods().add(extraTurn); + if (loseGameAtEnd) { + game.addDelayedTriggeredAbility(new LoseGameDelayedTriggeredAbility(extraTurn.getId()), source); + } + if (turnModApplier != null) { + turnModApplier.apply(extraTurn.getId(), source, game); } return true; } + @Override + public String getText(Mode mode) { + if (staticText != null && !staticText.isEmpty()) { + return staticText; + } + return "take an extra turn after this one" + + (loseGameAtEnd ? ". At the beginning of that turn's end step, you lose the game" : ""); + } } class LoseGameDelayedTriggeredAbility extends DelayedTriggeredAbility { - private UUID connectedTurnMod; + private final UUID connectedTurnMod; - public LoseGameDelayedTriggeredAbility() { + public LoseGameDelayedTriggeredAbility(UUID connectedTurnMod) { super(new LoseGameSourceControllerEffect(), Duration.EndOfGame); + this.connectedTurnMod = connectedTurnMod; } public LoseGameDelayedTriggeredAbility(final LoseGameDelayedTriggeredAbility ability) { @@ -89,10 +109,6 @@ class LoseGameDelayedTriggeredAbility extends DelayedTriggeredAbility { return connectedTurnMod != null && connectedTurnMod.equals(game.getState().getTurnId()); } - public void setConnectedTurnMod(UUID connectedTurnMod) { - this.connectedTurnMod = connectedTurnMod; - } - @Override public String getRule() { return "At the beginning of that turn's end step, you lose the game"; diff --git a/Mage/src/main/java/mage/abilities/effects/keyword/InvestigateEffect.java b/Mage/src/main/java/mage/abilities/effects/keyword/InvestigateEffect.java index 8a6de130bf8..1ac4c6fc9b2 100644 --- a/Mage/src/main/java/mage/abilities/effects/keyword/InvestigateEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/keyword/InvestigateEffect.java @@ -2,6 +2,8 @@ package mage.abilities.effects.keyword; import mage.abilities.Ability; import mage.abilities.Mode; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.OneShotEffect; import mage.constants.Outcome; import mage.game.Game; @@ -14,13 +16,17 @@ import mage.util.CardUtil; */ public class InvestigateEffect extends OneShotEffect { - private final int amount; + private final DynamicValue amount; public InvestigateEffect() { this(1); } public InvestigateEffect(int amount) { + this(StaticValue.get(amount)); + } + + public InvestigateEffect(DynamicValue amount) { super(Outcome.Benefit); this.amount = amount; } @@ -32,8 +38,12 @@ public class InvestigateEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - new ClueArtifactToken().putOntoBattlefield(amount, game, source, source.getControllerId()); - for (int i = 0; i < amount; i++) { + int value = this.amount.calculate(game, source, this); + if (value < 1) { + return false; + } + new ClueArtifactToken().putOntoBattlefield(value, game, source, source.getControllerId()); + for (int i = 0; i < value; i++) { game.fireEvent(GameEvent.getEvent(GameEvent.EventType.INVESTIGATED, source.getSourceId(), source, source.getControllerId())); } return true; @@ -50,15 +60,20 @@ public class InvestigateEffect extends OneShotEffect { return staticText; } String message; - switch (amount) { - case 1: - message = ". (C"; - break; - case 2: - message = "twice. (To investigate, c"; - break; - default: - message = CardUtil.numberToText(amount) + " times. (To investigate, c"; + if (amount instanceof StaticValue) { + int value = ((StaticValue) amount).getValue(); + switch (value) { + case 1: + message = ". (C"; + break; + case 2: + message = " twice. (To investigate, c"; + break; + default: + message = ' ' + CardUtil.numberToText(value) + " times. (To investigate, c"; + } + } else { + message = " X times, where X is the " + amount.getMessage() + ". (To investigate, c"; } return "investigate" + message + "reate a colorless Clue artifact token " + "with \"{2}, Sacrifice this artifact: Draw a card.\")"; diff --git a/Mage/src/main/java/mage/abilities/hint/common/ControlArtifactAndEnchantmentHint.java b/Mage/src/main/java/mage/abilities/hint/common/ControlArtifactAndEnchantmentHint.java new file mode 100644 index 00000000000..e04c0a7bf31 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/hint/common/ControlArtifactAndEnchantmentHint.java @@ -0,0 +1,34 @@ +package mage.abilities.hint.common; + +import mage.abilities.Ability; +import mage.abilities.hint.Hint; +import mage.filter.StaticFilters; +import mage.game.Game; + +/** + * @author TheElk801 + */ +public enum ControlArtifactAndEnchantmentHint implements Hint { + instance; + + @Override + public String getText(Game game, Ability ability) { + boolean artifact = game.getBattlefield().contains( + StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT, ability, game, 1 + ); + boolean enchantment = game.getBattlefield().contains( + StaticFilters.FILTER_CONTROLLED_PERMANENT_ENCHANTMENT, ability, game, 1 + ); + if (artifact) { + return "You control and artifact" + (enchantment ? " and an enchantment" : ""); + } else if (enchantment) { + return "You control an enchantment"; + } + return null; + } + + @Override + public ControlArtifactAndEnchantmentHint copy() { + return this; + } +} diff --git a/Mage/src/main/java/mage/abilities/hint/common/DayNightHint.java b/Mage/src/main/java/mage/abilities/hint/common/DayNightHint.java new file mode 100644 index 00000000000..a4b56be021d --- /dev/null +++ b/Mage/src/main/java/mage/abilities/hint/common/DayNightHint.java @@ -0,0 +1,40 @@ +package mage.abilities.hint.common; + +import mage.abilities.Ability; +import mage.abilities.hint.Hint; +import mage.game.Game; +import mage.watchers.common.CastSpellLastTurnWatcher; + +/** + * @author TheElk801 + */ +public enum DayNightHint implements Hint { + instance; + + @Override + public String getText(Game game, Ability ability) { + if (!game.hasDayNight()) { + return "It's neither day nor night."; + } + boolean isDay = game.checkDayNight(true); + int spellsThisTurn = game + .getState() + .getWatcher(CastSpellLastTurnWatcher.class) + .getActivePlayerThisTurnCount(); + StringBuilder sb = new StringBuilder("It's currently "); + sb.append(isDay ? "day" : "night"); + sb.append(", active player has cast "); + sb.append(spellsThisTurn); + sb.append(" spells this turn. It will "); + sb.append((isDay ? spellsThisTurn == 0 : spellsThisTurn >= 2) ? "" : "not"); + sb.append(" become "); + sb.append(isDay ? "night" : "day"); + sb.append(" next turn."); + return sb.toString(); + } + + @Override + public DayNightHint copy() { + return this; + } +} diff --git a/Mage/src/main/java/mage/abilities/hint/common/MyTurnHint.java b/Mage/src/main/java/mage/abilities/hint/common/MyTurnHint.java index aab11d56241..0ce99226dc5 100644 --- a/Mage/src/main/java/mage/abilities/hint/common/MyTurnHint.java +++ b/Mage/src/main/java/mage/abilities/hint/common/MyTurnHint.java @@ -12,7 +12,7 @@ import mage.game.Game; public enum MyTurnHint implements Hint { instance; - private static final ConditionHint hint = new ConditionHint(MyTurnCondition.instance, "It's my turn"); + private static final ConditionHint hint = new ConditionHint(MyTurnCondition.instance, "It's your turn"); @Override public String getText(Game game, Ability ability) { diff --git a/Mage/src/main/java/mage/abilities/hint/common/NotMyTurnHint.java b/Mage/src/main/java/mage/abilities/hint/common/NotMyTurnHint.java index 718d8492c7e..9b95d0666e1 100644 --- a/Mage/src/main/java/mage/abilities/hint/common/NotMyTurnHint.java +++ b/Mage/src/main/java/mage/abilities/hint/common/NotMyTurnHint.java @@ -12,7 +12,7 @@ import mage.game.Game; public enum NotMyTurnHint implements Hint { instance; - private static final ConditionHint hint = new ConditionHint(NotMyTurnCondition.instance, "It's not my turn"); + private static final ConditionHint hint = new ConditionHint(NotMyTurnCondition.instance, "It's not your turn"); @Override public String getText(Game game, Ability ability) { diff --git a/Mage/src/main/java/mage/abilities/hint/common/NightHint.java b/Mage/src/main/java/mage/abilities/hint/common/OpponentsTurnHint.java similarity index 59% rename from Mage/src/main/java/mage/abilities/hint/common/NightHint.java rename to Mage/src/main/java/mage/abilities/hint/common/OpponentsTurnHint.java index afc7f2631dd..45025733975 100644 --- a/Mage/src/main/java/mage/abilities/hint/common/NightHint.java +++ b/Mage/src/main/java/mage/abilities/hint/common/OpponentsTurnHint.java @@ -1,7 +1,7 @@ package mage.abilities.hint.common; import mage.abilities.Ability; -import mage.abilities.condition.common.NightCondition; +import mage.abilities.condition.common.OpponentsTurnCondition; import mage.abilities.hint.ConditionHint; import mage.abilities.hint.Hint; import mage.game.Game; @@ -9,11 +9,9 @@ import mage.game.Game; /** * @author TheElk801 */ -public enum NightHint implements Hint { +public enum OpponentsTurnHint implements Hint { instance; - private static final Hint hint = new ConditionHint( - NightCondition.instance, "It's currently night" - ); + private static final ConditionHint hint = new ConditionHint(OpponentsTurnCondition.instance, "It's an opponent's turn"); @Override public String getText(Game game, Ability ability) { @@ -22,6 +20,6 @@ public enum NightHint implements Hint { @Override public Hint copy() { - return this; + return instance; } } diff --git a/Mage/src/main/java/mage/abilities/keyword/AwakenAbility.java b/Mage/src/main/java/mage/abilities/keyword/AwakenAbility.java index 381f7c42a23..9148905463f 100644 --- a/Mage/src/main/java/mage/abilities/keyword/AwakenAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/AwakenAbility.java @@ -103,7 +103,7 @@ public class AwakenAbility extends SpellAbility { } } if (targetId != null) { - FixedTarget fixedTarget = new FixedTarget(targetId); + FixedTarget fixedTarget = new FixedTarget(targetId, game); ContinuousEffect continuousEffect = new BecomesCreatureTargetEffect(new AwakenElementalToken(), false, true, Duration.Custom); continuousEffect.setTargetPointer(fixedTarget); game.addEffect(continuousEffect, source); diff --git a/Mage/src/main/java/mage/abilities/keyword/BattleCryAbility.java b/Mage/src/main/java/mage/abilities/keyword/BattleCryAbility.java index 4203ecfd058..1057188981f 100644 --- a/Mage/src/main/java/mage/abilities/keyword/BattleCryAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/BattleCryAbility.java @@ -7,8 +7,10 @@ import mage.filter.common.FilterAttackingCreature; public class BattleCryAbility extends AttacksTriggeredAbility { + private static final FilterAttackingCreature filter = new FilterAttackingCreature(); + public BattleCryAbility() { - super(new BoostControlledEffect(1, 0, Duration.EndOfTurn, new FilterAttackingCreature(), true), false); + super(new BoostControlledEffect(1, 0, Duration.EndOfTurn, filter, true), false); } public BattleCryAbility(final BattleCryAbility ability) { diff --git a/Mage/src/main/java/mage/abilities/keyword/BestowAbility.java b/Mage/src/main/java/mage/abilities/keyword/BestowAbility.java index 0f832613a23..d80fcba2ae8 100644 --- a/Mage/src/main/java/mage/abilities/keyword/BestowAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/BestowAbility.java @@ -117,6 +117,7 @@ public class BestowAbility extends SpellAbility { if (permanent != null) { MageObject basicObject = permanent.getBasicMageObject(game); if (basicObject != null) { + game.checkStateAndTriggered(); // Bug #8157 basicObject.getSubtype().remove(SubType.AURA); basicObject.addCardType(CardType.CREATURE); } diff --git a/Mage/src/main/java/mage/abilities/keyword/ChannelAbility.java b/Mage/src/main/java/mage/abilities/keyword/ChannelAbility.java index 78ed122452e..3ca999b0b8f 100644 --- a/Mage/src/main/java/mage/abilities/keyword/ChannelAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/ChannelAbility.java @@ -1,5 +1,3 @@ - - package mage.abilities.keyword; import mage.abilities.ActivatedAbilityImpl; @@ -18,7 +16,7 @@ public class ChannelAbility extends ActivatedAbilityImpl { } public ChannelAbility(String manaString, Effect effect, TimingRule timing) { - super(Zone.HAND, effect, new ManaCostsImpl(manaString)); + super(Zone.HAND, effect, new ManaCostsImpl<>(manaString)); this.addCost(new DiscardSourceCost()); this.timing = timing; this.setAbilityWord(AbilityWord.CHANNEL); @@ -40,6 +38,4 @@ public class ChannelAbility extends ActivatedAbilityImpl { } return super.getRule(); } - } - diff --git a/Mage/src/main/java/mage/abilities/keyword/CleaveAbility.java b/Mage/src/main/java/mage/abilities/keyword/CleaveAbility.java new file mode 100644 index 00000000000..2bccbe68404 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/keyword/CleaveAbility.java @@ -0,0 +1,41 @@ +package mage.abilities.keyword; + +import mage.abilities.SpellAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.Effect; +import mage.cards.Card; +import mage.constants.SpellAbilityType; +import mage.constants.TimingRule; + +/** + * @author TheElk801 + */ +public class CleaveAbility extends SpellAbility { + + public CleaveAbility(Card card, Effect effect, String manaString) { + super(new ManaCostsImpl<>(manaString), card.getName() + " with cleave"); + this.spellAbilityType = SpellAbilityType.BASE_ALTERNATE; + this.addEffect(effect); + this.setRuleAtTheTop(true); + this.timing = (card.isSorcery(null) ? TimingRule.SORCERY : TimingRule.INSTANT); + } + + public CleaveAbility(final CleaveAbility ability) { + super(ability); + } + + @Override + public CleaveAbility copy() { + return new CleaveAbility(this); + } + + @Override + public String getRule(boolean all) { + return getRule(); + } + + @Override + public String getRule() { + return "Cleave " + getManaCostsToPay().getText() + " (You may cast this spell for its cleave cost. If you do, remove the words in square brackets.)"; + } +} diff --git a/Mage/src/main/java/mage/abilities/keyword/CompleatedAbility.java b/Mage/src/main/java/mage/abilities/keyword/CompleatedAbility.java new file mode 100644 index 00000000000..3a777f6e333 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/keyword/CompleatedAbility.java @@ -0,0 +1,42 @@ +package mage.abilities.keyword; + +import mage.abilities.MageSingleton; +import mage.abilities.StaticAbility; +import mage.constants.Zone; + +import java.io.ObjectStreamException; + +/** + * @author TheElk801 + */ +public class CompleatedAbility extends StaticAbility implements MageSingleton { + + private static final CompleatedAbility instance; + + static { + instance = new CompleatedAbility(); + } + + private Object readResolve() throws ObjectStreamException { + return instance; + } + + public static CompleatedAbility getInstance() { + return instance; + } + + private CompleatedAbility() { + super(Zone.ALL, null); + } + + @Override + public String getRule() { + return "compleated"; + } + + @Override + public CompleatedAbility copy() { + return instance; + } + +} diff --git a/Mage/src/main/java/mage/abilities/keyword/ConspireAbility.java b/Mage/src/main/java/mage/abilities/keyword/ConspireAbility.java index 43332eb8b5a..7532725b295 100644 --- a/Mage/src/main/java/mage/abilities/keyword/ConspireAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/ConspireAbility.java @@ -3,13 +3,11 @@ package mage.abilities.keyword; import mage.abilities.Ability; import mage.abilities.SpellAbility; import mage.abilities.StaticAbility; -import mage.abilities.TriggeredAbilityImpl; import mage.abilities.costs.*; import mage.abilities.costs.common.TapTargetCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; +import mage.abilities.effects.common.CastSourceTriggeredAbility; +import mage.abilities.effects.common.CopySourceSpellEffect; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; @@ -21,10 +19,10 @@ import mage.game.events.GameEvent; import mage.game.stack.Spell; import mage.players.Player; import mage.target.common.TargetControlledPermanent; +import mage.util.CardUtil; -import java.util.HashSet; import java.util.Iterator; -import java.util.Set; +import java.util.Objects; import java.util.UUID; /* @@ -45,62 +43,58 @@ import java.util.UUID; public class ConspireAbility extends StaticAbility implements OptionalAdditionalSourceCosts { private static final String keywordText = "Conspire"; - private static final FilterControlledPermanent filter = new FilterControlledPermanent("untapped creatures you control that share a color with it"); - protected static final String CONSPIRE_ACTIVATION_KEY = "ConspireActivation"; + private static final FilterControlledPermanent filter + = new FilterControlledPermanent("untapped creatures you control that share a color with it"); static { filter.add(TappedPredicate.UNTAPPED); - filter.add(new SharesColorWithSourcePredicate()); + filter.add(SharesColorWithSourcePredicate.instance); filter.add(CardType.CREATURE.getPredicate()); } public enum ConspireTargets { - NONE, - ONE, - MORE + NONE(""), + ONE(" and you may choose a new target for the copy"), + MORE(" and you may choose new targets for the copy"); + private final String message; + + ConspireTargets(String message) { + this.message = message; + } + + public String getReminder() { + return "as you cast this spell, you may tap two untapped creatures you control " + + "that share a color with it. When you do, copy it" + message; + } } private final UUID conspireId; - private String reminderText; - private OptionalAdditionalCost conspireCost; + private UUID addedById = null; + private final String reminderText; + private final OptionalAdditionalCost conspireCost; /** * Unique Id for a ConspireAbility but may not change while a continuous * effect gives Conspire * - * @param conspireId * @param conspireTargets controls the content of the reminder text */ - public ConspireAbility(UUID conspireId, ConspireTargets conspireTargets) { + public ConspireAbility(ConspireTargets conspireTargets) { super(Zone.STACK, null); - this.conspireId = conspireId; - switch (conspireTargets) { - case NONE: - reminderText = "As you cast this spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it."; - break; - case ONE: - reminderText = "As you cast this spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it and you may choose a new target for the copy."; - break; - case MORE: - reminderText = "As you cast this spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it and you may choose new targets for the copy."; - break; - } - - Cost cost = new TapTargetCost(new TargetControlledPermanent(2, 2, filter, true)); - cost.setText(""); - addConspireCostAndSetup(new OptionalAdditionalCostImpl(keywordText, " ", reminderText, cost)); - - addSubAbility(new ConspireTriggeredAbility(conspireId)); - } - - private void addConspireCostAndSetup(OptionalAdditionalCost newCost) { - this.conspireCost = newCost; + this.conspireId = UUID.randomUUID(); + reminderText = conspireTargets.getReminder(); + this.conspireCost = new OptionalAdditionalCostImpl( + keywordText, " ", reminderText, + new TapTargetCost(new TargetControlledPermanent(2, filter)) + ); this.conspireCost.setCostType(VariableCostType.ADDITIONAL); + this.addSubAbility(new ConspireTriggeredAbility(conspireId)); } public ConspireAbility(final ConspireAbility ability) { super(ability); this.conspireId = ability.conspireId; + this.addedById = ability.addedById; this.conspireCost = ability.conspireCost.copy(); this.reminderText = ability.reminderText; } @@ -113,66 +107,41 @@ public class ConspireAbility extends StaticAbility implements OptionalAdditional @Override public void addCost(Cost cost) { if (conspireCost != null) { - ((Costs) conspireCost).add(cost); + ((Costs) conspireCost).add(cost); } } - public UUID getConspireId() { - return conspireId; - } - @Override public boolean isActivated() { throw new UnsupportedOperationException("Use ConspireAbility.isActivated(Ability ability, Game game) method instead!"); } - public boolean isActivated(Ability ability, Game game) { - Set activations = (Set) game.getState().getValue(CONSPIRE_ACTIVATION_KEY + ability.getId()); - if (activations != null) { - return activations.contains(getConspireId()); - } - return false; - } - @Override public void addOptionalAdditionalCosts(Ability ability, Game game) { - if (ability instanceof SpellAbility) { - Player player = game.getPlayer(getControllerId()); - if (player != null) { - resetConspire(ability, game); - // AI supports conspire - if (conspireCost.canPay(ability, this, getControllerId(), game) - && player.chooseUse(Outcome.Benefit, "Pay " + conspireCost.getText(false) + " ?", ability, game)) { - activateConspire(ability, game); - for (Iterator it = ((Costs) conspireCost).iterator(); it.hasNext(); ) { - Cost cost = (Cost) it.next(); - if (cost instanceof ManaCostsImpl) { - ability.getManaCostsToPay().add((ManaCostsImpl) cost.copy()); - } else { - ability.getCosts().add(cost.copy()); - } - } - } + if (!(ability instanceof SpellAbility)) { + return; + } + Player player = game.getPlayer(getControllerId()); + if (player == null) { + return; + } + // AI supports conspire + if (!conspireCost.canPay(ability, this, getControllerId(), game) + || !player.chooseUse(Outcome.Benefit, "Pay " + + conspireCost.getText(false) + " ?", ability, game)) { + return; + } + ability.getEffects().setValue("ConspireActivation" + conspireId + addedById, true); + for (Iterator it = ((Costs) conspireCost).iterator(); it.hasNext(); ) { + Cost cost = (Cost) it.next(); + if (cost instanceof ManaCostsImpl) { + ability.getManaCostsToPay().add((ManaCostsImpl) cost.copy()); + } else { + ability.getCosts().add(cost.copy()); } } } - private void activateConspire(Ability ability, Game game) { - Set activations = (Set) game.getState().getValue(CONSPIRE_ACTIVATION_KEY + ability.getId()); - if (activations == null) { - activations = new HashSet<>(); - game.getState().setValue(CONSPIRE_ACTIVATION_KEY + ability.getId(), activations); - } - activations.add(getConspireId()); - } - - private void resetConspire(Ability ability, Game game) { - Set activations = (Set) game.getState().getValue(CONSPIRE_ACTIVATION_KEY + ability.getId()); - if (activations != null) { - activations.remove(getConspireId()); - } - } - @Override public String getRule() { StringBuilder sb = new StringBuilder(); @@ -185,35 +154,34 @@ public class ConspireAbility extends StaticAbility implements OptionalAdditional @Override public String getCastMessageSuffix() { - if (conspireCost != null) { - return conspireCost.getCastSuffixMessage(0); - } else { - return ""; - } + return conspireCost != null ? conspireCost.getCastSuffixMessage(0) : ""; } - public String getReminderText() { - if (conspireCost != null) { - return conspireCost.getReminderText(); - } else { - return ""; - } + public ConspireAbility setAddedById(UUID addedById) { + this.addedById = addedById; + CardUtil.castStream( + this.subAbilities.stream(), + ConspireTriggeredAbility.class + ).forEach(ability -> ability.setAddedById(addedById)); + return this; } } -class ConspireTriggeredAbility extends TriggeredAbilityImpl { +class ConspireTriggeredAbility extends CastSourceTriggeredAbility { private final UUID conspireId; + private UUID addedById = null; public ConspireTriggeredAbility(UUID conspireId) { - super(Zone.STACK, new ConspireEffect()); - this.conspireId = conspireId; + super(new CopySourceSpellEffect(), false); this.setRuleVisible(false); + this.conspireId = conspireId; } private ConspireTriggeredAbility(final ConspireTriggeredAbility ability) { super(ability); this.conspireId = ability.conspireId; + this.addedById = ability.addedById; } @Override @@ -221,74 +189,27 @@ class ConspireTriggeredAbility extends TriggeredAbilityImpl { return new ConspireTriggeredAbility(this); } - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.SPELL_CAST; - } - @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getSourceId().equals(getSourceId())) { - Spell spell = game.getStack().getSpell(event.getSourceId()); - for (Ability ability : spell.getAbilities(game)) { - if (ability instanceof ConspireAbility - && ((ConspireAbility) ability).getConspireId().equals(getConspireId())) { - if (((ConspireAbility) ability).isActivated(spell.getSpellAbility(), game)) { - for (Effect effect : this.getEffects()) { - if (effect instanceof ConspireEffect) { - ((ConspireEffect) effect).setConspiredSpell(spell); - } - } - return true; - } - } - } + if (!super.checkTrigger(event, game)) { + return false; } - return false; - } - - public UUID getConspireId() { - return conspireId; + Spell spell = game.getStack().getSpell(event.getSourceId()); + return spell != null + && spell + .getSpellAbility() + .getEffects() + .stream() + .map(effect -> effect.getValue("ConspireActivation" + conspireId + addedById)) + .anyMatch(Objects::nonNull); } @Override public String getRule() { return "When you pay the conspire costs, copy it and you may choose a new target for the copy."; } -} -class ConspireEffect extends OneShotEffect { - - private Spell conspiredSpell; - - public ConspireEffect() { - super(Outcome.Copy); - } - - public ConspireEffect(final ConspireEffect effect) { - super(effect); - this.conspiredSpell = effect.conspiredSpell; - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null && conspiredSpell != null) { - Card card = game.getCard(conspiredSpell.getSourceId()); - if (card != null) { - conspiredSpell.createCopyOnStack(game, source, source.getControllerId(), true); - return true; - } - } - return false; - } - - public void setConspiredSpell(Spell conspiredSpell) { - this.conspiredSpell = conspiredSpell; - } - - @Override - public ConspireEffect copy() { - return new ConspireEffect(this); + public void setAddedById(UUID addedById) { + this.addedById = addedById; } } diff --git a/Mage/src/main/java/mage/abilities/keyword/CrewAbility.java b/Mage/src/main/java/mage/abilities/keyword/CrewAbility.java index 5f0ba42fdd3..5aa9f43eb6a 100644 --- a/Mage/src/main/java/mage/abilities/keyword/CrewAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/CrewAbility.java @@ -1,11 +1,14 @@ package mage.abilities.keyword; -import mage.MageInt; import mage.abilities.Ability; +import mage.abilities.common.CrewIncreasedPowerAbility; import mage.abilities.common.CrewWithToughnessAbility; import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.Cost; import mage.abilities.costs.CostImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.InfoEffect; import mage.abilities.effects.common.continuous.AddCardTypeSourceEffect; import mage.abilities.hint.HintUtils; import mage.abilities.icon.abilities.CrewAbilityIcon; @@ -14,12 +17,15 @@ import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.TappedPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; +import mage.players.Player; import mage.target.Target; import mage.target.common.TargetControlledCreaturePermanent; +import mage.util.CardUtil; import java.awt.*; import java.util.Objects; @@ -33,10 +39,22 @@ public class CrewAbility extends SimpleActivatedAbility { private final int value; public CrewAbility(int value) { - super(Zone.BATTLEFIELD, new AddCardTypeSourceEffect(Duration.EndOfTurn, CardType.ARTIFACT), new CrewCost(value)); - this.addEffect(new AddCardTypeSourceEffect(Duration.EndOfTurn, CardType.ARTIFACT, CardType.CREATURE)); + this(value, null); + } + + public CrewAbility(int value, Cost altCost) { + super(Zone.BATTLEFIELD, new AddCardTypeSourceEffect( + Duration.EndOfTurn, CardType.ARTIFACT, CardType.CREATURE + ), new CrewCost(value, altCost)); + this.addEffect(new CrewEventEffect()); this.addIcon(CrewAbilityIcon.instance); this.value = value; + if (altCost != null) { + this.addSubAbility(new SimpleStaticAbility(Zone.ALL, new InfoEffect( + "you may " + CardUtil.addCostVerb(altCost.getText()) + + " rather than pay {this}'s crew cost" + ))); + } } public CrewAbility(final CrewAbility ability) { @@ -51,31 +69,82 @@ public class CrewAbility extends SimpleActivatedAbility { @Override public String getRule() { - return "Crew " + value + " (Tap any number of creatures you control with total power " + value + " or more: This Vehicle becomes an artifact creature until end of turn.)"; + return "Crew " + value + " (Tap any number of creatures you control with total power " + + value + " or more: This Vehicle becomes an artifact creature until end of turn.)"; + } +} + +class CrewEventEffect extends OneShotEffect { + + CrewEventEffect() { + super(Outcome.Benefit); + } + + private CrewEventEffect(final CrewEventEffect effect) { + super(effect); + } + + @Override + public CrewEventEffect copy() { + return new CrewEventEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + if (source.getSourcePermanentIfItStillExists(game) != null) { + game.fireEvent(GameEvent.getEvent( + GameEvent.EventType.VEHICLE_CREWED, + source.getSourceId(), + source, source.getControllerId() + )); + } + return true; } } class CrewCost extends CostImpl { - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("untapped creature you control"); + private static final FilterControlledCreaturePermanent filter + = new FilterControlledCreaturePermanent("another untapped creature you control"); static { filter.add(TappedPredicate.UNTAPPED); + filter.add(AnotherPredicate.instance); } private final int value; + private final Cost altCost; - CrewCost(int value) { + CrewCost(int value, Cost altCost) { this.value = value; + this.altCost = altCost; } - CrewCost(final CrewCost cost) { + private CrewCost(final CrewCost cost) { super(cost); this.value = cost.value; + this.altCost = cost.altCost != null ? cost.altCost.copy() : null; + } + + private boolean handleAltCost(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) { + if (altCost == null || !altCost.canPay(ability, source, controllerId, game)) { + return false; + } + Player player = game.getPlayer(controllerId); + String message = CardUtil.getTextWithFirstCharUpperCase( + CardUtil.addCostVerb(altCost.getText()) + ) + " rather than the crew cost?"; + return player != null + && player.chooseUse(Outcome.Benefit, message, source, game) + && altCost.pay(ability, game, source, controllerId, noMana, costToPay); } @Override public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) { + if (handleAltCost(ability, game, source, controllerId, noMana, costToPay)) { + paid = true; + return true; + } Target target = new TargetControlledCreaturePermanent(0, Integer.MAX_VALUE, filter, true) { @Override public String getMessage() { @@ -120,6 +189,9 @@ class CrewCost extends CostImpl { @Override public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) { + if (altCost != null && altCost.canPay(ability, source, controllerId, game)) { + return true; + } int sumPower = 0; for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filter, controllerId, game)) { int powerToAdd = getCrewPower(permanent, game); @@ -140,14 +212,12 @@ class CrewCost extends CostImpl { } private int getCrewPower(Permanent permanent, Game game) { - MageInt crewPowerSource = null; - if (permanent.hasAbility(CrewWithToughnessAbility.getInstance(), game)) { - crewPowerSource = permanent.getToughness(); + return permanent.getToughness().getValue(); + } else if (permanent.getAbilities(game).containsClass(CrewIncreasedPowerAbility.class)) { + return permanent.getPower().getValue() + 2; } else { - crewPowerSource = permanent.getPower(); + return permanent.getPower().getValue(); } - - return crewPowerSource.getValue(); } } diff --git a/Mage/src/main/java/mage/abilities/keyword/DashAbility.java b/Mage/src/main/java/mage/abilities/keyword/DashAbility.java index 9e89a3c160b..67bd739b0a5 100644 --- a/Mage/src/main/java/mage/abilities/keyword/DashAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/DashAbility.java @@ -1,8 +1,5 @@ package mage.abilities.keyword; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.SpellAbility; @@ -10,13 +7,7 @@ import mage.abilities.StaticAbility; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.condition.common.DashedCondition; -import mage.abilities.condition.common.SourceOnBattlefieldCondition; -import mage.abilities.costs.AlternativeCost2; -import mage.abilities.costs.AlternativeCost2Impl; -import mage.abilities.costs.AlternativeSourceCosts; -import mage.abilities.costs.Cost; -import mage.abilities.costs.Costs; -import mage.abilities.costs.CostsImpl; +import mage.abilities.costs.*; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.OneShotEffect; @@ -30,8 +21,11 @@ import mage.game.Game; import mage.players.Player; import mage.target.targetpointer.FixedTarget; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + /** - * * @author LevelX2 */ public class DashAbility extends StaticAbility implements AlternativeSourceCosts { @@ -112,11 +106,11 @@ public class DashAbility extends StaticAbility implements AlternativeSourceCosts for (AlternativeCost2 dashCost : alternativeSourceCosts) { if (dashCost.canPay(ability, this, player.getId(), game) && player.chooseUse(Outcome.Benefit, KEYWORD - + " the creature for " + dashCost.getText(true) + " ?", ability, game)) { + + " the creature for " + dashCost.getText(true) + " ?", ability, game)) { activateDash(dashCost, game); ability.getManaCostsToPay().clear(); ability.getCosts().clear(); - for (Iterator it = ((Costs) dashCost).iterator(); it.hasNext();) { + for (Iterator it = ((Costs) dashCost).iterator(); it.hasNext(); ) { Cost cost = (Cost) it.next(); if (cost instanceof ManaCostsImpl) { ability.getManaCostsToPay().add((ManaCostsImpl) cost.copy()); @@ -208,7 +202,7 @@ class DashAddDelayedTriggeredAbilityEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { if (game.getPermanentEntering(source.getSourceId()) != null) { OneShotEffect returnToHandEffect = new ReturnToHandTargetEffect(); - ConditionalOneShotEffect mustBeOnBattlefieldToReturn = new ConditionalOneShotEffect(returnToHandEffect, SourceOnBattlefieldCondition.instance); + ConditionalOneShotEffect mustBeOnBattlefieldToReturn = new ConditionalOneShotEffect(returnToHandEffect, DashAddDelayedTriggeredAbilityEffect::check); mustBeOnBattlefieldToReturn.setText("return the dashed creature from the battlefield to its owner's hand"); // init target pointer now because the dashed creature will only be returned from battlefield zone (now in entering state so zone change counter is not raised yet) mustBeOnBattlefieldToReturn.setTargetPointer(new FixedTarget(source.getSourceId(), game.getState().getZoneChangeCounter(source.getSourceId()) + 1)); @@ -218,4 +212,8 @@ class DashAddDelayedTriggeredAbilityEffect extends OneShotEffect { } return false; } + + static boolean check(Game game, Ability source) { + return game.getState().getZoneChangeCounter(source.getSourceId()) == source.getSourceObjectZoneChangeCounter() + 1; + } } diff --git a/Mage/src/main/java/mage/abilities/keyword/DayboundAbility.java b/Mage/src/main/java/mage/abilities/keyword/DayboundAbility.java index 1e8761d6b4c..2971b12b291 100644 --- a/Mage/src/main/java/mage/abilities/keyword/DayboundAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/DayboundAbility.java @@ -1,16 +1,21 @@ package mage.abilities.keyword; +import mage.abilities.Ability; import mage.abilities.StaticAbility; -import mage.constants.Zone; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.hint.common.DayNightHint; +import mage.constants.*; +import mage.game.Game; /** * @author TheElk801 - * TODO: Implement this */ public class DayboundAbility extends StaticAbility { public DayboundAbility() { - super(Zone.BATTLEFIELD, null); + super(Zone.BATTLEFIELD, new DayboundEffect()); + this.addHint(DayNightHint.instance); + this.addSubAbility(new TransformAbility()); } private DayboundAbility(final DayboundAbility ability) { @@ -27,3 +32,27 @@ public class DayboundAbility extends StaticAbility { return new DayboundAbility(this); } } + +class DayboundEffect extends ContinuousEffectImpl { + + DayboundEffect() { + super(Duration.WhileOnBattlefield, Layer.PlayerEffects, SubLayer.NA, Outcome.Benefit); + } + + private DayboundEffect(final DayboundEffect effect) { + super(effect); + } + + @Override + public DayboundEffect copy() { + return new DayboundEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + if (!game.hasDayNight()) { + game.setDaytime(true); + } + return true; + } +} diff --git a/Mage/src/main/java/mage/abilities/keyword/DisturbAbility.java b/Mage/src/main/java/mage/abilities/keyword/DisturbAbility.java index be956cf33d7..7a87c5dea38 100644 --- a/Mage/src/main/java/mage/abilities/keyword/DisturbAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/DisturbAbility.java @@ -1,22 +1,129 @@ package mage.abilities.keyword; +import mage.abilities.Ability; import mage.abilities.SpellAbility; -import mage.abilities.costs.Cost; -import mage.constants.SpellAbilityType; -import mage.constants.TimingRule; -import mage.constants.Zone; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.cards.Card; +import mage.constants.*; +import mage.game.Game; +import mage.game.stack.Spell; + +import java.util.UUID; /** - * @author weirddan455 - * TODO: this is currently implemented in a pull request + * 702.146. Disturb + *

+ * 702.146a Disturb is an ability found on the front face of some transforming double-faced cards + * (see rule 712, “Double-Faced Cards”). “Disturb [cost]” means “You may cast this card + * transformed from your graveyard by paying [cost] rather than its mana cost.” See + * rule 712.4b. + *

+ * 702.146b A resolving transforming double-faced spell that was cast using its disturb + * ability enters the battlefield with its back face up. + * + * @author weirddan455, JayDi85 */ public class DisturbAbility extends SpellAbility { - public DisturbAbility(Cost cost) { - this(cost, TimingRule.SORCERY); + private final String manaCost; + private SpellAbility spellAbilityToResolve; + + public DisturbAbility(Card card, String manaCost) { + super(card.getSpellAbility()); + this.newId(); + + // verify check + if (card.getSecondCardFace() == null || card.getSecondCardFace().getClass().equals(card.getClass())) { + throw new IllegalArgumentException("Wrong code usage. Disturb ability can be added to double faces card only (main side)."); + } + + this.setCardName(card.getSecondCardFace().getName() + " with Disturb"); + this.zone = Zone.GRAVEYARD; + this.spellAbilityType = SpellAbilityType.BASE_ALTERNATE; + this.spellAbilityCastMode = SpellAbilityCastMode.DISTURB; + + this.manaCost = manaCost; + this.getManaCosts().clear(); + this.getManaCostsToPay().clear(); + this.addManaCost(new ManaCostsImpl(manaCost)); + this.addSubAbility(new TransformAbility()); } - public DisturbAbility(Cost cost, TimingRule timingRule) { - super(null, "", Zone.GRAVEYARD, SpellAbilityType.BASE_ALTERNATE); + private DisturbAbility(final DisturbAbility ability) { + super(ability); + this.manaCost = ability.manaCost; + this.spellAbilityToResolve = ability.spellAbilityToResolve; + } + + @Override + public DisturbAbility copy() { + return new DisturbAbility(this); + } + + @Override + public boolean activate(Game game, boolean noMana) { + if (super.activate(game, noMana)) { + game.getState().setValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + getSourceId(), Boolean.TRUE); + // TODO: must be removed after transform cards (one side) migrated to MDF engine (multiple sides) + game.addEffect(new DisturbEffect(), this); + return true; + } + return false; + } + + @Override + public ActivationStatus canActivate(UUID playerId, Game game) { + if (super.canActivate(playerId, game).canActivate()) { + Card card = game.getCard(getSourceId()); + if (card != null && game.getState().getZone(card.getId()) == Zone.GRAVEYARD) { + return card.getSpellAbility().canActivate(playerId, game); + } + } + return ActivationStatus.getFalse(); + } + + @Override + public String getRule(boolean all) { + return this.getRule(); + } + + @Override + public String getRule() { + return "Disturb " + this.manaCost + + " (You may cast this card transformed from your graveyard for its disturb cost.)"; } } + +class DisturbEffect extends ContinuousEffectImpl { + + public DisturbEffect() { + super(Duration.WhileOnStack, Layer.CopyEffects_1, SubLayer.CopyEffects_1a, Outcome.BecomeCreature); + staticText = ""; + } + + private DisturbEffect(final DisturbEffect effect) { + super(effect); + } + + @Override + public DisturbEffect copy() { + return new DisturbEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Spell spell = game.getSpell(source.getSourceId()); + if (spell == null || spell.getFromZone() != Zone.GRAVEYARD) { + return false; + } + + if (spell.getCard().getSecondCardFace() == null) { + return false; + } + + // simulate another side as new card (another code part in spell constructor) + TransformAbility.transformCardSpellDynamic(spell, spell.getCard().getSecondCardFace(), game); + return true; + } +} \ No newline at end of file diff --git a/Mage/src/main/java/mage/abilities/keyword/EquipAbility.java b/Mage/src/main/java/mage/abilities/keyword/EquipAbility.java index 1bc91d6cfcc..f3dcfe00fd4 100644 --- a/Mage/src/main/java/mage/abilities/keyword/EquipAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/EquipAbility.java @@ -3,7 +3,7 @@ package mage.abilities.keyword; import mage.abilities.ActivatedAbilityImpl; import mage.abilities.costs.Cost; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.EquipEffect; +import mage.abilities.effects.common.AttachEffect; import mage.constants.Outcome; import mage.constants.TimingRule; import mage.constants.Zone; @@ -26,7 +26,7 @@ public class EquipAbility extends ActivatedAbilityImpl { } public EquipAbility(Outcome outcome, Cost cost, Target target) { - super(Zone.BATTLEFIELD, new EquipEffect(outcome), cost); + super(Zone.BATTLEFIELD, new AttachEffect(outcome, "Equip"), cost); this.addTarget(target); this.timing = TimingRule.SORCERY; } diff --git a/Mage/src/main/java/mage/abilities/keyword/EvolveAbility.java b/Mage/src/main/java/mage/abilities/keyword/EvolveAbility.java index 5b21034d348..1fe14aaf1bd 100644 --- a/Mage/src/main/java/mage/abilities/keyword/EvolveAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/EvolveAbility.java @@ -88,7 +88,7 @@ public class EvolveAbility extends TriggeredAbilityImpl { && triggeringCreature.isControlledBy(this.controllerId)) { Permanent sourceCreature = game.getPermanent(sourceId); if (sourceCreature != null && isPowerOrThoughnessGreater(sourceCreature, triggeringCreature)) { - this.getEffects().get(0).setTargetPointer(new FixedTarget(event.getTargetId())); + this.getEffects().get(0).setTargetPointer(new FixedTarget(event.getTargetId(), game)); return true; } } diff --git a/Mage/src/main/java/mage/abilities/keyword/FlankingAbility.java b/Mage/src/main/java/mage/abilities/keyword/FlankingAbility.java index ce887d86e0c..3c7319a71ee 100644 --- a/Mage/src/main/java/mage/abilities/keyword/FlankingAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/FlankingAbility.java @@ -38,7 +38,7 @@ public class FlankingAbility extends TriggeredAbilityImpl { if (!hasFlankingAbility) { for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getSourceId())); + effect.setTargetPointer(new FixedTarget(event.getSourceId(), game)); } return true; } diff --git a/Mage/src/main/java/mage/abilities/keyword/ForetellAbility.java b/Mage/src/main/java/mage/abilities/keyword/ForetellAbility.java index 045cfd70856..8a20a4bb066 100644 --- a/Mage/src/main/java/mage/abilities/keyword/ForetellAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/ForetellAbility.java @@ -139,7 +139,7 @@ public class ForetellAbility extends SpecialAction { // exile the card face-down effect.setWithName(false); - effect.setTargetPointer(new FixedTarget(card.getId())); + effect.setTargetPointer(new FixedTarget(card.getId(), game)); effect.apply(game, source); card.setFaceDown(true, game); game.addEffect(new ForetellAddCostEffect(new MageObjectReference(card, game)), source); diff --git a/Mage/src/main/java/mage/abilities/keyword/FortifyAbility.java b/Mage/src/main/java/mage/abilities/keyword/FortifyAbility.java index 14596029b34..be1b1ea368a 100644 --- a/Mage/src/main/java/mage/abilities/keyword/FortifyAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/FortifyAbility.java @@ -1,20 +1,17 @@ - - package mage.abilities.keyword; +import mage.abilities.ActivatedAbilityImpl; +import mage.abilities.costs.Cost; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.common.FortifyEffect; +import mage.abilities.effects.common.AttachEffect; import mage.constants.Outcome; import mage.constants.TimingRule; import mage.constants.Zone; -import mage.abilities.ActivatedAbilityImpl; -import mage.abilities.costs.Cost; -import mage.filter.common.FilterControlledLandPermanent; +import mage.filter.StaticFilters; import mage.target.Target; import mage.target.TargetPermanent; /** - * * @author BetaSteward_at_googlemail.com */ @@ -26,11 +23,11 @@ public class FortifyAbility extends ActivatedAbilityImpl { } public FortifyAbility(Outcome outcome, Cost cost) { - this(outcome, cost, new TargetPermanent(new FilterControlledLandPermanent())); + this(outcome, cost, new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND)); } public FortifyAbility(Outcome outcome, Cost cost, Target target) { - super(Zone.BATTLEFIELD, new FortifyEffect(outcome), cost); + super(Zone.BATTLEFIELD, new AttachEffect(outcome, "Fortify"), cost); this.addTarget(target); this.timing = TimingRule.SORCERY; } @@ -49,4 +46,4 @@ public class FortifyAbility extends ActivatedAbilityImpl { public String getRule() { return "Fortify " + costs.getText() + manaCosts.getText() + " (" + manaCosts.getText() + ": Attach to target land you control. Fortify only as a sorcery.)"; } -} \ No newline at end of file +} diff --git a/Mage/src/main/java/mage/abilities/keyword/FriendsForeverAbility.java b/Mage/src/main/java/mage/abilities/keyword/FriendsForeverAbility.java new file mode 100644 index 00000000000..f53cf3d2ec5 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/keyword/FriendsForeverAbility.java @@ -0,0 +1,37 @@ +package mage.abilities.keyword; + +import mage.abilities.MageSingleton; +import mage.abilities.StaticAbility; +import mage.constants.Zone; + +import java.io.ObjectStreamException; + +/** + * @author TheElk801 + */ +public class FriendsForeverAbility extends StaticAbility implements MageSingleton { + + private static final FriendsForeverAbility instance = new FriendsForeverAbility(); + + private Object readResolve() throws ObjectStreamException { + return instance; + } + + public static FriendsForeverAbility getInstance() { + return instance; + } + + private FriendsForeverAbility() { + super(Zone.BATTLEFIELD, null); + } + + @Override + public String getRule() { + return "Friends forever (You can have two commanders if both have friends forever.)"; + } + + @Override + public FriendsForeverAbility copy() { + return instance; + } +} diff --git a/Mage/src/main/java/mage/abilities/keyword/GraftAbility.java b/Mage/src/main/java/mage/abilities/keyword/GraftAbility.java index ea143e5fd46..bc588c22f2b 100644 --- a/Mage/src/main/java/mage/abilities/keyword/GraftAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/GraftAbility.java @@ -72,7 +72,7 @@ public class GraftAbility extends TriggeredAbilityImpl { && sourcePermanent.getCounters(game).containsKey(CounterType.P1P1) && filter.match(permanent, game)) { for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getTargetId())); + effect.setTargetPointer(new FixedTarget(event.getTargetId(), game)); } return true; } diff --git a/Mage/src/main/java/mage/abilities/keyword/HauntAbility.java b/Mage/src/main/java/mage/abilities/keyword/HauntAbility.java index d487b1bfe53..a9582a551a4 100644 --- a/Mage/src/main/java/mage/abilities/keyword/HauntAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/HauntAbility.java @@ -201,7 +201,7 @@ class HauntEffect extends OneShotEffect { .append(source.getSourceId().toString()) .append(card.getZoneChangeCounter(game) + hauntedCreature.getZoneChangeCounter(game)).toString(); // in case it is blinked - game.getState().setValue(key, new FixedTarget(targetPointer.getFirst(game, source))); + game.getState().setValue(key, new FixedTarget(targetPointer.getFirst(game, source), game)); card.addInfo("hauntinfo", new StringBuilder("Haunting ").append(hauntedCreature.getLogName()).toString(), game); hauntedCreature.addInfo("hauntinfo", new StringBuilder("Haunted by ").append(card.getLogName()).toString(), game); game.informPlayers(new StringBuilder(card.getName()).append(" haunting ").append(hauntedCreature.getLogName()).toString()); diff --git a/Mage/src/main/java/mage/abilities/keyword/IntimidateAbility.java b/Mage/src/main/java/mage/abilities/keyword/IntimidateAbility.java index 0b4d247a84c..b41f53acae3 100644 --- a/Mage/src/main/java/mage/abilities/keyword/IntimidateAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/IntimidateAbility.java @@ -13,12 +13,14 @@ import mage.game.permanent.Permanent; *

* 702.13a Intimidate is an evasion ability. *

- * 702.13b A creature with intimidate can't be blocked except by artifact creatures - * and/or creatures that share a color with it. (See rule 509, "Declare Blockers Step.") # + * 702.13b A creature with intimidate can't be blocked except by artifact + * creatures and/or creatures that share a color with it. (See rule 509, + * "Declare Blockers Step.") # *

* 702.13c Multiple instances of intimidate on the same creature are redundant. */ public class IntimidateAbility extends EvasionAbility implements MageSingleton { + private static final IntimidateAbility instance = new IntimidateAbility(); public static IntimidateAbility getInstance() { @@ -31,7 +33,7 @@ public class IntimidateAbility extends EvasionAbility implements MageSingleton { @Override public String getRule() { - return "intimidate"; + return "intimidate (This creature can't be blocked except by artifact creatures and/or creatures that share a color with it.)"; } @Override @@ -41,6 +43,7 @@ public class IntimidateAbility extends EvasionAbility implements MageSingleton { } class IntimidateEffect extends RestrictionEffect implements MageSingleton { + public IntimidateEffect() { super(Duration.EndOfGame); } diff --git a/Mage/src/main/java/mage/abilities/keyword/KickerAbility.java b/Mage/src/main/java/mage/abilities/keyword/KickerAbility.java index 5ad122945e2..cdf4e4ee6fc 100644 --- a/Mage/src/main/java/mage/abilities/keyword/KickerAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/KickerAbility.java @@ -115,18 +115,11 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo }); } - public void resetKicker(Game game, Ability source) { + private void resetKicker() { for (OptionalAdditionalCost cost : kickerCosts) { cost.reset(); } - String key = getActivationKey(source, "", game); - for (Iterator iterator = activations.keySet().iterator(); iterator.hasNext(); ) { - String activationKey = iterator.next(); - if (activationKey.startsWith(key) - && activations.get(activationKey) > 0) { - activations.put(key, 0); - } - } + activations.clear(); } private int getKickedCounterStrict(Game game, Ability source, String needKickerCost) { @@ -241,7 +234,7 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo if (ability instanceof SpellAbility) { Player player = game.getPlayer(ability.getControllerId()); if (player != null) { - this.resetKicker(game, ability); + this.resetKicker(); for (OptionalAdditionalCost kickerCost : kickerCosts) { boolean again = true; while (player.canRespond() && again) { diff --git a/Mage/src/main/java/mage/abilities/keyword/MadnessAbility.java b/Mage/src/main/java/mage/abilities/keyword/MadnessAbility.java index 65a9352d671..366da4513b0 100644 --- a/Mage/src/main/java/mage/abilities/keyword/MadnessAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/MadnessAbility.java @@ -1,11 +1,9 @@ package mage.abilities.keyword; import mage.MageObject; -import mage.abilities.Ability; -import mage.abilities.SpellAbility; -import mage.abilities.StaticAbility; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.*; import mage.abilities.condition.Condition; +import mage.abilities.costs.common.PayLifeCost; import mage.abilities.costs.mana.ManaCost; import mage.abilities.costs.mana.ManaCosts; import mage.abilities.effects.OneShotEffect; @@ -52,14 +50,28 @@ public class MadnessAbility extends StaticAbility { private final String rule; - @SuppressWarnings("unchecked") - public MadnessAbility(Card card, ManaCosts madnessCost) { - super(Zone.HAND, new MadnessReplacementEffect((ManaCosts) madnessCost)); - addSubAbility(new MadnessTriggeredAbility((ManaCosts) madnessCost, getOriginalId())); - rule = "Madness " + madnessCost.getText() + " (If you discard this card, discard it into exile. When you do, cast it for its madness cost or put it into your graveyard.)"; + public MadnessAbility(Card card, ManaCosts madnessCost) { + this(card, madnessCost, 0); } - public MadnessAbility(final MadnessAbility ability) { + public MadnessAbility(Card card, ManaCosts madnessCost, int lifeCost) { + super(Zone.HAND, new MadnessReplacementEffect(madnessCost, lifeCost)); + addSubAbility(new MadnessTriggeredAbility(madnessCost, lifeCost, getOriginalId())); + + String costText; + + if (lifeCost > 0) { + costText = "Madness—" + madnessCost.getText() + ", Pay " + lifeCost + " life."; + } else { + costText = "Madness " + madnessCost.getText(); + } + + this.rule = costText + " (If you discard this card, discard it into exile. " + + "When you do, cast it for its madness cost or put it into your graveyard.)"; + } + + + private MadnessAbility(final MadnessAbility ability) { super(ability); this.rule = ability.rule; } @@ -81,12 +93,21 @@ public class MadnessAbility extends StaticAbility { class MadnessReplacementEffect extends ReplacementEffectImpl { - public MadnessReplacementEffect(ManaCosts madnessCost) { + public MadnessReplacementEffect(ManaCosts madnessCost, int lifeCost) { super(Duration.EndOfGame, Outcome.Benefit); - staticText = "Madness " + madnessCost.getText() + " (If you discard this card, you may cast it for its madness cost instead of putting it into your graveyard.)"; + + String costText; + + if (lifeCost > 0) { + costText = "Madness—" + madnessCost.getText() + ", Pay " + lifeCost + " life."; + } else { + costText = "Madness " + madnessCost.getText(); + } + + staticText = costText + " (If you discard this card, you may cast it for its madness cost instead of putting it into your graveyard.)"; } - public MadnessReplacementEffect(final MadnessReplacementEffect effect) { + private MadnessReplacementEffect(final MadnessReplacementEffect effect) { super(effect); } @@ -103,18 +124,19 @@ class MadnessReplacementEffect extends ReplacementEffectImpl { @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Card card = game.getCard(event.getTargetId()); - if (card != null) { - if (controller.moveCardToExileWithInfo(card, source.getSourceId(), "Madness", source, game, ((ZoneChangeEvent) event).getFromZone(), true)) { - game.applyEffects(); // needed to add Madness ability to cards (e.g. by Falkenrath Gorger) - GameEvent gameEvent = new MadnessCardExiledEvent(card.getId(), source, controller.getId()); - game.fireEvent(gameEvent); - } - return true; - } + if (controller == null) { return false; } + + Card card = game.getCard(event.getTargetId()); + if (card == null) { return false; } + + // TODO, deal with deprecated call + if (controller.moveCardToExileWithInfo(card, source.getSourceId(), "Madness", source, game, ((ZoneChangeEvent) event).getFromZone(), true)) { + game.applyEffects(); // needed to add Madness ability to cards (e.g. by Falkenrath Gorger) + GameEvent gameEvent = new MadnessCardExiledEvent(card.getId(), source, controller.getId()); + game.fireEvent(gameEvent); } - return false; + + return true; } @Override @@ -125,7 +147,8 @@ class MadnessReplacementEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { return event.getTargetId().equals(source.getSourceId()) - && ((ZoneChangeEvent) event).getFromZone() == Zone.HAND && ((ZoneChangeEvent) event).getToZone() == Zone.GRAVEYARD; + && ((ZoneChangeEvent) event).getFromZone() == Zone.HAND + && ((ZoneChangeEvent) event).getToZone() == Zone.GRAVEYARD; } } @@ -138,13 +161,13 @@ class MadnessTriggeredAbility extends TriggeredAbilityImpl { private final UUID madnessOriginalId; - MadnessTriggeredAbility(ManaCosts madnessCost, UUID madnessOriginalId) { - super(Zone.EXILED, new MadnessCastEffect(madnessCost), true); + MadnessTriggeredAbility(ManaCosts madnessCost, int lifeCost, UUID madnessOriginalId) { + super(Zone.EXILED, new MadnessCastEffect(madnessCost, lifeCost), true); this.madnessOriginalId = madnessOriginalId; this.setRuleVisible(false); } - MadnessTriggeredAbility(final MadnessTriggeredAbility ability) { + private MadnessTriggeredAbility(final MadnessTriggeredAbility ability) { super(ability); this.madnessOriginalId = ability.madnessOriginalId; } @@ -161,23 +184,23 @@ class MadnessTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - return event.getSourceId().equals(madnessOriginalId); // Check that the event was from the connected replacement effect + // Check that the event was from the connected replacement effect + return event.getSourceId().equals(madnessOriginalId); } @Override public boolean resolve(Game game) { - if (!super.resolve(game)) { - Card card = game.getCard(getSourceId()); - if (card != null) { - Player owner = game.getPlayer(card.getOwnerId()); - if (owner != null) { - // if cast was not successfull, the card is moved to graveyard - owner.moveCards(card, Zone.GRAVEYARD, this, game); - } - } - return false; - } - return true; + if (super.resolve(game)) { return true; } + + Card card = game.getCard(getSourceId()); + if (card == null) { return false; } + + Player owner = game.getPlayer(card.getOwnerId()); + if (owner == null) { return false; } + + // if cast was not successfull, the card is moved to graveyard + owner.moveCards(card, Zone.GRAVEYARD, this, game); + return false; } @Override @@ -189,43 +212,52 @@ class MadnessTriggeredAbility extends TriggeredAbilityImpl { class MadnessCastEffect extends OneShotEffect { private final ManaCosts madnessCost; + private final int lifeCost; - public MadnessCastEffect(ManaCosts madnessCost) { + public MadnessCastEffect(ManaCosts madnessCost, int lifeCost) { super(Outcome.Benefit); this.madnessCost = madnessCost; - staticText = "you may cast it by paying " + madnessCost.getText() + " instead of putting it into your graveyard"; + this.lifeCost = lifeCost; + + String costText; + + if (lifeCost > 0) { + costText = madnessCost.getText() + " and " + lifeCost + " life"; + } else { + costText = madnessCost.getText(); + } + + staticText = "you may cast it by paying " + costText + " instead of putting it into your graveyard"; } - public MadnessCastEffect(final MadnessCastEffect effect) { + private MadnessCastEffect(final MadnessCastEffect effect) { super(effect); this.madnessCost = effect.madnessCost; + this.lifeCost = effect.lifeCost; } + @Override + public MadnessCastEffect copy() { return new MadnessCastEffect(this); } + @Override public boolean apply(Game game, Ability source) { Card card = game.getCard(source.getSourceId()); - if (card == null) { - return false; - } + if (card == null) { return false; } Player owner = game.getPlayer(card.getOwnerId()); - if (owner == null) { - return false; - } + if (owner == null) { return false; } - // replace with the new cost + // Replace with the new cost SpellAbility castByMadness = card.getSpellAbility().copy(); ManaCosts costRef = castByMadness.getManaCostsToPay(); castByMadness.setSpellAbilityType(SpellAbilityType.BASE_ALTERNATE); castByMadness.setSpellAbilityCastMode(SpellAbilityCastMode.MADNESS); + castByMadness.getCosts().clear(); + castByMadness.addCost(new PayLifeCost(this.lifeCost)); costRef.clear(); costRef.add(madnessCost); - return owner.cast(castByMadness, game, false, new ApprovingObject(source, game)); - } - @Override - public MadnessCastEffect copy() { - return new MadnessCastEffect(this); + return owner.cast(castByMadness, game, false, new ApprovingObject(source, game)); } } @@ -236,12 +268,10 @@ enum MadnessCondition implements Condition { @Override public boolean apply(Game game, Ability source) { MageObject madnessSpell = game.getLastKnownInformation(source.getSourceId(), Zone.STACK, source.getSourceObjectZoneChangeCounter() - 1); - if (madnessSpell instanceof Spell) { - if (((Spell) madnessSpell).getSpellAbility() != null) { - return ((Spell) madnessSpell).getSpellAbility().getSpellAbilityCastMode() == SpellAbilityCastMode.MADNESS; - } - } - return false; - } + if (!(madnessSpell instanceof Spell)) { return false; } + if (((Spell) madnessSpell).getSpellAbility() == null) { return false; } + + return ((Spell) madnessSpell).getSpellAbility().getSpellAbilityCastMode() == SpellAbilityCastMode.MADNESS; + } } diff --git a/Mage/src/main/java/mage/abilities/keyword/MyriadAbility.java b/Mage/src/main/java/mage/abilities/keyword/MyriadAbility.java index ae1ad7f3d94..0aaa7018e28 100644 --- a/Mage/src/main/java/mage/abilities/keyword/MyriadAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/MyriadAbility.java @@ -79,7 +79,7 @@ class MyriadEffect extends OneShotEffect { CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(controller.getId(), null, false, 1, true, true, playerId); effect.setTargetPointer(new FixedTarget(sourceObject, game)); effect.apply(game, source); - tokens.addAll(effect.getAddedPermanent()); + tokens.addAll(effect.getAddedPermanents()); } } } diff --git a/Mage/src/main/java/mage/abilities/keyword/NightboundAbility.java b/Mage/src/main/java/mage/abilities/keyword/NightboundAbility.java index 804b63c83cc..5269cd301bb 100644 --- a/Mage/src/main/java/mage/abilities/keyword/NightboundAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/NightboundAbility.java @@ -1,16 +1,21 @@ package mage.abilities.keyword; +import mage.abilities.Ability; import mage.abilities.StaticAbility; -import mage.constants.Zone; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.hint.common.DayNightHint; +import mage.cards.Card; +import mage.constants.*; +import mage.game.Game; /** * @author TheElk801 - * TODO: Implement this */ public class NightboundAbility extends StaticAbility { public NightboundAbility() { - super(Zone.BATTLEFIELD, null); + super(Zone.BATTLEFIELD, new NightboundEffect()); + this.addHint(DayNightHint.instance); } private NightboundAbility(final NightboundAbility ability) { @@ -26,4 +31,34 @@ public class NightboundAbility extends StaticAbility { public NightboundAbility copy() { return new NightboundAbility(this); } + + public static boolean checkCard(Card card, Game game) { + return game.checkDayNight(false) + && card.getSecondCardFace() != null + && card.getSecondCardFace().getAbilities().containsClass(NightboundAbility.class); + } +} + +class NightboundEffect extends ContinuousEffectImpl { + + NightboundEffect() { + super(Duration.WhileOnBattlefield, Layer.PlayerEffects, SubLayer.NA, Outcome.Benefit); + } + + private NightboundEffect(final NightboundEffect effect) { + super(effect); + } + + @Override + public NightboundEffect copy() { + return new NightboundEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + if (!game.hasDayNight()) { + game.setDaytime(false); + } + return true; + } } diff --git a/Mage/src/main/java/mage/abilities/keyword/NinjutsuAbility.java b/Mage/src/main/java/mage/abilities/keyword/NinjutsuAbility.java index 2acc6fc9fa5..0a43464b9dc 100644 --- a/Mage/src/main/java/mage/abilities/keyword/NinjutsuAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/NinjutsuAbility.java @@ -4,7 +4,7 @@ import mage.abilities.Ability; import mage.abilities.ActivatedAbilityImpl; import mage.abilities.costs.Cost; import mage.abilities.costs.CostImpl; -import mage.abilities.costs.mana.ManaCost; +import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; import mage.cards.Cards; @@ -18,7 +18,6 @@ import mage.game.Game; import mage.game.command.CommandObject; import mage.game.permanent.Permanent; import mage.players.Player; -import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetControlledPermanent; import mage.util.CardUtil; @@ -46,28 +45,22 @@ import java.util.UUID; public class NinjutsuAbility extends ActivatedAbilityImpl { private final boolean commander; - private static final FilterControlledCreaturePermanent filter = - new FilterControlledCreaturePermanent("unblocked attacker you control"); - - static { - filter.add(UnblockedPredicate.instance); - } /** - * @param manaCost ninjutsu mana cost + * @param manaString ninjutsu mana cost */ - public NinjutsuAbility(ManaCost manaCost) { - this(manaCost, false); + public NinjutsuAbility(String manaString) { + this(new ManaCostsImpl<>(manaString), false); } - public NinjutsuAbility(ManaCost manaCost, boolean commander) { - super(commander ? Zone.ALL : Zone.HAND, new NinjutsuEffect(), manaCost); + public NinjutsuAbility(Cost cost, boolean commander) { + super(commander ? Zone.ALL : Zone.HAND, new NinjutsuEffect(), cost); this.addCost(new RevealNinjutsuCardCost(commander)); - this.addCost(new ReturnAttackerToHandTargetCost(new TargetControlledCreaturePermanent(1, 1, filter, true))); + this.addCost(new ReturnAttackerToHandTargetCost()); this.commander = commander; } - public NinjutsuAbility(NinjutsuAbility ability) { + private NinjutsuAbility(final NinjutsuAbility ability) { super(ability); this.commander = ability.commander; } @@ -135,11 +128,17 @@ class NinjutsuEffect extends OneShotEffect { class ReturnAttackerToHandTargetCost extends CostImpl { - private UUID defendingPlayerId; + private static final FilterControlledCreaturePermanent filter = + new FilterControlledCreaturePermanent("unblocked attacker you control"); - public ReturnAttackerToHandTargetCost(TargetControlledPermanent target) { - this.addTarget(target); - this.defendingPlayerId = null; + static { + filter.add(UnblockedPredicate.instance); + } + + private UUID defendingPlayerId = null; + + public ReturnAttackerToHandTargetCost() { + this.addTarget(new TargetControlledPermanent(filter)); this.text = "Return an unblocked attacker you control to hand"; } diff --git a/Mage/src/main/java/mage/abilities/keyword/ProtectionAbility.java b/Mage/src/main/java/mage/abilities/keyword/ProtectionAbility.java index 42bffda3b83..d7130b6cf6b 100644 --- a/Mage/src/main/java/mage/abilities/keyword/ProtectionAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/ProtectionAbility.java @@ -15,8 +15,6 @@ import mage.game.stack.StackObject; import mage.players.Player; import mage.util.CardUtil; -import java.util.ArrayList; -import java.util.List; import java.util.UUID; import java.util.stream.Collectors; @@ -29,7 +27,7 @@ public class ProtectionAbility extends StaticAbility { protected boolean removeAuras; protected boolean removeEquipment; protected boolean doesntRemoveControlled; - protected static List colors = new ArrayList<>(); + protected ObjectColor fromColor; protected UUID auraIdNotToBeRemoved; // defines an Aura objectId that will not be removed from this protection ability public ProtectionAbility(Filter filter) { @@ -38,6 +36,7 @@ public class ProtectionAbility extends StaticAbility { this.removeAuras = true; this.removeEquipment = true; this.doesntRemoveControlled = false; + this.fromColor = new ObjectColor(); this.auraIdNotToBeRemoved = null; } @@ -47,22 +46,25 @@ public class ProtectionAbility extends StaticAbility { this.removeAuras = ability.removeAuras; this.removeEquipment = ability.removeEquipment; this.doesntRemoveControlled = ability.doesntRemoveControlled; + this.fromColor = ability.fromColor; this.auraIdNotToBeRemoved = ability.auraIdNotToBeRemoved; } public static ProtectionAbility from(ObjectColor color) { FilterObject filter = new FilterObject(getFilterText(color)); filter.add(new ColorPredicate(color)); - colors.add(color); - return new ProtectionAbility(filter); + ProtectionAbility ability = new ProtectionAbility(filter); + ability.getFromColor().addColor(color); + return ability; } public static ProtectionAbility from(ObjectColor color1, ObjectColor color2) { FilterObject filter = new FilterObject(color1.getDescription() + " and from " + color2.getDescription()); filter.add(Predicates.or(new ColorPredicate(color1), new ColorPredicate(color2))); - colors.add(color1); - colors.add(color2); - return new ProtectionAbility(filter); + ProtectionAbility ability = new ProtectionAbility(filter); + ability.getFromColor().addColor(color1); + ability.getFromColor().addColor(color2); + return ability; } @Override @@ -171,8 +173,8 @@ public class ProtectionAbility extends StaticAbility { return doesntRemoveControlled; } - public List getColors() { - return colors; + public ObjectColor getFromColor() { + return fromColor; } public UUID getAuraIdNotToBeRemoved() { diff --git a/Mage/src/main/java/mage/abilities/keyword/ProwessAbility.java b/Mage/src/main/java/mage/abilities/keyword/ProwessAbility.java index f3d46f24d4f..5c16fac1131 100644 --- a/Mage/src/main/java/mage/abilities/keyword/ProwessAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/ProwessAbility.java @@ -2,25 +2,17 @@ package mage.abilities.keyword; import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.effects.common.continuous.BoostSourceEffect; -import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.FilterSpell; -import mage.filter.predicate.Predicates; +import mage.filter.StaticFilters; /** * @author LevelX2 */ public class ProwessAbility extends SpellCastControllerTriggeredAbility { - private static final FilterSpell filterNonCreatureSpell = new FilterSpell("noncreature spell"); - - static { - filterNonCreatureSpell.add(Predicates.not(CardType.CREATURE.getPredicate())); - } - public ProwessAbility() { super(new BoostSourceEffect(1, 1, Duration.EndOfTurn), false); - this.filter = filterNonCreatureSpell; + this.filter = StaticFilters.FILTER_SPELL_NON_CREATURE; } public ProwessAbility(final ProwessAbility ability) { diff --git a/Mage/src/main/java/mage/abilities/keyword/ReconfigureAbility.java b/Mage/src/main/java/mage/abilities/keyword/ReconfigureAbility.java new file mode 100644 index 00000000000..61d672923a8 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/keyword/ReconfigureAbility.java @@ -0,0 +1,135 @@ +package mage.abilities.keyword; + +import mage.abilities.Ability; +import mage.abilities.ActivatedAbilityImpl; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.AttachEffect; +import mage.constants.*; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetControlledCreaturePermanent; + +/** + * @author TheElk801 + */ +public class ReconfigureAbility extends ActivatedAbilityImpl { + + private final String manaString; + + public ReconfigureAbility(String manaString) { + super(Zone.BATTLEFIELD, new AttachEffect(Outcome.BoostCreature), new ManaCostsImpl<>(manaString)); + this.manaString = manaString; + this.timing = TimingRule.SORCERY; + this.addTarget(new TargetControlledCreaturePermanent()); + this.addSubAbility(new ReconfigureUnattachAbility(manaString)); + Ability ability = new SimpleStaticAbility(new ReconfigureTypeEffect()); + ability.setRuleVisible(false); + this.addSubAbility(ability); + } + + private ReconfigureAbility(final ReconfigureAbility ability) { + super(ability); + this.manaString = ability.manaString; + } + + @Override + public ReconfigureAbility copy() { + return new ReconfigureAbility(this); + } + + @Override + public String getRule() { + return "Reconfigure " + manaString + " (" + manaString + + ": Attach to target creature you control; " + + "or unattach from a creature. Reconfigure only as a sorcery. " + + "While attached, this isn't a creature.)"; + } +} + +class ReconfigureUnattachAbility extends ActivatedAbilityImpl { + + protected ReconfigureUnattachAbility(String manaString) { + super(Zone.BATTLEFIELD, new ReconfigureUnattachEffect(), new ManaCostsImpl<>(manaString)); + this.condition = ReconfigureUnattachAbility::checkForCreature; + this.timing = TimingRule.SORCERY; + this.setRuleVisible(false); + } + + private ReconfigureUnattachAbility(final ReconfigureUnattachAbility ability) { + super(ability); + } + + @Override + public ReconfigureUnattachAbility copy() { + return new ReconfigureUnattachAbility(this); + } + + @Override + public String getRule() { + return super.getRule() + " Activate only if this permanent is attached to a creature and only as a sorcery."; + } + + private static boolean checkForCreature(Game game, Ability source) { + Permanent equipment = source.getSourcePermanentIfItStillExists(game); + if (equipment == null) { + return false; + } + Permanent permanent = game.getPermanent(equipment.getAttachedTo()); + return permanent != null && permanent.isCreature(game); + } +} + +class ReconfigureUnattachEffect extends OneShotEffect { + + ReconfigureUnattachEffect() { + super(Outcome.Benefit); + staticText = "unattach this permanent"; + } + + private ReconfigureUnattachEffect(final ReconfigureUnattachEffect effect) { + super(effect); + } + + @Override + public ReconfigureUnattachEffect copy() { + return new ReconfigureUnattachEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (permanent != null) { + permanent.unattach(game); + } + return true; + } +} + +class ReconfigureTypeEffect extends ContinuousEffectImpl { + + ReconfigureTypeEffect() { + super(Duration.WhileOnBattlefield, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Benefit); + } + + private ReconfigureTypeEffect(final ReconfigureTypeEffect effect) { + super(effect); + } + + @Override + public ReconfigureTypeEffect copy() { + return new ReconfigureTypeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (permanent == null || game.getPermanent(permanent.getAttachedTo()) == null) { + return false; + } + permanent.removeCardType(game, CardType.CREATURE); + return true; + } +} diff --git a/Mage/src/main/java/mage/abilities/keyword/SuspendAbility.java b/Mage/src/main/java/mage/abilities/keyword/SuspendAbility.java index ea752b29bb5..f3ae5a2748b 100644 --- a/Mage/src/main/java/mage/abilities/keyword/SuspendAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/SuspendAbility.java @@ -126,8 +126,6 @@ public class SuspendAbility extends SpecialAction { public SuspendAbility(int suspend, ManaCost cost, Card card, boolean shortRule) { super(Zone.HAND); this.addCost(cost); - // suspend uses both sorcery/instant timing depends on object, so it checks with object, see canActivate - this.setTiming(TimingRule.SORCERY); this.addEffect(new SuspendExileEffect(suspend)); this.usesStack = false; if (suspend == Integer.MAX_VALUE) { @@ -210,7 +208,14 @@ public class SuspendAbility extends SpecialAction { if (game.getState().getZone(getSourceId()) != Zone.HAND) { return ActivationStatus.getFalse(); } - + // suspend uses card's timing restriction + Card card = game.getCard(getSourceId()); + if (card == null) { + return ActivationStatus.getFalse(); + } + if (!card.getSpellAbility().spellCanBeActivatedRegularlyNow(playerId, game)) { + return ActivationStatus.getFalse(); + } return super.canActivate(playerId, game); } diff --git a/Mage/src/main/java/mage/abilities/keyword/TotemArmorAbility.java b/Mage/src/main/java/mage/abilities/keyword/TotemArmorAbility.java index be6d8b442ae..6b643db3550 100644 --- a/Mage/src/main/java/mage/abilities/keyword/TotemArmorAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/TotemArmorAbility.java @@ -1,5 +1,3 @@ - - package mage.abilities.keyword; import mage.abilities.Ability; @@ -24,16 +22,17 @@ import mage.game.permanent.Permanent; */ public class TotemArmorAbility extends SimpleStaticAbility { + public TotemArmorAbility() { super(Zone.BATTLEFIELD, new TotemArmorEffect()); } - public TotemArmorAbility(final TotemArmorAbility ability) { + private TotemArmorAbility(final TotemArmorAbility ability) { super(ability); } @Override - public SimpleStaticAbility copy() { + public TotemArmorAbility copy() { return new TotemArmorAbility(this); } @@ -44,27 +43,27 @@ public class TotemArmorAbility extends SimpleStaticAbility { } class TotemArmorEffect extends ReplacementEffectImpl { + TotemArmorEffect() { super(Duration.WhileOnBattlefield, Outcome.Benefit); } - TotemArmorEffect(final TotemArmorEffect effect) { + private TotemArmorEffect(final TotemArmorEffect effect) { super(effect); } @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Permanent sourcePermanent = game.getPermanent(source.getSourceId()); - if (sourcePermanent != null) { - Permanent equipedPermanent = game.getPermanent(event.getTargetId()); - if (equipedPermanent != null) { - equipedPermanent.removeAllDamage(game); - sourcePermanent.destroy(source, game, false); - return true; - } + Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game); + Permanent enchantedPermanent = game.getPermanent(event.getTargetId()); + if (sourcePermanent == null || enchantedPermanent == null) { + return false; } - return false; + enchantedPermanent.removeAllDamage(game); + sourcePermanent.destroy(source, game, false); + return true; } + @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.DESTROY_PERMANENT; @@ -72,7 +71,7 @@ class TotemArmorEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { - Permanent sourcePermanent = game.getPermanent(source.getSourceId()); + Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game); return sourcePermanent != null && event.getTargetId().equals(sourcePermanent.getAttachedTo()); } diff --git a/Mage/src/main/java/mage/abilities/keyword/TrainingAbility.java b/Mage/src/main/java/mage/abilities/keyword/TrainingAbility.java new file mode 100644 index 00000000000..0b1a9d4bed0 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/keyword/TrainingAbility.java @@ -0,0 +1,92 @@ +package mage.abilities.keyword; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; + +import java.util.Objects; + +/** + * @author TheElk801 + */ +public class TrainingAbility extends TriggeredAbilityImpl { + + public TrainingAbility() { + super(Zone.BATTLEFIELD, new TrainingAbilityEffect()); + } + + private TrainingAbility(final TrainingAbility ability) { + super(ability); + } + + @Override + public TrainingAbility copy() { + return new TrainingAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DECLARED_ATTACKERS; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (!game.getCombat().getAttackers().contains(this.getSourceId())) { + return false; + } + Permanent permanent = getSourcePermanentIfItStillExists(game); + return permanent != null && game + .getCombat() + .getAttackers() + .stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .map(MageObject::getPower) + .mapToInt(MageInt::getValue) + .anyMatch(x -> x > permanent.getPower().getValue()); + } + + @Override + public String getRule() { + return "Training (Whenever this creature attacks with another creature " + + "with greater power, put a +1/+1 counter on this creature.)"; + } +} + +class TrainingAbilityEffect extends OneShotEffect { + + TrainingAbilityEffect() { + super(Outcome.Neutral); + } + + private TrainingAbilityEffect(final TrainingAbilityEffect effect) { + super(effect); + } + + @Override + public TrainingAbilityEffect copy() { + return new TrainingAbilityEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (permanent == null) { + return false; + } + permanent.addCounters(CounterType.P1P1.createInstance(), source, game); + game.fireEvent(GameEvent.getEvent( + GameEvent.EventType.TRAINED_CREATURE, + source.getSourceId(), source, source.getControllerId() + )); + return true; + } +} diff --git a/Mage/src/main/java/mage/abilities/keyword/TransformAbility.java b/Mage/src/main/java/mage/abilities/keyword/TransformAbility.java index 7017d0cd068..2990baa62eb 100644 --- a/Mage/src/main/java/mage/abilities/keyword/TransformAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/TransformAbility.java @@ -7,7 +7,9 @@ import mage.abilities.effects.ContinuousEffectImpl; import mage.cards.Card; import mage.constants.*; import mage.game.Game; +import mage.game.MageObjectAttribute; import mage.game.permanent.Permanent; +import mage.game.stack.Spell; /** * @author nantuko @@ -35,16 +37,16 @@ public class TransformAbility extends SimpleStaticAbility { return ""; } - public static void transform(Permanent permanent, Card sourceCard, Game game, Ability source) { - + public static void transformPermanent(Permanent permanent, Card sourceCard, Game game, Ability source) { if (sourceCard == null) { return; } + permanent.setTransformed(true); permanent.setName(sourceCard.getName()); permanent.getColor(game).setColor(sourceCard.getColor(game)); permanent.getManaCost().clear(); - permanent.getManaCost().add(sourceCard.getManaCost()); + permanent.getManaCost().add(sourceCard.getManaCost().copy()); permanent.removeAllCardTypes(game); for (CardType type : sourceCard.getCardType(game)) { permanent.addCardType(game, type); @@ -64,7 +66,49 @@ public class TransformAbility extends SimpleStaticAbility { } permanent.getPower().modifyBaseValue(sourceCard.getPower().getValue()); permanent.getToughness().modifyBaseValue(sourceCard.getToughness().getValue()); - permanent.setTransformable(sourceCard.isTransformable()); + permanent.setStartingLoyalty(sourceCard.getStartingLoyalty()); + } + + public static Card transformCardSpellStatic(Card mainSide, Card otherSide, Game game) { + // workaround to simulate transformed card on the stack (example: disturb ability) + // prepare static attributes + // TODO: must be removed after transform cards (one side) migrated to MDF engine (multiple sides) + Card newCard = mainSide.copy(); + newCard.setName(otherSide.getName()); + newCard.getSuperType().clear(); + + // mana value must be from main side only + newCard.getManaCost().clear(); + newCard.getManaCost().add(mainSide.getManaCost().copy()); + + for (SuperType type : otherSide.getSuperType()) { + newCard.addSuperType(type); + } + game.getState().getCardState(newCard.getId()).clearAbilities(); + for (Ability ability : otherSide.getAbilities()) { + game.getState().addOtherAbility(newCard, ability); + } + newCard.getPower().modifyBaseValue(otherSide.getPower().getValue()); + newCard.getToughness().modifyBaseValue(otherSide.getToughness().getValue()); + + return newCard; + } + + public static void transformCardSpellDynamic(Spell spell, Card otherSide, Game game) { + // workaround to simulate transformed card on the stack (example: disturb ability) + // prepare dynamic attributes + // TODO: must be removed after transform cards (one side) migrated to MDF engine (multiple sides) + MageObjectAttribute moa = game.getState().getCreateMageObjectAttribute(spell.getCard(), game); + moa.getColor().setColor(otherSide.getColor(game)); + moa.getCardType().clear(); + moa.getCardType().addAll(otherSide.getCardType(game)); + moa.getSubtype().clear(); + moa.getSubtype().addAll(otherSide.getSubtype(game)); + + game.getState().getCardState(spell.getCard().getId()).clearAbilities(); + for (Ability ability : otherSide.getAbilities()) { + game.getState().addOtherAbility(spell.getCard(), ability); + } } } @@ -102,7 +146,7 @@ class TransformEffect extends ContinuousEffectImpl { return false; } - TransformAbility.transform(permanent, card, game, source); + TransformAbility.transformPermanent(permanent, card, game, source); return true; diff --git a/Mage/src/main/java/mage/abilities/keyword/WardAbility.java b/Mage/src/main/java/mage/abilities/keyword/WardAbility.java index 586ee2a118b..0d9ce2405bc 100644 --- a/Mage/src/main/java/mage/abilities/keyword/WardAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/WardAbility.java @@ -8,6 +8,7 @@ import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.stack.StackObject; +import mage.target.Target; import mage.target.targetpointer.FixedTarget; import mage.util.CardUtil; @@ -33,16 +34,29 @@ public class WardAbility extends TriggeredAbilityImpl { return event.getType() == GameEvent.EventType.TARGETED; } + private StackObject getTargetingObject(GameEvent event, Game game) { + for (StackObject stackObject : game.getStack()) { + if (stackObject.getId().equals(event.getSourceId()) || stackObject.getSourceId().equals(event.getSourceId())) { + for (Target target : stackObject.getStackAbility().getTargets()) { + if (target.contains(getSourceId())) { + return stackObject; + } + } + } + } + return null; + } + @Override public boolean checkTrigger(GameEvent event, Game game) { if (!getSourceId().equals(event.getTargetId())) { return false; } - StackObject stackObject = game.getStack().getStackObject(event.getSourceId()); + StackObject stackObject = getTargetingObject(event, game); if (stackObject == null || !game.getOpponents(getControllerId()).contains(stackObject.getControllerId())) { return false; } - getEffects().setTargetPointer(new FixedTarget(event.getSourceId(), game)); + getEffects().setTargetPointer(new FixedTarget(stackObject.getId(), game)); return true; } diff --git a/Mage/src/main/java/mage/abilities/text/TextPart.java b/Mage/src/main/java/mage/abilities/text/TextPart.java deleted file mode 100644 index 87431fd1d64..00000000000 --- a/Mage/src/main/java/mage/abilities/text/TextPart.java +++ /dev/null @@ -1,26 +0,0 @@ - -package mage.abilities.text; - -import java.io.Serializable; -import java.util.UUID; -import mage.util.Copyable; - -/** - * - * @author LevelX2 - * @param - */ -public interface TextPart extends Serializable, Copyable { - - UUID getId(); - - String getText(); - - E getBaseValue(); - - E getCurrentValue(); - - void replaceWith(E o); - - void reset(); -} diff --git a/Mage/src/main/java/mage/abilities/text/TextPartColor.java b/Mage/src/main/java/mage/abilities/text/TextPartColor.java deleted file mode 100644 index c32527291f1..00000000000 --- a/Mage/src/main/java/mage/abilities/text/TextPartColor.java +++ /dev/null @@ -1,64 +0,0 @@ - -package mage.abilities.text; - -import mage.ObjectColor; - -/** - * - * This implementation is not finished yet. There is no support to also change - * the rules text of an object. - * - * @author LevelX2 - */ -public class TextPartColor extends TextPartImpl { - - private final ObjectColor objectColorBase; - private ObjectColor objectColorCurrent; - - public TextPartColor(ObjectColor objectColor) { - this.objectColorBase = objectColor; - this.objectColorCurrent = objectColor; - } - - public TextPartColor(final TextPartColor textPartColor) { - super(); - this.objectColorBase = textPartColor.objectColorBase; - this.objectColorCurrent = textPartColor.objectColorCurrent; - } - - @Override - public String getText() { - return objectColorCurrent.getDescription(); - } - - @Override - public ObjectColor getCurrentValue() { - return objectColorCurrent; - } - - @Override - public ObjectColor getBaseValue() { - return objectColorBase; - } - - @Override - public void replaceWith(ObjectColor objectColor) { - this.objectColorCurrent = objectColor; - } - - @Override - public void reset() { - this.objectColorCurrent = this.objectColorBase; - } - - @Override - public TextPartColor copy() { - return new TextPartColor(this); - } - - @Override - public String toString() { - return objectColorCurrent.toString(); - } - -} diff --git a/Mage/src/main/java/mage/abilities/text/TextPartImpl.java b/Mage/src/main/java/mage/abilities/text/TextPartImpl.java deleted file mode 100644 index a14b9bb088e..00000000000 --- a/Mage/src/main/java/mage/abilities/text/TextPartImpl.java +++ /dev/null @@ -1,27 +0,0 @@ -package mage.abilities.text; - -import java.util.UUID; - -/** - * - * @author LevelX2 - * @param - */ -public abstract class TextPartImpl implements TextPart { - - private final UUID id; - - public TextPartImpl() { - this.id = UUID.randomUUID(); - } - - public TextPartImpl(final TextPartImpl textPartimpl) { - this.id = textPartimpl.id; - } - - @Override - public UUID getId() { - return id; - } - -} diff --git a/Mage/src/main/java/mage/abilities/text/TextPartSubType.java b/Mage/src/main/java/mage/abilities/text/TextPartSubType.java deleted file mode 100644 index 8335613e7a6..00000000000 --- a/Mage/src/main/java/mage/abilities/text/TextPartSubType.java +++ /dev/null @@ -1,64 +0,0 @@ - -package mage.abilities.text; - -import mage.constants.SubType; - -/** - * This implementation is not finished yet. There is no support to also change - * the rules text of an object. Also all the cards that user subtypes in the - * text have to be updated with the new elements. - * - * @author LevelX2 - */ -public class TextPartSubType extends TextPartImpl { - - private final SubType subTypeBase; - private SubType subTypeCurrent; - - public TextPartSubType(SubType subType) { - this.subTypeBase = subType; - this.subTypeCurrent = subType; - } - - public TextPartSubType(final TextPartSubType textPartSubType) { - super(); - this.subTypeBase = textPartSubType.subTypeBase; - this.subTypeCurrent = textPartSubType.subTypeCurrent; - } - - @Override - public String getText() { - return subTypeCurrent.getDescription(); - } - - @Override - public SubType getCurrentValue() { - return subTypeCurrent; - } - - @Override - public SubType getBaseValue() { - return subTypeBase; - } - - @Override - public void replaceWith(SubType subType) { - this.subTypeCurrent = subType; - } - - @Override - public void reset() { - this.subTypeCurrent = this.subTypeBase; - } - - @Override - public TextPartSubType copy() { - return new TextPartSubType(this); - } - - @Override - public String toString() { - return subTypeCurrent.toString(); - } - -} diff --git a/Mage/src/main/java/mage/cards/Card.java b/Mage/src/main/java/mage/cards/Card.java index c00bffaf700..5db0ca7208d 100644 --- a/Mage/src/main/java/mage/cards/Card.java +++ b/Mage/src/main/java/mage/cards/Card.java @@ -70,8 +70,6 @@ public interface Card extends MageObject { boolean isTransformable(); - void setTransformable(boolean transformable); - Card getSecondCardFace(); boolean isNightCard(); diff --git a/Mage/src/main/java/mage/cards/CardImpl.java b/Mage/src/main/java/mage/cards/CardImpl.java index 0bd9d7464e3..daa45f8e5d0 100644 --- a/Mage/src/main/java/mage/cards/CardImpl.java +++ b/Mage/src/main/java/mage/cards/CardImpl.java @@ -8,6 +8,7 @@ import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.continuous.HasSubtypesSourceEffect; import mage.abilities.keyword.ChangelingAbility; import mage.abilities.keyword.FlashbackAbility; +import mage.abilities.keyword.ReconfigureAbility; import mage.abilities.mana.ActivatedManaAbilityImpl; import mage.cards.repository.PluginClassloaderRegistery; import mage.constants.*; @@ -43,7 +44,6 @@ public abstract class CardImpl extends MageObjectImpl implements Card { protected String tokenSetCode; protected String tokenDescriptor; protected Rarity rarity; - protected boolean transformable; protected Class secondSideCardClazz; protected Card secondSideCard; protected boolean nightCard; @@ -121,7 +121,6 @@ public abstract class CardImpl extends MageObjectImpl implements Card { tokenDescriptor = card.tokenDescriptor; rarity = card.rarity; - transformable = card.transformable; secondSideCardClazz = card.secondSideCardClazz; secondSideCard = null; // will be set on first getSecondCardFace call if card has one nightCard = card.nightCard; @@ -305,9 +304,7 @@ public abstract class CardImpl extends MageObjectImpl implements Card { public void addAbility(Ability ability) { ability.setSourceId(this.getId()); abilities.add(ability); - for (Ability subAbility : ability.getSubAbilities()) { - abilities.add(subAbility); - } + abilities.addAll(ability.getSubAbilities()); // dynamic check: you can't add ability to the PermanentCard, use permanent.addAbility(a, source, game) instead // reason: triggered abilities are not processing here @@ -620,12 +617,7 @@ public abstract class CardImpl extends MageObjectImpl implements Card { @Override public boolean isTransformable() { - return this.transformable; - } - - @Override - public void setTransformable(boolean transformable) { - this.transformable = transformable; + return this.secondSideCardClazz != null || this.nightCard; } @Override @@ -822,21 +814,35 @@ public abstract class CardImpl extends MageObjectImpl implements Card { @Override public boolean addAttachment(UUID permanentId, Ability source, Game game) { - if (!this.attachments.contains(permanentId)) { - Permanent attachment = game.getPermanent(permanentId); - if (attachment == null) { - attachment = game.getPermanentEntering(permanentId); - } - if (attachment != null) { - if (!game.replaceEvent(new AttachEvent(objectId, attachment, source))) { - this.attachments.add(permanentId); - attachment.attachTo(objectId, source, game); - game.fireEvent(new AttachedEvent(objectId, attachment, source)); - return true; - } - } + if (permanentId == null + || this.attachments.contains(permanentId) + || permanentId.equals(this.getId())) { + return false; } - return false; + Permanent attachment = game.getPermanent(permanentId); + if (attachment == null) { + attachment = game.getPermanentEntering(permanentId); + } + if (attachment == null) { + return false; + } + if (attachment.hasSubtype(SubType.EQUIPMENT, game) + && (attachment.isCreature(game) + && !attachment.getAbilities(game).containsClass(ReconfigureAbility.class) + || !this.isCreature(game))) { + return false; + } + if (attachment.hasSubtype(SubType.FORTIFICATION, game) + && (attachment.isCreature(game) || !this.isLand(game))) { + return false; + } + if (game.replaceEvent(new AttachEvent(objectId, attachment, source))) { + return false; + } + this.attachments.add(permanentId); + attachment.attachTo(objectId, source, game); + game.fireEvent(new AttachedEvent(objectId, attachment, source)); + return true; } @Override diff --git a/Mage/src/main/java/mage/cards/ExpansionSet.java b/Mage/src/main/java/mage/cards/ExpansionSet.java index 4afae0f6080..d053f843127 100644 --- a/Mage/src/main/java/mage/cards/ExpansionSet.java +++ b/Mage/src/main/java/mage/cards/ExpansionSet.java @@ -1,15 +1,14 @@ package mage.cards; -import mage.MageObject; import mage.ObjectColor; -import mage.abilities.Ability; -import mage.abilities.keyword.PartnerWithAbility; import mage.cards.repository.CardCriteria; import mage.cards.repository.CardInfo; import mage.cards.repository.CardRepository; import mage.collation.BoosterCollator; +import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.SetType; +import mage.filter.FilterMana; import mage.util.CardUtil; import mage.util.RandomUtil; import org.apache.log4j.Logger; @@ -116,25 +115,35 @@ public abstract class ExpansionSet implements Serializable { protected int numBoosterSpecial; protected int numBoosterLands; - protected int ratioBoosterSpecialLand = 0; // if > 0 basic lands are replaced with special land with probability ratioBoosterSpecialLandNumerator / ratioBoosterSpecialLand + + // if ratioBoosterSpecialLand > 0, one basic land may be replaced with a special card + // with probability ratioBoosterSpecialLandNumerator / ratioBoosterSpecialLand + protected int ratioBoosterSpecialLand = 0; protected int ratioBoosterSpecialLandNumerator = 1; + // if ratioBoosterSpecialCommon > 0, one common may be replaced with a special card + // with probability 1 / ratioBoosterSpecialCommon + protected int ratioBoosterSpecialCommon = 0; + + // if ratioBoosterSpecialRare > 0, one uncommon or rare is always replaced with a special card + // probability that a rare rather than an uncommon is replaced is 1 / ratioBoosterSpecialRare + // probability that the replacement card for a rare is a mythic is 1 / ratioBoosterSpecialMythic + protected double ratioBoosterSpecialRare = 0; + protected double ratioBoosterSpecialMythic; + protected int numBoosterCommon; protected int numBoosterUncommon; protected int numBoosterRare; protected int numBoosterDoubleFaced; // -1 = include normally 0 = exclude 1-n = include explicit protected double ratioBoosterMythic; - protected boolean hasPartnerMechanic = false; - protected boolean needsLegendCreature = false; - protected boolean needsPlaneswalker = false; - protected boolean validateBoosterColors = true; - protected double rejectMissingColorProbability = 0.8; - protected double rejectSameColorUncommonsProbability = 0.8; + protected boolean hasUnbalancedColors = false; + protected boolean hasOnlyMulticolorCards = false; protected int maxCardNumberInBooster; // used to omit cards with collector numbers beyond the regular cards in a set for boosters - protected final EnumMap> savedCards; + protected final EnumMap> savedCards = new EnumMap<>(Rarity.class); + protected final EnumMap> savedSpecialCards = new EnumMap<>(Rarity.class); protected final Map inBoosterMap = new HashMap<>(); public ExpansionSet(String name, String code, Date releaseDate, SetType setType) { @@ -143,7 +152,6 @@ public abstract class ExpansionSet implements Serializable { this.releaseDate = releaseDate; this.setType = setType; this.maxCardNumberInBooster = Integer.MAX_VALUE; - savedCards = new EnumMap<>(Rarity.class); } public String getName() { @@ -214,35 +222,6 @@ public abstract class ExpansionSet implements Serializable { return theBooster; } - protected int addMissingPartner(List booster, boolean partnerAllowed, int max, int i) { - - Card sourceCard = booster.get(booster.size() - 1); - for (Ability ability : sourceCard.getAbilities()) { - - //Check if fetched card has the PartnerWithAbility - if (ability instanceof PartnerWithAbility) { - String partnerName = ((PartnerWithAbility) ability).getPartnerName(); - //Check if the pack already contains a partner pair - if (partnerAllowed) { - //Added card always replaces an uncommon card - Card card = CardRepository.instance.findCardWPreferredSet(partnerName, sourceCard.getExpansionSetCode(), false).getCard(); - if (i < max) { - booster.add(card); - } else { - booster.set(0, card); - } - //2 return value indicates found partner - return 2; - } else { - //If partner already exists, remove card and loop again - booster.remove(booster.size() - 1); - return 0; - } - } - } - return 1; - } - protected void addToBooster(List booster, List cards) { if (!cards.isEmpty()) { CardInfo cardInfo = cards.remove(RandomUtil.nextInt(cards.size())); @@ -266,16 +245,7 @@ public abstract class ExpansionSet implements Serializable { } for (int i = 0; i < 100; i++) {//don't want to somehow loop forever - - List booster; - if (hasPartnerMechanic) { - // battlebond's partners cards - booster = createPartnerBooster(); - } else { - // all other sets - booster = tryBooster(); - } - + List booster = tryBooster(); if (boosterIsValid(booster)) { return booster; } @@ -318,17 +288,12 @@ public abstract class ExpansionSet implements Serializable { } protected boolean boosterIsValid(List booster) { - if (validateBoosterColors) { - if (!validateColors(booster)) { - return false; - } + if (!validateCommonColors(booster)) { + return false; } - if (needsLegendCreature) { - return booster.stream().anyMatch(card -> card.isLegendary() && card.isCreature()); - } - if (needsPlaneswalker) { - return booster.stream().filter(MageObject::isPlaneswalker).count() == 1; + if (!validateUncommonColors(booster)) { + return false; } // TODO: add partner check @@ -336,138 +301,87 @@ public abstract class ExpansionSet implements Serializable { return true; } - protected boolean validateColors(List booster) { - List magicColors - = Arrays.asList(ObjectColor.WHITE, ObjectColor.BLUE, ObjectColor.BLACK, ObjectColor.RED, ObjectColor.GREEN); - - // all cards colors - Map colorWeight = new HashMap<>(); - // uncommon/rare/mythic cards colors - Map uncommonWeight = new HashMap<>(); - - for (ObjectColor color : magicColors) { - colorWeight.put(color, 0); - uncommonWeight.put(color, 0); - } - - // count colors in the booster - for (Card card : booster) { - ObjectColor cardColor = card.getColor(null); - if (cardColor != null) { - List colors = cardColor.getColors(); - // todo: do we need gold color? - colors.remove(ObjectColor.GOLD); - if (!colors.isEmpty()) { - // 60 - full card weight - // multicolored cards add part of the weight to each color - int cardColorWeight = 60 / colors.size(); - for (ObjectColor color : colors) { - colorWeight.put(color, colorWeight.get(color) + cardColorWeight); - if (card.getRarity() != Rarity.COMMON) { - uncommonWeight.put(color, uncommonWeight.get(color) + cardColorWeight); - } - } - } + private static ObjectColor getColorForValidate(Card card) { + ObjectColor color = card.getColor(); + // treat colorless nonland cards with exactly one ID color as cards of that color + // (e.g. devoid, emerge, spellbombs... but not mana fixing artifacts) + if (color.isColorless() && !card.isLand()) { + FilterMana colorIdentity = card.getColorIdentity(); + if (colorIdentity.getColorCount() == 1) { + return new ObjectColor(colorIdentity.toString()); } } - - // check that all colors are present - if (magicColors.stream().anyMatch(color -> colorWeight.get(color) < 60)) { - // reject only part of the boosters - if (RandomUtil.nextDouble() < rejectMissingColorProbability) { - return false; - } - } - - // check that we don't have 3 or more uncommons/rares of the same color - if (magicColors.stream().anyMatch(color -> uncommonWeight.get(color) >= 180)) { - // reject only part of the boosters - return !(RandomUtil.nextDouble() < rejectSameColorUncommonsProbability); - } - - return true; + return color; } - private boolean checkMythic() { + protected boolean validateCommonColors(List booster) { + List commonColors = booster.stream() + .filter(card -> card.getRarity() == Rarity.COMMON) + .map(ExpansionSet::getColorForValidate) + .collect(Collectors.toList()); + + // for multicolor sets, count not just the colors present at common, + // but also the number of color combinations (guilds/shards/wedges) + // e.g. a booster with three UB commons, three RW commons and four G commons + // has all five colors but isn't "balanced" + ObjectColor colorsRepresented = new ObjectColor(); + Set colorCombinations = new HashSet<>(); + int colorlessCountPlusOne = 1; + + for (ObjectColor color : commonColors) { + colorCombinations.add(color); + int colorCount = color.getColorCount(); + if (colorCount == 0) { + ++colorlessCountPlusOne; + } else if (colorCount > 1 && !hasOnlyMulticolorCards) { + // to prevent biasing toward multicolor over monocolor cards, + // count them as one of their colors chosen at random + List multiColor = color.getColors(); + colorsRepresented.addColor(multiColor.get(RandomUtil.nextInt(multiColor.size()))); + } else { + colorsRepresented.addColor(color); + } + } + + int colors = Math.min(colorsRepresented.getColorCount(), colorCombinations.size()); + + // if booster has all five colors in five unique combinations, or if it has + // one card per color and all but one of the rest are colorless, accept it + // ("all but one" adds some leeway for sets with small boosters) + if (colors >= Math.min(5, commonColors.size() - colorlessCountPlusOne)) return true; + // otherwise, if booster is missing more than one color, reject it + if (colors < 4) return false; + // for Torment and Judgment, always accept boosters with four out of five colors + if (hasUnbalancedColors) return true; + // if a common was replaced by a special card, increase the chance to accept four colors + if (commonColors.size() < numBoosterCommon) ++colorlessCountPlusOne; + + // otherwise, stochiastically treat each colorless card as 1/5 of a card of the missing color + return (RandomUtil.nextDouble() > Math.pow(0.8, colorlessCountPlusOne)); + } + + private static final ObjectColor COLORLESS = new ObjectColor(); + + protected boolean validateUncommonColors(List booster) { + List uncommonColors = booster.stream() + .filter(card -> card.getRarity() == Rarity.UNCOMMON) + .map(ExpansionSet::getColorForValidate) + .collect(Collectors.toList()); + + // if there are only two uncommons, they can be the same color + if (uncommonColors.size() < 3) return true; + // boosters of artifact sets can have all colorless uncommons + if (uncommonColors.contains(COLORLESS)) return true; + // otherwise, reject if all uncommons are the same color combination + return (new HashSet<>(uncommonColors).size() > 1); + } + + protected boolean checkMythic() { return ratioBoosterMythic > 0 && ratioBoosterMythic * RandomUtil.nextDouble() <= 1; } - public List createPartnerBooster() { - - List booster = new ArrayList<>(); - - boolean partnerAllowed = true; - - List uncommons = getCardsByRarity(Rarity.UNCOMMON); - for (int i = 0; i < numBoosterUncommon; i++) { - while (true) { - addToBooster(booster, uncommons); - int check = addMissingPartner(booster, partnerAllowed, numBoosterUncommon - 1, i); - if (check == 1) { - break; - } - if (check == 2) { - partnerAllowed = false; - //Be sure to account for the added card - if (i != numBoosterUncommon - 1) { - i += 1; - } - break; - } - } - } - - int numSpecialCommons = getNumberOfSpecialCommons(); - int numCommonsToGenerate = numBoosterCommon - numSpecialCommons; - - List commons = getCardsByRarity(Rarity.COMMON); - for (int i = 0; i < numCommonsToGenerate; i++) { - addToBooster(booster, commons); - } - - List rares = getCardsByRarity(Rarity.RARE); - List mythics = getCardsByRarity(Rarity.MYTHIC); - for (int i = 0; i < numBoosterRare; i++) { - if (checkMythic()) { - while (true) { - addToBooster(booster, mythics); - int check = addMissingPartner(booster, partnerAllowed, -1, 1); - if (check == 1) { - break; - } - if (check == 2) { - partnerAllowed = false; - break; - } - } - } else { - while (true) { - addToBooster(booster, rares); - int check = addMissingPartner(booster, partnerAllowed, -1, 1); - if (check == 1) { - break; - } - if (check == 2) { - partnerAllowed = false; - break; - } - } - } - } - - if (numBoosterLands > 0) { - List specialLands = getSpecialLand(); - List basicLands = getCardsByRarity(Rarity.LAND); - for (int i = 0; i < numBoosterLands; i++) { - if (ratioBoosterSpecialLand > 0 && RandomUtil.nextInt(ratioBoosterSpecialLand) < ratioBoosterSpecialLandNumerator && specialLands != null) { - addToBooster(booster, specialLands); - } else { - addToBooster(booster, basicLands); - } - } - } - - return booster; + protected boolean checkSpecialMythic() { + return ratioBoosterSpecialMythic > 0 && ratioBoosterSpecialMythic * RandomUtil.nextDouble() <= 1; } public List tryBooster() { @@ -477,40 +391,52 @@ public abstract class ExpansionSet implements Serializable { } if (numBoosterLands > 0) { - List specialLands = getSpecialLand(); + List specialLands = getSpecialCardsByRarity(Rarity.LAND); List basicLands = getCardsByRarity(Rarity.LAND); for (int i = 0; i < numBoosterLands; i++) { - if (ratioBoosterSpecialLand > 0 && RandomUtil.nextInt(ratioBoosterSpecialLand) < ratioBoosterSpecialLandNumerator && specialLands != null) { + if (ratioBoosterSpecialLand > 0 && RandomUtil.nextInt(ratioBoosterSpecialLand) < ratioBoosterSpecialLandNumerator) { addToBooster(booster, specialLands); } else { addToBooster(booster, basicLands); } } } - int numSpecialCommons = getNumberOfSpecialCommons(); - int numCommonsToGenerate = numBoosterCommon - numSpecialCommons; + + int numCommonsToGenerate = numBoosterCommon; + int numSpecialToGenerate = numBoosterSpecial; + if (ratioBoosterSpecialCommon > 0 && RandomUtil.nextInt(ratioBoosterSpecialCommon) < 1) { + --numCommonsToGenerate; + ++numSpecialToGenerate; + } List commons = getCardsByRarity(Rarity.COMMON); for (int i = 0; i < numCommonsToGenerate; i++) { addToBooster(booster, commons); } - if (numSpecialCommons > 0) { // e.g. used to conditionally replace common cards in the booster - addSpecialCommon(booster, numSpecialCommons); + int numUncommonsToGenerate = numBoosterUncommon; + int numRaresToGenerate = numBoosterRare; + if (ratioBoosterSpecialRare > 0) { + Rarity specialRarity = Rarity.UNCOMMON; + if (ratioBoosterSpecialRare * RandomUtil.nextDouble() <= 1) { + specialRarity = (checkSpecialMythic() ? Rarity.MYTHIC : Rarity.RARE); + --numRaresToGenerate; + } else { + --numUncommonsToGenerate; + } + addToBooster(booster, getSpecialCardsByRarity(specialRarity)); } List uncommons = getCardsByRarity(Rarity.UNCOMMON); - for (int i = 0; i < numBoosterUncommon; i++) { + for (int i = 0; i < numUncommonsToGenerate; i++) { addToBooster(booster, uncommons); } - List rares = getCardsByRarity(Rarity.RARE); - List mythics = getCardsByRarity(Rarity.MYTHIC); - for (int i = 0; i < numBoosterRare; i++) { - if (checkMythic()) { - addToBooster(booster, mythics); - } else { - addToBooster(booster, rares); + if (numRaresToGenerate > 0) { + List rares = getCardsByRarity(Rarity.RARE); + List mythics = getCardsByRarity(Rarity.MYTHIC); + for (int i = 0; i < numRaresToGenerate; i++) { + addToBooster(booster, checkMythic() ? mythics : rares); } } @@ -518,8 +444,8 @@ public abstract class ExpansionSet implements Serializable { addDoubleFace(booster); } - if (numBoosterSpecial > 0) { - addSpecial(booster); + if (numSpecialToGenerate > 0) { + addSpecialCards(booster, numSpecialToGenerate); } return booster; @@ -528,21 +454,27 @@ public abstract class ExpansionSet implements Serializable { /* add double faced card for Innistrad booster * rarity near as the normal distribution */ - public void addDoubleFace(List booster) { + protected void addDoubleFace(List booster) { + Rarity rarity; for (int i = 0; i < numBoosterDoubleFaced; i++) { - CardCriteria criteria = new CardCriteria(); - criteria.setCodes(this.code).doubleFaced(true); - if (RandomUtil.nextInt(15) < 10) { - criteria.rarities(Rarity.COMMON); - } else if (RandomUtil.nextInt(5) < 4) { - criteria.rarities(Rarity.UNCOMMON); - } else if (RandomUtil.nextInt(8) < 7) { - criteria.rarities(Rarity.RARE); + int rarityKey = RandomUtil.nextInt(121); + if (rarityKey < 66) { + rarity = Rarity.COMMON; + } else if (rarityKey < 108) { + rarity = Rarity.UNCOMMON; + } else if (rarityKey < 120) { + rarity = Rarity.RARE; } else { - criteria.rarities(Rarity.MYTHIC); + rarity = Rarity.MYTHIC; } - List doubleFacedCards = CardRepository.instance.findCards(criteria); - addToBooster(booster, doubleFacedCards); + addToBooster(booster, getSpecialCardsByRarity(rarity)); + } + } + + protected void addSpecialCards(List booster, int number) { + List specialCards = getCardsByRarity(Rarity.SPECIAL); + for (int i = 0; i < number; i++) { + addToBooster(booster, specialCards); } } @@ -551,75 +483,6 @@ public abstract class ExpansionSet implements Serializable { return new GregorianCalendar(year, month - 1, day).getTime(); } - /** - * Can be overwritten if sometimes special cards will be generated instead - * of common slots - * - * @return - */ - public int getNumberOfSpecialCommons() { - return 0; - } - - /** - * Can be overwritten to add a replacement for common card in boosters - * - * @param booster - * @param number - */ - public void addSpecialCommon(List booster, int number) { - - } - - private void addSpecial(List booster) { - int specialCards = 0; - List specialBonus = getSpecialBonus(); - specialCards += specialBonus.size(); - - List specialMythic = getSpecialMythic(); - specialCards += specialMythic.size(); - - List specialRare = getSpecialRare(); - specialCards += specialRare.size(); - - List specialUncommon = getSpecialUncommon(); - specialCards += specialUncommon.size(); - - List specialCommon = getSpecialCommon(); - specialCards += specialCommon.size(); - - if (specialCards > 0) { - for (int i = 0; i < numBoosterSpecial; i++) { - if (!specialCommon.isEmpty() - && RandomUtil.nextInt(15) < 10) { - addToBooster(booster, specialCommon); - continue; - } - if (!specialUncommon.isEmpty() - && RandomUtil.nextInt(4) < 3) { - addToBooster(booster, specialUncommon); - continue; - } - if (!specialRare.isEmpty() - && RandomUtil.nextInt(8) < 7) { - addToBooster(booster, specialRare); - continue; - } - if (!specialMythic.isEmpty()) { - if (specialBonus.isEmpty() || RandomUtil.nextInt(3) < 2) { - addToBooster(booster, specialMythic); - continue; - } - } - if (!specialBonus.isEmpty()) { - addToBooster(booster, specialBonus); - continue; - } - i--; - } - } - } - public boolean hasBoosters() { return hasBoosters; } @@ -628,58 +491,90 @@ public abstract class ExpansionSet implements Serializable { return hasBasicLands; } - public List getCardsByRarity(Rarity rarity) { - List savedCardsInfos = savedCards.get(rarity); - if (savedCardsInfos == null) { - CardCriteria criteria = new CardCriteria(); - if (rarity == Rarity.LAND && !hasBasicLands && parentSet != null) { - // get basic lands from parent set if this set doesn't have them - criteria.setCodes(parentSet.code); - } else { - criteria.setCodes(this.code); - } - criteria.rarities(rarity); - if (numBoosterDoubleFaced > -1) { - criteria.doubleFaced(false); - } - savedCardsInfos = CardRepository.instance.findCards(criteria); - // Workaround after card number is numeric - if (maxCardNumberInBooster != Integer.MAX_VALUE) { - savedCardsInfos.removeIf(next -> next.getCardNumberAsInt() > maxCardNumberInBooster && rarity != Rarity.LAND); - } - - savedCards.put(rarity, savedCardsInfos); + public final synchronized List getCardsByRarity(Rarity rarity) { + List savedCardInfos = savedCards.get(rarity); + if (savedCardInfos == null) { + savedCardInfos = findCardsByRarity(rarity); + savedCards.put(rarity, savedCardInfos); } // Return a copy of the saved cards information, as not to let modify the original. - return new ArrayList<>(savedCardsInfos); + return new ArrayList<>(savedCardInfos); } - public List getSpecialCommon() { - return new ArrayList<>(); + public final synchronized List getSpecialCardsByRarity(Rarity rarity) { + List savedCardInfos = savedSpecialCards.get(rarity); + if (savedCardInfos == null) { + savedCardInfos = findSpecialCardsByRarity(rarity); + savedSpecialCards.put(rarity, savedCardInfos); + } + // Return a copy of the saved cards information, as not to let modify the original. + return new ArrayList<>(savedCardInfos); } - public List getSpecialUncommon() { - return new ArrayList<>(); + protected List findCardsByRarity(Rarity rarity) { + // get basic lands from parent set if this set doesn't have them + if (rarity == Rarity.LAND && !hasBasicLands && parentSet != null) { + return parentSet.getCardsByRarity(rarity); + } + + List cardInfos = CardRepository.instance.findCards(new CardCriteria() + .setCodes(this.code) + .rarities(rarity) + .maxCardNumber(maxCardNumberInBooster)); + + cardInfos.removeIf(next -> ( + next.getCardNumber().contains("*") + || next.getCardNumber().contains("+"))); + + // special slot cards must not also appear in regular slots of their rarity + // special land slot cards must not appear in regular common slots either + List specialCards = getSpecialCardsByRarity(rarity); + if (rarity == Rarity.COMMON && ratioBoosterSpecialLand > 0) { + specialCards.addAll(getSpecialCardsByRarity(Rarity.LAND)); + } + cardInfos.removeAll(specialCards); + return cardInfos; } - public List getSpecialRare() { - return new ArrayList<>(); - } + /** + * "Special cards" are cards that have common/uncommon/rare/mythic rarities + * but can only appear in a specific slot in boosters. Examples are DFCs in + * Innistrad sets and common nonbasic lands in many post-2018 sets. + * + * Note that Rarity.SPECIAL and Rarity.BONUS cards are not normally treated + * as "special cards" because by default boosters don't even have slots for + * those rarities. + * + * Also note that getCardsByRarity calls getSpecialCardsByRarity to exclude + * special cards from non-special booster slots, so sets that override this + * method must not call getCardsByRarity in it or infinite recursion will occur. + */ + protected List findSpecialCardsByRarity(Rarity rarity) { + List cardInfos = new ArrayList<>(); - public List getSpecialMythic() { - return new ArrayList<>(); - } + // if set has special land slot, assume all common lands are special cards + if (rarity == Rarity.LAND && ratioBoosterSpecialLand > 0) { + cardInfos.addAll(CardRepository.instance.findCards(new CardCriteria() + .setCodes(this.code) + .rarities(Rarity.COMMON) + .types(CardType.LAND) + .maxCardNumber(maxCardNumberInBooster))); + } - public List getSpecialBonus() { - return new ArrayList<>(); - } + // if set has special slot(s) for DFCs, they are special cards + if (numBoosterDoubleFaced > 0) { + cardInfos.addAll(CardRepository.instance.findCards(new CardCriteria() + .setCodes(this.code) + .rarities(rarity) + .doubleFaced(true) + .maxCardNumber(maxCardNumberInBooster))); + } - public List getSpecialLand() { - return new ArrayList<>(); - } + cardInfos.removeIf(next -> ( + next.getCardNumber().contains("*") + || next.getCardNumber().contains("+"))); - public void removeSavedCards() { - savedCards.clear(); + return cardInfos; } public int getMaxCardNumberInBooster() { diff --git a/Mage/src/main/java/mage/cards/ModalDoubleFacesCardHalfImpl.java b/Mage/src/main/java/mage/cards/ModalDoubleFacesCardHalfImpl.java index ce24d5e81e9..c6de77a716b 100644 --- a/Mage/src/main/java/mage/cards/ModalDoubleFacesCardHalfImpl.java +++ b/Mage/src/main/java/mage/cards/ModalDoubleFacesCardHalfImpl.java @@ -38,8 +38,7 @@ public class ModalDoubleFacesCardHalfImpl extends CardImpl implements ModalDoubl @Override public String getImageName() { - // TODO: own name? - return parentCard.getImageName(); + return name; } @Override diff --git a/Mage/src/main/java/mage/cards/basiclands/Forest.java b/Mage/src/main/java/mage/cards/basiclands/Forest.java index 2092d60e98c..c47de36b2a6 100644 --- a/Mage/src/main/java/mage/cards/basiclands/Forest.java +++ b/Mage/src/main/java/mage/cards/basiclands/Forest.java @@ -15,7 +15,7 @@ import mage.cards.CardSetInfo; public class Forest extends BasicLand { public Forest(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new GreenManaAbility()); - this.frameColor = ObjectColor.GREEN; + this.frameColor.setGreen(true); } public Forest(final Forest land) { diff --git a/Mage/src/main/java/mage/cards/basiclands/Island.java b/Mage/src/main/java/mage/cards/basiclands/Island.java index 063123bc022..d0ecb8d8ec9 100644 --- a/Mage/src/main/java/mage/cards/basiclands/Island.java +++ b/Mage/src/main/java/mage/cards/basiclands/Island.java @@ -15,7 +15,7 @@ import mage.cards.CardSetInfo; public class Island extends BasicLand { public Island(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new BlueManaAbility()); - this.frameColor = ObjectColor.BLUE; + this.frameColor.setBlue(true); } public Island(Island land) { diff --git a/Mage/src/main/java/mage/cards/basiclands/Mountain.java b/Mage/src/main/java/mage/cards/basiclands/Mountain.java index 5db835d2324..e16c4ac34aa 100644 --- a/Mage/src/main/java/mage/cards/basiclands/Mountain.java +++ b/Mage/src/main/java/mage/cards/basiclands/Mountain.java @@ -15,7 +15,7 @@ import mage.cards.CardSetInfo; public class Mountain extends BasicLand { public Mountain(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new RedManaAbility()); - this.frameColor = ObjectColor.RED; + this.frameColor.setRed(true); } public Mountain(Mountain land) { diff --git a/Mage/src/main/java/mage/cards/basiclands/Plains.java b/Mage/src/main/java/mage/cards/basiclands/Plains.java index cef5b07cc4e..4f4151e71bd 100644 --- a/Mage/src/main/java/mage/cards/basiclands/Plains.java +++ b/Mage/src/main/java/mage/cards/basiclands/Plains.java @@ -15,7 +15,7 @@ import mage.cards.CardSetInfo; public class Plains extends BasicLand { public Plains(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new WhiteManaAbility()); - this.frameColor = ObjectColor.WHITE; + this.frameColor.setWhite(true); } public Plains(Plains land) { diff --git a/Mage/src/main/java/mage/cards/basiclands/Swamp.java b/Mage/src/main/java/mage/cards/basiclands/Swamp.java index 9ea15701f87..5e866c99d6b 100644 --- a/Mage/src/main/java/mage/cards/basiclands/Swamp.java +++ b/Mage/src/main/java/mage/cards/basiclands/Swamp.java @@ -15,7 +15,7 @@ import mage.cards.CardSetInfo; public class Swamp extends BasicLand { public Swamp(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new BlackManaAbility()); - this.frameColor = ObjectColor.BLACK; + this.frameColor.setBlack(true); } public Swamp(Swamp land) { diff --git a/Mage/src/main/java/mage/cards/mock/MockCard.java b/Mage/src/main/java/mage/cards/mock/MockCard.java index 0a6b411344c..cb763203f53 100644 --- a/Mage/src/main/java/mage/cards/mock/MockCard.java +++ b/Mage/src/main/java/mage/cards/mock/MockCard.java @@ -63,7 +63,6 @@ public class MockCard extends CardImpl { this.flipCard = card.isFlipCard(); - this.transformable = card.isDoubleFaced(); this.nightCard = card.isNightCard(); if (card.getSecondSideName() != null && !card.getSecondSideName().isEmpty()) { this.secondSideCard = new MockCard(CardRepository.instance.findCardWPreferredSet(card.getSecondSideName(), card.getSetCode(), false)); @@ -180,6 +179,6 @@ public class MockCard extends CardImpl { @Override public boolean isTransformable() { // must enable toggle mode in deck editor (switch between card sides); - return super.isTransformable() || this.isModalDoubleFacesCard; + return super.isTransformable() || this.isModalDoubleFacesCard || this.secondSideCard != null; } } diff --git a/Mage/src/main/java/mage/cards/mock/MockSplitCard.java b/Mage/src/main/java/mage/cards/mock/MockSplitCard.java index d0b3b861131..de5e116b608 100644 --- a/Mage/src/main/java/mage/cards/mock/MockSplitCard.java +++ b/Mage/src/main/java/mage/cards/mock/MockSplitCard.java @@ -37,7 +37,6 @@ public class MockSplitCard extends SplitCard { this.color = card.getColor(); this.flipCard = card.isFlipCard(); - this.transformable = card.isDoubleFaced(); this.nightCard = card.isNightCard(); if (card.getSecondSideName() != null && !card.getSecondSideName().isEmpty()) { this.secondSideCard = new MockCard(CardRepository.instance.findCardWPreferredSet(card.getSecondSideName(), card.getSetCode(), false)); diff --git a/Mage/src/main/java/mage/cards/repository/CardCriteria.java b/Mage/src/main/java/mage/cards/repository/CardCriteria.java index 94d73d0da28..e9a5c044ba9 100644 --- a/Mage/src/main/java/mage/cards/repository/CardCriteria.java +++ b/Mage/src/main/java/mage/cards/repository/CardCriteria.java @@ -5,6 +5,8 @@ import com.j256.ormlite.stmt.SelectArg; import com.j256.ormlite.stmt.Where; import mage.constants.CardType; import mage.constants.Rarity; +import mage.constants.SubType; +import mage.constants.SuperType; import java.sql.SQLException; import java.util.ArrayList; @@ -23,11 +25,12 @@ public class CardCriteria { private final List ignoreSetCodes; // sets to ignore, use with little amount of sets (example: ignore sets with snow lands) private final List types; private final List notTypes; - private final List supertypes; - private final List notSupertypes; - private final List subtypes; + private final List supertypes; + private final List notSupertypes; + private final List subtypes; private final List rarities; private Boolean doubleFaced; + private Boolean modalDoubleFaced; private boolean black; private boolean blue; private boolean green; @@ -38,6 +41,7 @@ public class CardCriteria { private String sortBy; private Long start; private Long count; + // compare numerical card numbers (123b -> 123) private int minCardNumber; private int maxCardNumber; @@ -97,6 +101,11 @@ public class CardCriteria { return this; } + public CardCriteria modalDoubleFaced(boolean modalDoubleFaced) { + this.modalDoubleFaced = modalDoubleFaced; + return this; + } + public CardCriteria name(String name) { this.name = name; return this; @@ -152,17 +161,17 @@ public class CardCriteria { return this; } - public CardCriteria supertypes(String... supertypes) { + public CardCriteria supertypes(SuperType... supertypes) { this.supertypes.addAll(Arrays.asList(supertypes)); return this; } - public CardCriteria notSupertypes(String... supertypes) { + public CardCriteria notSupertypes(SuperType... supertypes) { this.notSupertypes.addAll(Arrays.asList(supertypes)); return this; } - public CardCriteria subtypes(String... subtypes) { + public CardCriteria subtypes(SubType... subtypes) { this.subtypes.addAll(Arrays.asList(subtypes)); return this; } @@ -212,6 +221,11 @@ public class CardCriteria { clausesCount++; } + if (modalDoubleFaced != null) { + where.eq("modalDoubleFacesCard", modalDoubleFaced); + clausesCount++; + } + for (Rarity rarity : rarities) { where.eq("rarity", rarity); } @@ -251,17 +265,17 @@ public class CardCriteria { clausesCount++; } - for (String superType : supertypes) { - where.like("supertypes", new SelectArg('%' + superType + '%')); + for (SuperType superType : supertypes) { + where.like("supertypes", new SelectArg('%' + superType.name() + '%')); clausesCount++; } - for (String subType : notSupertypes) { - where.not().like("supertypes", new SelectArg('%' + subType + '%')); + for (SuperType superType : notSupertypes) { + where.not().like("supertypes", new SelectArg('%' + superType.name() + '%')); clausesCount++; } - for (String subType : subtypes) { - where.like("subtypes", new SelectArg('%' + subType + '%')); + for (SubType subType : subtypes) { + where.like("subtypes", new SelectArg('%' + subType.toString() + '%')); clausesCount++; } @@ -302,12 +316,12 @@ public class CardCriteria { } if (minCardNumber != Integer.MIN_VALUE) { - where.ge("cardNumber", minCardNumber); + where.ge("cardNumberAsInt", minCardNumber); clausesCount++; } if (maxCardNumber != Integer.MAX_VALUE) { - where.le("cardNumber", maxCardNumber); + where.le("cardNumberAsInt", maxCardNumber); clausesCount++; } @@ -389,15 +403,15 @@ public class CardCriteria { return notTypes; } - public List getSupertypes() { + public List getSupertypes() { return supertypes; } - public List getNotSupertypes() { + public List getNotSupertypes() { return notSupertypes; } - public List getSubtypes() { + public List getSubtypes() { return subtypes; } @@ -409,6 +423,10 @@ public class CardCriteria { return doubleFaced; } + public Boolean getModalDoubleFaced() { + return modalDoubleFaced; + } + public boolean isBlack() { return black; } diff --git a/Mage/src/main/java/mage/cards/repository/CardInfo.java b/Mage/src/main/java/mage/cards/repository/CardInfo.java index 4576179609a..55677c9fe8b 100644 --- a/Mage/src/main/java/mage/cards/repository/CardInfo.java +++ b/Mage/src/main/java/mage/cards/repository/CardInfo.java @@ -4,9 +4,7 @@ import com.j256.ormlite.field.DataType; import com.j256.ormlite.field.DatabaseField; import com.j256.ormlite.table.DatabaseTable; import mage.ObjectColor; -import mage.abilities.Ability; import mage.abilities.SpellAbility; -import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; import mage.cards.*; import mage.cards.mock.MockCard; import mage.cards.mock.MockSplitCard; @@ -46,6 +44,11 @@ public class CardInfo { protected String setCode; @DatabaseField(indexName = "setCode_cardNumber_index") protected String cardNumber; + /** + * Fast access to numerical card number (number without prefix/postfix: 123b -> 123) + */ + @DatabaseField(indexName = "cardNumberAsInt_index") + protected int cardNumberAsInt; @DatabaseField(indexName = "className_index") protected String className; @DatabaseField @@ -124,6 +127,7 @@ public class CardInfo { this.name = card.getName(); this.lower_name = name.toLowerCase(Locale.ENGLISH); this.cardNumber = card.getCardNumber(); + this.cardNumberAsInt = CardUtil.parseCardNumberAsInt(card.getCardNumber()); this.setCode = card.getExpansionSetCode(); this.className = card.getClass().getCanonicalName(); this.power = card.getPower().toString(); @@ -240,13 +244,15 @@ public class CardInfo { // Starting loyalty if (card.isPlaneswalker()) { - for (Ability ab : card.getAbilities()) { - if (ab instanceof PlaneswalkerEntersWithLoyaltyCountersAbility) { - this.startingLoyalty = "" + ((PlaneswalkerEntersWithLoyaltyCountersAbility) ab).getStartingLoyalty(); - } - } - if (this.startingLoyalty == null) { - this.startingLoyalty = ""; + switch (card.getStartingLoyalty()) { + case -2: + this.startingLoyalty = "X"; + break; + case -1: + this.startingLoyalty = ""; + break; + default: + this.startingLoyalty = "" + card.getStartingLoyalty(); } } else { this.startingLoyalty = ""; @@ -424,7 +430,7 @@ public class CardInfo { } public int getCardNumberAsInt() { - return CardUtil.parseCardNumberAsInt(cardNumber); + return cardNumberAsInt; } public boolean isSplitCard() { @@ -479,6 +485,16 @@ public class CardInfo { return modalDoubleFacesSecondSideName; } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || !(o instanceof CardInfo)) return false; + CardInfo other = (CardInfo) o; + return (this.name.equals(other.name) + && this.setCode.equals(other.setCode) + && this.cardNumber.equals(other.cardNumber)); + } + @Override public String toString() { return String.format("%s (%s, %s)", getName(), getSetCode(), getCardNumber()); diff --git a/Mage/src/main/java/mage/cards/repository/CardRepository.java b/Mage/src/main/java/mage/cards/repository/CardRepository.java index 15de5a22743..52b3cac6dbc 100644 --- a/Mage/src/main/java/mage/cards/repository/CardRepository.java +++ b/Mage/src/main/java/mage/cards/repository/CardRepository.java @@ -34,7 +34,7 @@ public enum CardRepository { private static final String JDBC_URL = "jdbc:h2:file:./db/cards.h2;AUTO_SERVER=TRUE"; private static final String VERSION_ENTITY_NAME = "card"; // raise this if db structure was changed - private static final long CARD_DB_VERSION = 53; + private static final long CARD_DB_VERSION = 54; // raise this if new cards were added to the server private static final long CARD_CONTENT_VERSION = 241; private Dao cardDao; @@ -442,7 +442,6 @@ public enum CardRepository { cards = findCards(name); } if (!cards.isEmpty()) { - CardInfo cardToUse = null; for (CardInfo cardinfo : cards) { if (cardinfo.getSetCode() != null && expansion != null && expansion.equalsIgnoreCase(cardinfo.getSetCode())) { return cardinfo; @@ -470,9 +469,25 @@ public enum CardRepository { if (limitByMaxAmount > 0) { queryBuilder.limit(limitByMaxAmount); } - return cardDao.query(queryBuilder.prepare()); + + List result = cardDao.query(queryBuilder.prepare()); + + // Got no results, could be because the name referred to a double-face cards (e.g. Malakir Rebirth // Malakir Mire) + if (result.isEmpty() && name.contains(" // ")) { + // If there IS a " // " then the card could be either a double-face card (e.g. Malakir Rebirth // Malakir Mire) + // OR a split card (e.g. Assault // Battery). + // Since you can't tell based on the name, we split the text based on " // " and try the operation again with + // the string on the left side of " // " (double-faced cards are stored under the name on the left of the " // "). + queryBuilder.where().eq("name", new SelectArg(name.split(" // ", 2)[0])); + + result = cardDao.query(queryBuilder.prepare()); + } + + return result; } catch (SQLException ex) { + Logger.getLogger(CardRepository.class).error("Error during execution of raw sql statement", ex); } + return Collections.emptyList(); } @@ -482,6 +497,7 @@ public enum CardRepository { queryBuilder.where().eq("className", new SelectArg(canonicalClassName)); return cardDao.query(queryBuilder.prepare()); } catch (SQLException ex) { + Logger.getLogger(CardRepository.class).error("Error during execution of raw sql statement", ex); } return Collections.emptyList(); } @@ -492,19 +508,34 @@ public enum CardRepository { GenericRawResults rawResults = cardDao.queryRaw( "select * from " + CardRepository.VERSION_ENTITY_NAME + " where lower_name = '" + sqlName + '\'', cardDao.getRawRowMapper()); - List result = new ArrayList<>(); - for (CardInfo cardinfo : rawResults) { - result.add(cardinfo); + + List result = rawResults.getResults(); + + // Got no results, could be because the name referred to a double-face cards (e.g. Malakir Rebirth // Malakir Mire) + if (result.isEmpty() && sqlName.contains(" // ")) { + // If there IS a " // " then the card could be either a double-face card (e.g. Malakir Rebirth // Malakir Mire) + // OR a split card (e.g. Assault // Battery). + // Since you can't tell based on the name, we split the text based on " // " and try the operation again with + // the string on the left side of " // " (double-faced cards are stored under the name on the left of the " // "). + String leftCardName = sqlName.split(" // ", 2)[0]; + + GenericRawResults rawResults2 = cardDao.queryRaw( + "select * from " + CardRepository.VERSION_ENTITY_NAME + " where lower_name = '" + leftCardName + '\'', + cardDao.getRawRowMapper()); + + result = rawResults2.getResults(); } + return result; } catch (SQLException ex) { Logger.getLogger(CardRepository.class).error("Error during execution of raw sql statement", ex); } + return Collections.emptyList(); } /** - * Warning, don't use db functions in card's code - it generate heavy db loading in AI simulations. If you + * Warning, don't use db functions in card's code - it generates heavy db loading in AI simulations. If you * need that feature then check for simulation mode. See https://github.com/magefree/mage/issues/7014 * * @param criteria diff --git a/Mage/src/main/java/mage/choices/VoteHandler.java b/Mage/src/main/java/mage/choices/VoteHandler.java index ba561d770f3..dffd2f41f5c 100644 --- a/Mage/src/main/java/mage/choices/VoteHandler.java +++ b/Mage/src/main/java/mage/choices/VoteHandler.java @@ -6,6 +6,7 @@ import mage.game.Game; import mage.game.events.VoteEvent; import mage.game.events.VotedEvent; import mage.players.Player; +import mage.util.CardUtil; import java.util.*; import java.util.stream.Collectors; @@ -86,7 +87,7 @@ public abstract class VoteHandler { .stream() .flatMap(votesList -> votesList.getValue().stream()) .forEach(vote -> { - totalVotes.compute(vote, (u, i) -> i == null ? 1 : Integer.sum(i, 1)); + totalVotes.compute(vote, CardUtil::setOrIncrementValue); }); Set winners = this.getMostVoted(); @@ -157,7 +158,7 @@ public abstract class VoteHandler { .values() .stream() .flatMap(Collection::stream) - .forEach(t -> map.compute(t, (s, i) -> i == null ? 1 : Integer.sum(i, 1))); + .forEach(t -> map.compute(t, CardUtil::setOrIncrementValue)); int max = map.values().stream().mapToInt(x -> x).max().orElse(0); return map .entrySet() diff --git a/Mage/src/main/java/mage/constants/Duration.java b/Mage/src/main/java/mage/constants/Duration.java index 7e9e43b5dfe..243cb7b28e5 100644 --- a/Mage/src/main/java/mage/constants/Duration.java +++ b/Mage/src/main/java/mage/constants/Duration.java @@ -7,6 +7,7 @@ public enum Duration { OneUse("", true, true), EndOfGame("for the rest of the game", false, false), WhileOnBattlefield("", false, false), + WhileControlled("for as long as you control {this}", true, false), WhileOnStack("", false, true), WhileInGraveyard("", false, false), EndOfTurn("until end of turn", true, true), diff --git a/Mage/src/main/java/mage/constants/SpellAbilityCastMode.java b/Mage/src/main/java/mage/constants/SpellAbilityCastMode.java index 9fa3da28629..faa191afed3 100644 --- a/Mage/src/main/java/mage/constants/SpellAbilityCastMode.java +++ b/Mage/src/main/java/mage/constants/SpellAbilityCastMode.java @@ -12,7 +12,8 @@ public enum SpellAbilityCastMode { NORMAL("Normal"), MADNESS("Madness"), FLASHBACK("Flashback"), - BESTOW("Bestow"); + BESTOW("Bestow"), + DISTURB("Disturb"); private final String text; diff --git a/Mage/src/main/java/mage/constants/SubType.java b/Mage/src/main/java/mage/constants/SubType.java index 70374d6f864..31cb8739ffa 100644 --- a/Mage/src/main/java/mage/constants/SubType.java +++ b/Mage/src/main/java/mage/constants/SubType.java @@ -39,6 +39,7 @@ public enum SubType { SHARD("Shard", SubTypeSet.EnchantmentType), SHRINE("Shrine", SubTypeSet.EnchantmentType), // 205.3g: Artifacts have their own unique set of subtypes; these subtypes are called artifact types. + BLOOD("Blood", SubTypeSet.ArtifactType), CLUE("Clue", SubTypeSet.ArtifactType), CONTRAPTION("Contraption", SubTypeSet.ArtifactType), EQUIPMENT("Equipment", SubTypeSet.ArtifactType), @@ -170,6 +171,7 @@ public enum SubType { GRAVEBORN("Graveborn", SubTypeSet.CreatureType), GREMLIN("Gremlin", SubTypeSet.CreatureType), GRIFFIN("Griffin", SubTypeSet.CreatureType), + GUEST("Guest", SubTypeSet.CreatureType), GUNGAN("Gungan", SubTypeSet.CreatureType, true), // Star Wars // H HAG("Hag", SubTypeSet.CreatureType), @@ -425,6 +427,7 @@ public enum SubType { HUATLI("Huatli", SubTypeSet.PlaneswalkerType), JACE("Jace", SubTypeSet.PlaneswalkerType), JESKA("Jeska", SubTypeSet.PlaneswalkerType), + KAITO("Kaito", SubTypeSet.PlaneswalkerType), KARN("Karn", SubTypeSet.PlaneswalkerType), KASMINA("Kasmina", SubTypeSet.PlaneswalkerType), KAYA("Kaya", SubTypeSet.PlaneswalkerType), diff --git a/Mage/src/main/java/mage/constants/SubTypeSet.java b/Mage/src/main/java/mage/constants/SubTypeSet.java index 840b2ca2825..0c848f8281a 100644 --- a/Mage/src/main/java/mage/constants/SubTypeSet.java +++ b/Mage/src/main/java/mage/constants/SubTypeSet.java @@ -3,9 +3,22 @@ package mage.constants; public enum SubTypeSet { CreatureType, SpellType, - BasicLandType, - NonBasicLandType, + BasicLandType(true), + NonBasicLandType(true), EnchantmentType, ArtifactType, - PlaneswalkerType -} \ No newline at end of file + PlaneswalkerType; + private final boolean isLand; + + SubTypeSet() { + this(false); + } + + SubTypeSet(boolean isLand) { + this.isLand = isLand; + } + + public boolean isLand() { + return isLand; + } +} diff --git a/Mage/src/main/java/mage/counters/CounterType.java b/Mage/src/main/java/mage/counters/CounterType.java index 3a4612fb276..c64da20a7be 100644 --- a/Mage/src/main/java/mage/counters/CounterType.java +++ b/Mage/src/main/java/mage/counters/CounterType.java @@ -21,6 +21,7 @@ public enum CounterType { AWAKENING("awakening"), BLAZE("blaze"), BLOOD("blood"), + BLOODLINE("bloodline"), BOOK("book"), BOUNTY("bounty"), BRIBERY("bribery"), @@ -34,6 +35,7 @@ public enum CounterType { CORPSE("corpse"), CORRUPTION("corruption"), CREDIT("credit"), + CROAK("croak"), CRYSTAL("crystal"), CUBE("cube"), CURRENCY("currency"), @@ -91,10 +93,13 @@ public enum CounterType { INDESTRUCTIBLE("indestructible"), INFECTION("infection"), INTERVENTION("intervention"), + INVITATION("invitation"), ISOLATION("isolation"), JAVELIN("javelin"), + JUDGMENT("judgment"), KNOWLEDGE("knowledge"), KI("ki"), + KICK("kick"), LANDMARK("landmark"), LEVEL("level"), LIFELINK("lifelink"), @@ -160,6 +165,7 @@ public enum CounterType { STORAGE("storage"), STRIFE("strife"), STUDY("study"), + SUSPECT("suspect"), TASK("task"), THEFT("theft"), TIDE("tide"), diff --git a/Mage/src/main/java/mage/designations/Designation.java b/Mage/src/main/java/mage/designations/Designation.java index 142daffc9fc..023c14e1a25 100644 --- a/Mage/src/main/java/mage/designations/Designation.java +++ b/Mage/src/main/java/mage/designations/Designation.java @@ -9,7 +9,6 @@ import mage.abilities.Ability; import mage.abilities.costs.mana.ManaCost; import mage.abilities.costs.mana.ManaCosts; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.text.TextPart; import mage.cards.FrameStyle; import mage.constants.CardType; import mage.constants.SubType; @@ -119,6 +118,7 @@ public abstract class Designation implements MageObject { public void addAbility(Ability ability) { ability.setSourceId(id); abilites.add(ability); + abilites.addAll(ability.getSubAbilities()); } @Override @@ -270,16 +270,6 @@ public abstract class Designation implements MageObject { public void setIsAllCreatureTypes(Game game, boolean value) { } - @Override - public List getTextParts() { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public TextPart addTextPart(TextPart textPart) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - public boolean isUnique() { return unique; } diff --git a/Mage/src/main/java/mage/filter/Filter.java b/Mage/src/main/java/mage/filter/Filter.java index 3f839a0f649..50d0cceeeae 100644 --- a/Mage/src/main/java/mage/filter/Filter.java +++ b/Mage/src/main/java/mage/filter/Filter.java @@ -1,10 +1,10 @@ - package mage.filter; +import mage.filter.predicate.Predicate; +import mage.game.Game; + import java.io.Serializable; import java.util.List; -import mage.filter.predicate.Predicate; -import mage.game.Game; /** * @param @@ -29,5 +29,9 @@ public interface Filter extends Serializable { Filter copy(); + public boolean isLockedFilter(); + + public void setLockedFilter(boolean lockedFilter); + List> getPredicates(); } diff --git a/Mage/src/main/java/mage/filter/FilterImpl.java b/Mage/src/main/java/mage/filter/FilterImpl.java index 704acf389e7..f7e9fe68aed 100644 --- a/Mage/src/main/java/mage/filter/FilterImpl.java +++ b/Mage/src/main/java/mage/filter/FilterImpl.java @@ -67,10 +67,12 @@ public abstract class FilterImpl implements Filter { return message; } + @Override public boolean isLockedFilter() { return lockedFilter; } + @Override public void setLockedFilter(boolean lockedFilter) { this.lockedFilter = lockedFilter; } diff --git a/Mage/src/main/java/mage/filter/StaticFilters.java b/Mage/src/main/java/mage/filter/StaticFilters.java index c0717cec1f2..e69aea0e331 100644 --- a/Mage/src/main/java/mage/filter/StaticFilters.java +++ b/Mage/src/main/java/mage/filter/StaticFilters.java @@ -1,15 +1,18 @@ package mage.filter; +import mage.ObjectColor; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; import mage.constants.TargetController; +import mage.counters.CounterType; import mage.filter.common.*; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.filter.predicate.mageobject.ColorPredicate; import mage.filter.predicate.mageobject.KickedSpellPredicate; import mage.filter.predicate.mageobject.MulticoloredPredicate; -import mage.filter.predicate.permanent.AttackingPredicate; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.filter.predicate.permanent.TappedPredicate; import mage.filter.predicate.permanent.TokenPredicate; @@ -24,16 +27,10 @@ import mage.filter.predicate.permanent.TokenPredicate; */ public final class StaticFilters { - public static final FilterSpiritOrArcaneCard SPIRIT_OR_ARCANE_CARD = new FilterSpiritOrArcaneCard(); + public static final FilterSpiritOrArcaneCard FILTER_SPIRIT_OR_ARCANE_CARD = new FilterSpiritOrArcaneCard(); static { - SPIRIT_OR_ARCANE_CARD.setLockedFilter(true); - } - - public static final FilterEnchantmentPermanent FILTER_ENCHANTMENT_PERMANENT = new FilterEnchantmentPermanent(); - - static { - FILTER_ENCHANTMENT_PERMANENT.setLockedFilter(true); + FILTER_SPIRIT_OR_ARCANE_CARD.setLockedFilter(true); } public static final FilterCard FILTER_CARD = new FilterCard("card"); @@ -115,6 +112,12 @@ public final class StaticFilters { FILTER_CARD_FROM_YOUR_GRAVEYARD.setLockedFilter(true); } + public static final FilterCard FILTER_CARDS_FROM_YOUR_GRAVEYARD = new FilterCard("cards from your graveyard"); + + static { + FILTER_CARDS_FROM_YOUR_GRAVEYARD.setLockedFilter(true); + } + public static final FilterCard FILTER_CARD_INSTANT_OR_SORCERY_FROM_YOUR_GRAVEYARD = new FilterInstantOrSorceryCard("instant or sorcery card from your graveyard"); static { @@ -229,6 +232,19 @@ public final class StaticFilters { FILTER_PERMANENTS.setLockedFilter(true); } + public static final FilterEnchantmentPermanent FILTER_PERMANENT_ENCHANTMENT = new FilterEnchantmentPermanent(); + + static { + FILTER_PERMANENT_ENCHANTMENT.setLockedFilter(true); + } + + + public static final FilterEnchantmentPermanent FILTER_PERMANENT_ENCHANTMENTS = new FilterEnchantmentPermanent("enchantments"); + + static { + FILTER_PERMANENT_ENCHANTMENTS.setLockedFilter(true); + } + public static final FilterArtifactPermanent FILTER_PERMANENT_ARTIFACT = new FilterArtifactPermanent("artifact"); static { @@ -253,6 +269,17 @@ public final class StaticFilters { FILTER_PERMANENT_ARTIFACT_OR_ENCHANTMENT.setLockedFilter(true); } + + public static final FilterControlledPermanent FILTER_PERMANENT_CONTROLLED_ARTIFACT_OR_ENCHANTMENT = new FilterControlledPermanent("artifact or enchantment you control"); + + static { + FILTER_PERMANENT_CONTROLLED_ARTIFACT_OR_ENCHANTMENT.add(Predicates.or( + CardType.ARTIFACT.getPredicate(), + CardType.ENCHANTMENT.getPredicate() + )); + FILTER_PERMANENT_CONTROLLED_ARTIFACT_OR_ENCHANTMENT.setLockedFilter(true); + } + public static final FilterCreaturePermanent FILTER_PERMANENT_ARTIFACT_CREATURE = new FilterArtifactCreaturePermanent(); static { @@ -357,12 +384,24 @@ public final class StaticFilters { FILTER_CONTROLLED_PERMANENT_ARTIFACT_OR_CREATURE.setLockedFilter(true); } + public static final FilterControlledPermanent FILTER_CONTROLLED_PERMANENT_ENCHANTMENT = new FilterControlledEnchantmentPermanent(); + + static { + FILTER_CONTROLLED_PERMANENT_ENCHANTMENT.setLockedFilter(true); + } + public static final FilterControlledPermanent FILTER_CONTROLLED_PERMANENT_LAND = new FilterControlledLandPermanent(); static { FILTER_CONTROLLED_PERMANENT_LAND.setLockedFilter(true); } + public static final FilterControlledPermanent FILTER_CONTROLLED_PERMANENT_A_LAND = new FilterControlledLandPermanent("a land you control"); + + static { + FILTER_CONTROLLED_PERMANENT_A_LAND.setLockedFilter(true); + } + public static final FilterControlledPermanent FILTER_CONTROLLED_PERMANENT_LANDS = new FilterControlledLandPermanent("lands you control"); static { @@ -415,6 +454,13 @@ public final class StaticFilters { FILTER_OPPONENTS_PERMANENT_ARTIFACT_OR_CREATURE.setLockedFilter(true); } + public static final FilterCreaturePermanent FILTER_ANOTHER_CREATURE_TARGET_2 = new FilterCreaturePermanent("another target creature"); + + static { + FILTER_ANOTHER_CREATURE_TARGET_2.add(new AnotherTargetPredicate(2)); + FILTER_ANOTHER_CREATURE_TARGET_2.setLockedFilter(true); + } + public static final FilterCreaturePermanent FILTER_CREATURE_YOU_DONT_CONTROL = new FilterCreaturePermanent("creature you don't control"); static { @@ -422,6 +468,13 @@ public final class StaticFilters { FILTER_CREATURE_YOU_DONT_CONTROL.setLockedFilter(true); } + public static final FilterCreaturePermanent FILTER_CREATURES_YOU_DONT_CONTROL = new FilterCreaturePermanent("creatures you don't control"); + + static { + FILTER_CREATURES_YOU_DONT_CONTROL.add(TargetController.NOT_YOU.getControllerPredicate()); + FILTER_CREATURES_YOU_DONT_CONTROL.setLockedFilter(true); + } + public static final FilterControlledCreaturePermanent FILTER_CONTROLLED_CREATURE = new FilterControlledCreaturePermanent(); static { @@ -507,12 +560,6 @@ public final class StaticFilters { FILTER_PERMANENT_CREATURE.setLockedFilter(true); } - public static final FilterCreaturePermanent FILTER_PERMANENT_CREATURE_A = new FilterCreaturePermanent("a creature"); - - static { - FILTER_PERMANENT_CREATURE_A.setLockedFilter(true); - } - public static final FilterPermanent FILTER_PERMANENT_CREATURE_OR_PLANESWALKER_A = new FilterPermanent("a creature or planeswalker"); static { @@ -554,6 +601,12 @@ public final class StaticFilters { FILTER_PERMANENT_CREATURES.setLockedFilter(true); } + public static final FilterCreaturePermanent FILTER_PERMANENT_ALL_CREATURES = new FilterCreaturePermanent("all creatures"); + + static { + FILTER_PERMANENT_ALL_CREATURES.setLockedFilter(true); + } + public static final FilterCreaturePermanent FILTER_PERMANENT_CREATURES_CONTROLLED = new FilterCreaturePermanent("creatures you control"); static { @@ -561,16 +614,46 @@ public final class StaticFilters { FILTER_PERMANENT_CREATURES_CONTROLLED.setLockedFilter(true); } + public static final FilterCreaturePermanent FILTER_PERMANENT_CREATURE_NON_BLACK = new FilterCreaturePermanent("nonblack creature"); + + static { + FILTER_PERMANENT_CREATURE_NON_BLACK.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); + FILTER_PERMANENT_CREATURE_NON_BLACK.setLockedFilter(true); + } + + public static final FilterCreaturePermanent FILTER_PERMANENT_CREATURES_NON_BLACK = new FilterCreaturePermanent("nonblack creatures"); + + static { + FILTER_PERMANENT_CREATURES_NON_BLACK.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); + FILTER_PERMANENT_CREATURES_NON_BLACK.setLockedFilter(true); + } + public static final FilterCreaturePermanent FILTER_PERMANENT_CREATURE_GOBLINS = new FilterCreaturePermanent(SubType.GOBLIN, "Goblin creatures"); static { FILTER_PERMANENT_CREATURE_GOBLINS.setLockedFilter(true); } - public static final FilterCreaturePermanent FILTER_PERMANENT_CREATURE_SLIVERS = new FilterCreaturePermanent(SubType.SLIVER, "all Sliver creatures"); + public static final FilterCreaturePermanent FILTER_PERMANENT_SLIVERS = new FilterCreaturePermanent(SubType.SLIVER, "Sliver creatures"); static { - FILTER_PERMANENT_CREATURE_SLIVERS.setLockedFilter(true); + FILTER_PERMANENT_SLIVERS.setLockedFilter(true); + } + + public static final FilterCreaturePermanent FILTER_PERMANENT_ALL_SLIVERS = new FilterCreaturePermanent(SubType.SLIVER, "all Sliver creatures"); + + static { + FILTER_PERMANENT_ALL_SLIVERS.setLockedFilter(true); + } + + public static final FilterControlledPermanent FILTER_CONTROLLED_SAMURAI_OR_WARRIOR = new FilterControlledPermanent("a Samurai or Warrior you control"); + + static { + FILTER_CONTROLLED_SAMURAI_OR_WARRIOR.add(Predicates.or( + SubType.SAMURAI.getPredicate(), + SubType.WARRIOR.getPredicate() + )); + FILTER_CONTROLLED_SAMURAI_OR_WARRIOR.setLockedFilter(true); } public static final FilterPlaneswalkerPermanent FILTER_PERMANENT_PLANESWALKER = new FilterPlaneswalkerPermanent(); @@ -628,21 +711,24 @@ public final class StaticFilters { FILTER_SPELL_CREATURE.setLockedFilter(true); } - public static final FilterSpell FILTER_SPELL_NON_CREATURE = (FilterSpell) new FilterSpell("noncreature spell").add(Predicates.not(CardType.CREATURE.getPredicate())); + public static final FilterSpell FILTER_SPELL_NON_CREATURE = (FilterSpell) new FilterSpell("noncreature spell"); static { + FILTER_SPELL_NON_CREATURE.add(Predicates.not(CardType.CREATURE.getPredicate())); FILTER_SPELL_NON_CREATURE.setLockedFilter(true); } - public static final FilterSpell FILTER_SPELLS_NON_CREATURE = (FilterSpell) new FilterSpell("noncreature spells").add(Predicates.not(CardType.CREATURE.getPredicate())); + public static final FilterSpell FILTER_SPELLS_NON_CREATURE = (FilterSpell) new FilterSpell("noncreature spells"); static { + FILTER_SPELLS_NON_CREATURE.add(Predicates.not(CardType.CREATURE.getPredicate())); FILTER_SPELLS_NON_CREATURE.setLockedFilter(true); } - public static final FilterSpell FILTER_SPELL_A_NON_CREATURE = (FilterSpell) new FilterSpell("a noncreature spell").add(Predicates.not(CardType.CREATURE.getPredicate())); + public static final FilterSpell FILTER_SPELL_A_NON_CREATURE = (FilterSpell) new FilterSpell("a noncreature spell"); static { + FILTER_SPELL_A_NON_CREATURE.add(Predicates.not(CardType.CREATURE.getPredicate())); FILTER_SPELL_A_NON_CREATURE.setLockedFilter(true); } @@ -706,6 +792,13 @@ public final class StaticFilters { FILTER_SPELL_INSTANT_SORCERY_WIZARD.setLockedFilter(true); } + public static final FilterSpell FILTER_SPELL_AN_ENCHANTMENT = new FilterSpell("an enchantment spell"); + + static { + FILTER_SPELL_AN_ENCHANTMENT.add(CardType.ENCHANTMENT.getPredicate()); + FILTER_SPELL_AN_ENCHANTMENT.setLockedFilter(true); + } + public static final FilterSpell FILTER_SPELL_KICKED_A = new FilterSpell("a kicked spell"); static { @@ -720,6 +813,49 @@ public final class StaticFilters { FILTER_CREATURE_TOKEN.setLockedFilter(true); } + public static final FilterControlledCreaturePermanent FILTER_A_CONTROLLED_CREATURE_P1P1 = new FilterControlledCreaturePermanent("a creature you control with a +1/+1 counter on it"); + + static { + FILTER_A_CONTROLLED_CREATURE_P1P1.add(CounterType.P1P1.getPredicate()); + FILTER_A_CONTROLLED_CREATURE_P1P1.setLockedFilter(true); + } + + public static final FilterControlledCreaturePermanent FILTER_CONTROLLED_CREATURE_P1P1 = new FilterControlledCreaturePermanent("creature you control with a +1/+1 counter on it"); + + static { + FILTER_CONTROLLED_CREATURE_P1P1.add(CounterType.P1P1.getPredicate()); + FILTER_CONTROLLED_CREATURE_P1P1.setLockedFilter(true); + } + + public static final FilterControlledCreaturePermanent FILTER_EACH_CONTROLLED_CREATURE_P1P1 = new FilterControlledCreaturePermanent("each creature you control with a +1/+1 counter on it"); + + static { + FILTER_EACH_CONTROLLED_CREATURE_P1P1.add(CounterType.P1P1.getPredicate()); + FILTER_EACH_CONTROLLED_CREATURE_P1P1.setLockedFilter(true); + } + + public static final FilterControlledCreaturePermanent FILTER_OTHER_CONTROLLED_CREATURE_P1P1 = new FilterControlledCreaturePermanent("other creature you control with a +1/+1 counter on it"); + + static { + FILTER_OTHER_CONTROLLED_CREATURE_P1P1.add(CounterType.P1P1.getPredicate()); + FILTER_OTHER_CONTROLLED_CREATURE_P1P1.add(AnotherPredicate.instance); + FILTER_OTHER_CONTROLLED_CREATURE_P1P1.setLockedFilter(true); + } + + public static final FilterCreaturePermanent FILTER_A_CREATURE_P1P1 = new FilterCreaturePermanent("a creature with a +1/+1 counter on it"); + + static { + FILTER_A_CREATURE_P1P1.add(CounterType.P1P1.getPredicate()); + FILTER_A_CREATURE_P1P1.setLockedFilter(true); + } + + public static final FilterCreaturePermanent FILTER_CREATURE_P1P1 = new FilterCreaturePermanent("creature with a +1/+1 counter on it"); + + static { + FILTER_CREATURE_P1P1.add(CounterType.P1P1.getPredicate()); + FILTER_CREATURE_P1P1.setLockedFilter(true); + } + public static final FilterCreaturePermanent FILTER_CREATURE_TOKENS = new FilterCreaturePermanent("creature tokens"); static { @@ -727,13 +863,24 @@ public final class StaticFilters { FILTER_CREATURE_TOKENS.setLockedFilter(true); } - public static final FilterCreaturePermanent FILTER_ATTACKING_CREATURES = new FilterCreaturePermanent("attacking creatures"); + public static final FilterAttackingCreature FILTER_ATTACKING_CREATURES = new FilterAttackingCreature("attacking creatures"); static { - FILTER_ATTACKING_CREATURES.add(AttackingPredicate.instance); FILTER_ATTACKING_CREATURES.setLockedFilter(true); } + public static final FilterAttackingOrBlockingCreature FILTER_ATTACKING_OR_BLOCKING_CREATURES = new FilterAttackingOrBlockingCreature("attacking or blocking creatures"); + + static { + FILTER_ATTACKING_OR_BLOCKING_CREATURES.setLockedFilter(true); + } + + public static final FilterBlockingCreature FILTER_BLOCKING_CREATURES = new FilterBlockingCreature("blocking creatures"); + + static { + FILTER_BLOCKING_CREATURES.setLockedFilter(true); + } + public static final FilterPermanent FILTER_PERMANENT_AURA = new FilterPermanent(); static { @@ -744,6 +891,10 @@ public final class StaticFilters { public static final FilterPermanent FILTER_PERMANENT_EQUIPMENT = new FilterEquipmentPermanent(); + static { + FILTER_PERMANENT_EQUIPMENT.setLockedFilter(true); + } + public static final FilterPermanent FILTER_PERMANENT_FORTIFICATION = new FilterPermanent(); static { diff --git a/Mage/src/main/java/mage/filter/common/FilterBasicLandCard.java b/Mage/src/main/java/mage/filter/common/FilterBasicLandCard.java index 815419817c2..aa24dff0e5a 100644 --- a/Mage/src/main/java/mage/filter/common/FilterBasicLandCard.java +++ b/Mage/src/main/java/mage/filter/common/FilterBasicLandCard.java @@ -2,11 +2,11 @@ package mage.filter.common; import mage.constants.CardType; +import mage.constants.SubType; import mage.constants.SuperType; import mage.filter.FilterCard; /** - * * @author BetaSteward_at_googlemail.com */ public class FilterBasicLandCard extends FilterCard { @@ -15,6 +15,11 @@ public class FilterBasicLandCard extends FilterCard { this("basic land card"); } + public FilterBasicLandCard(SubType subType) { + this("basic " + subType + " card"); + this.add(subType.getPredicate()); + } + public FilterBasicLandCard(String name) { super(name); this.add(CardType.LAND.getPredicate()); diff --git a/Mage/src/main/java/mage/filter/common/FilterControlledCreatureInPlay.java b/Mage/src/main/java/mage/filter/common/FilterControlledCreatureInPlay.java deleted file mode 100644 index 1e8b0d139de..00000000000 --- a/Mage/src/main/java/mage/filter/common/FilterControlledCreatureInPlay.java +++ /dev/null @@ -1,64 +0,0 @@ - -package mage.filter.common; - -import java.util.UUID; -import mage.constants.TargetController; -import mage.filter.FilterImpl; -import mage.filter.FilterInPlay; -import mage.game.Game; -import mage.game.permanent.Permanent; - -/** - * - * @author jeffwadsworth - */ -public class FilterControlledCreatureInPlay extends FilterImpl implements FilterInPlay { - - protected final FilterCreaturePermanent creatureFilter; - - public FilterControlledCreatureInPlay() { - this("creature"); - } - - public FilterControlledCreatureInPlay(String name) { - super(name); - creatureFilter = new FilterCreaturePermanent(); - creatureFilter.add(TargetController.YOU.getControllerPredicate()); - } - - @Override - public boolean checkObjectClass(Object object) { - return object instanceof Permanent; - } - - public FilterControlledCreatureInPlay(final FilterControlledCreatureInPlay filter) { - super(filter); - this.creatureFilter = filter.creatureFilter.copy(); - } - - @Override - public boolean match(Object o, Game game) { - if (o instanceof Permanent) { - return creatureFilter.match((Permanent) o, game); - } - return false; - } - - @Override - public boolean match(Object o, UUID sourceId, UUID playerId, Game game) { - if (o instanceof Permanent) { - return creatureFilter.match((Permanent) o, sourceId, playerId, game); - } - return false; - } - - public FilterCreaturePermanent getCreatureFilter() { - return this.creatureFilter; - } - - @Override - public FilterControlledCreatureInPlay copy() { - return new FilterControlledCreatureInPlay(this); - } - -} diff --git a/Mage/src/main/java/mage/filter/predicate/card/CastFromZonePredicate.java b/Mage/src/main/java/mage/filter/predicate/card/CastFromZonePredicate.java index 5008ed59580..466a11bff31 100644 --- a/Mage/src/main/java/mage/filter/predicate/card/CastFromZonePredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/card/CastFromZonePredicate.java @@ -1,16 +1,15 @@ package mage.filter.predicate.card; -import mage.cards.Card; +import mage.MageObject; import mage.constants.Zone; import mage.filter.predicate.Predicate; import mage.game.Game; import mage.game.stack.Spell; /** - * * @author LevelX2 */ -public class CastFromZonePredicate implements Predicate { +public class CastFromZonePredicate implements Predicate { private final Zone zone; @@ -19,7 +18,7 @@ public class CastFromZonePredicate implements Predicate { } @Override - public boolean apply(Card input, Game game) { + public boolean apply(MageObject input, Game game) { if (input instanceof Spell) { return zone.match(((Spell) input).getFromZone()); } else { diff --git a/Mage/src/main/java/mage/filter/predicate/card/PutIntoGraveFromAnywhereThisTurnPredicate.java b/Mage/src/main/java/mage/filter/predicate/card/PutIntoGraveFromAnywhereThisTurnPredicate.java new file mode 100644 index 00000000000..a6fdaab53d7 --- /dev/null +++ b/Mage/src/main/java/mage/filter/predicate/card/PutIntoGraveFromAnywhereThisTurnPredicate.java @@ -0,0 +1,20 @@ +package mage.filter.predicate.card; + +import mage.cards.Card; +import mage.filter.predicate.Predicate; +import mage.game.Game; +import mage.watchers.common.CardsPutIntoGraveyardWatcher; + +/** + * @author Alex-Vasile + */ +public enum PutIntoGraveFromAnywhereThisTurnPredicate implements Predicate { + instance; + + @Override + public boolean apply(Card input, Game game) { + CardsPutIntoGraveyardWatcher watcher = game.getState().getWatcher(CardsPutIntoGraveyardWatcher.class); + + return watcher != null && watcher.checkCardFromAnywhere(input, game); + } +} diff --git a/Mage/src/main/java/mage/filter/predicate/mageobject/SharesColorWithSourcePredicate.java b/Mage/src/main/java/mage/filter/predicate/mageobject/SharesColorWithSourcePredicate.java index 3beae46614e..bd76f122ecc 100644 --- a/Mage/src/main/java/mage/filter/predicate/mageobject/SharesColorWithSourcePredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/mageobject/SharesColorWithSourcePredicate.java @@ -6,11 +6,11 @@ import mage.filter.predicate.ObjectSourcePlayerPredicate; import mage.game.Game; /** - * * @author LevelX2 */ -public class SharesColorWithSourcePredicate implements ObjectSourcePlayerPredicate { +public enum SharesColorWithSourcePredicate implements ObjectSourcePlayerPredicate { + instance; @Override public boolean apply(ObjectSourcePlayer input, Game game) { @@ -26,4 +26,4 @@ public class SharesColorWithSourcePredicate implements ObjectSourcePlayerPredica public String toString() { return "shares a color"; } -} \ No newline at end of file +} diff --git a/Mage/src/main/java/mage/filter/predicate/mageobject/TextPartSubtypePredicate.java b/Mage/src/main/java/mage/filter/predicate/mageobject/TextPartSubtypePredicate.java deleted file mode 100644 index ecddb80214d..00000000000 --- a/Mage/src/main/java/mage/filter/predicate/mageobject/TextPartSubtypePredicate.java +++ /dev/null @@ -1,29 +0,0 @@ -package mage.filter.predicate.mageobject; - -import mage.MageObject; -import mage.abilities.text.TextPartSubType; -import mage.filter.predicate.Predicate; -import mage.game.Game; - -/** - * - * @author LevelX2 - */ -public class TextPartSubtypePredicate implements Predicate { - - private final TextPartSubType textPartSubtype; - - public TextPartSubtypePredicate(TextPartSubType textPartSubtype) { - this.textPartSubtype = textPartSubtype; - } - - @Override - public boolean apply(MageObject input, Game game) { - return input.hasSubtype(textPartSubtype.getCurrentValue(), game); - } - - @Override - public String toString() { - return "Subtype(" + textPartSubtype.getCurrentValue() + ')'; - } -} diff --git a/Mage/src/main/java/mage/filter/predicate/other/ArtifactSourcePredicate.java b/Mage/src/main/java/mage/filter/predicate/other/ArtifactSourcePredicate.java index 26ffd732330..3e98d6a14b6 100644 --- a/Mage/src/main/java/mage/filter/predicate/other/ArtifactSourcePredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/other/ArtifactSourcePredicate.java @@ -1,4 +1,3 @@ - package mage.filter.predicate.other; import mage.filter.predicate.Predicate; @@ -7,21 +6,15 @@ import mage.game.stack.StackAbility; import mage.game.stack.StackObject; /** - * * @author LevelX2 */ -public class ArtifactSourcePredicate implements Predicate { - - public ArtifactSourcePredicate() { - } +public enum ArtifactSourcePredicate implements Predicate { + instance; @Override public boolean apply(StackObject input, Game game) { - if (input instanceof StackAbility) { - StackAbility ability = (StackAbility) input; - return ability.getSourceObject(game).isArtifact(game); - } - return false; + return input instanceof StackAbility + && ((StackAbility) input).getSourceObject(game).isArtifact(game); } @Override diff --git a/Mage/src/main/java/mage/filter/predicate/permanent/CounterAnyPredicate.java b/Mage/src/main/java/mage/filter/predicate/permanent/CounterAnyPredicate.java index 228f1dd6285..5c1362ee6c9 100644 --- a/Mage/src/main/java/mage/filter/predicate/permanent/CounterAnyPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/permanent/CounterAnyPredicate.java @@ -1,6 +1,6 @@ - package mage.filter.predicate.permanent; +import mage.counters.Counter; import mage.filter.predicate.Predicate; import mage.game.Game; import mage.game.permanent.Permanent; @@ -13,8 +13,12 @@ public enum CounterAnyPredicate implements Predicate { @Override public boolean apply(Permanent input, Game game) { - return input.getCounters(game).values().stream().anyMatch(counter -> counter.getCount() > 0); - + return input + .getCounters(game) + .values() + .stream() + .mapToInt(Counter::getCount) + .anyMatch(x -> x > 0); } @Override diff --git a/Mage/src/main/java/mage/filter/predicate/permanent/EnchantedPredicate.java b/Mage/src/main/java/mage/filter/predicate/permanent/EnchantedPredicate.java index 094fc276300..9e323522d78 100644 --- a/Mage/src/main/java/mage/filter/predicate/permanent/EnchantedPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/permanent/EnchantedPredicate.java @@ -1,6 +1,6 @@ - package mage.filter.predicate.permanent; +import mage.constants.SubType; import mage.filter.predicate.Predicate; import mage.game.Game; import mage.game.permanent.Permanent; @@ -19,7 +19,7 @@ public enum EnchantedPredicate implements Predicate { .stream() .map(game::getPermanent) .filter(Objects::nonNull) - .anyMatch(permanent -> permanent.isEnchantment(game)); + .anyMatch(permanent -> permanent.hasSubtype(SubType.AURA, game)); } @Override diff --git a/Mage/src/main/java/mage/filter/predicate/permanent/ModifiedPredicate.java b/Mage/src/main/java/mage/filter/predicate/permanent/ModifiedPredicate.java new file mode 100644 index 00000000000..e0a4d9b739a --- /dev/null +++ b/Mage/src/main/java/mage/filter/predicate/permanent/ModifiedPredicate.java @@ -0,0 +1,39 @@ +package mage.filter.predicate.permanent; + +import mage.constants.SubType; +import mage.counters.Counter; +import mage.filter.predicate.Predicate; +import mage.game.Game; +import mage.game.permanent.Permanent; + +import java.util.Objects; + +/** + * @author TheElk801 + */ +public enum ModifiedPredicate implements Predicate { + instance; + + @Override + public boolean apply(Permanent input, Game game) { + return input + .getCounters(game) + .values() + .stream() + .mapToInt(Counter::getCount) + .anyMatch(x -> x > 0) + || input + .getAttachments() + .stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .anyMatch(permanent -> permanent.hasSubtype(SubType.EQUIPMENT, game) + || (permanent.hasSubtype(SubType.AURA, game) + && permanent.isControlledBy(input.getControllerId()))); + } + + @Override + public String toString() { + return "modified"; + } +} diff --git a/Mage/src/main/java/mage/game/Game.java b/Mage/src/main/java/mage/game/Game.java index 9cbdbf01d50..1ec9f2439fa 100644 --- a/Mage/src/main/java/mage/game/Game.java +++ b/Mage/src/main/java/mage/game/Game.java @@ -386,6 +386,26 @@ public interface Game extends MageItem, Serializable, Copyable { void ventureIntoDungeon(UUID playerId); + /** + * Tells whether the current game has day or night, defaults to false + */ + boolean hasDayNight(); + + /** + * Sets game to day or night, sets hasDayNight to true + * + * @param daytime day is true, night is false + */ + void setDaytime(boolean daytime); + + /** + * Returns true if hasDayNight is true and parameter matches current day/night value + * Returns false if hasDayNight is false + * + * @param daytime day is true, night is false + */ + boolean checkDayNight(boolean daytime); + /** * Adds a permanent to the battlefield * diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java index 71cb83352f3..1a1c4c0d75c 100644 --- a/Mage/src/main/java/mage/game/GameImpl.java +++ b/Mage/src/main/java/mage/game/GameImpl.java @@ -14,10 +14,7 @@ import mage.abilities.effects.Effect; import mage.abilities.effects.PreventionEffectData; import mage.abilities.effects.common.CopyEffect; import mage.abilities.effects.common.InfoEffect; -import mage.abilities.keyword.BestowAbility; -import mage.abilities.keyword.CompanionAbility; -import mage.abilities.keyword.MorphAbility; -import mage.abilities.keyword.TransformAbility; +import mage.abilities.keyword.*; import mage.abilities.mana.DelayedTriggeredManaAbility; import mage.abilities.mana.TriggeredManaAbility; import mage.actions.impl.MageAction; @@ -33,7 +30,6 @@ import mage.filter.Filter; import mage.filter.FilterCard; import mage.filter.FilterPermanent; import mage.filter.StaticFilters; -import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.NamePredicate; import mage.filter.predicate.permanent.ControllerIdPredicate; @@ -552,6 +548,35 @@ public abstract class GameImpl implements Game { fireEvent(GameEvent.getEvent(GameEvent.EventType.VENTURED, playerId, null, playerId)); } + @Override + public boolean hasDayNight() { + return state.isHasDayNight(); + } + + @Override + public void setDaytime(boolean daytime) { + if (!state.isHasDayNight()) { + informPlayers("It has become " + (daytime ? "day" : "night")); + } + if (!state.setDaytime(daytime)) { + return; + } + // TODO: add day/night sound effect + informPlayers("It has become " + (daytime ? "day" : "night")); + fireEvent(GameEvent.getEvent(GameEvent.EventType.BECOMES_DAY_NIGHT, null, null, null)); + for (Permanent permanent : state.getBattlefield().getAllPermanents()) { + if ((daytime && permanent.getAbilities(this).containsClass(NightboundAbility.class)) + || (!daytime && permanent.getAbilities(this).containsClass(DayboundAbility.class))) { + permanent.transform(null, this, true); + } + } + } + + @Override + public boolean checkDayNight(boolean daytime) { + return state.isHasDayNight() && state.isDaytime() == daytime; + } + @Override public UUID getOwnerId(UUID objectId) { return getOwnerId(getObject(objectId)); @@ -1863,7 +1888,7 @@ public abstract class GameImpl implements Game { } newBluePrint.assignNewId(); if (copyFromPermanent.isTransformed()) { - TransformAbility.transform(newBluePrint, newBluePrint.getSecondCardFace(), this, source); + TransformAbility.transformPermanent(newBluePrint, newBluePrint.getSecondCardFace(), this, source); } } if (applier != null) { @@ -1933,6 +1958,9 @@ public abstract class GameImpl implements Game { if (newAbility.getSourceObjectZoneChangeCounter() == 0) { newAbility.setSourceObjectZoneChangeCounter(getState().getZoneChangeCounter(ability.getSourceId())); } + if (!(newAbility instanceof DelayedTriggeredAbility)) { + newAbility.setSourcePermanentTransformCount(this); + } newAbility.setTriggerEvent(triggeringEvent); state.addTriggeredAbility(newAbility); } @@ -1949,6 +1977,7 @@ public abstract class GameImpl implements Game { newAbility.newId(); if (source != null) { newAbility.setSourceObjectZoneChangeCounter(getState().getZoneChangeCounter(source.getSourceId())); + newAbility.setSourcePermanentTransformCount(this); } newAbility.initOnAdding(this); // ability.init is called as the ability triggeres not now. @@ -2356,10 +2385,16 @@ public abstract class GameImpl implements Game { } } else { Filter auraFilter = spellAbility.getTargets().get(0).getFilter(); - if (auraFilter instanceof FilterControlledPermanent) { - if (!((FilterControlledPermanent) auraFilter).match(attachedTo, perm.getId(), perm.getControllerId(), this) + if (auraFilter instanceof FilterPermanent) { + if (!((FilterPermanent) auraFilter).match(attachedTo, perm.getId(), perm.getControllerId(), this) || attachedTo.cantBeAttachedBy(perm, null, this, true)) { - if (movePermanentToGraveyardWithInfo(perm)) { + Card card = this.getCard(perm.getId()); + if (card != null && card.isCreature(this)) { + UUID wasAttachedTo = perm.getAttachedTo(); + perm.attachTo(null, null, this); + BestowAbility.becomeCreature(perm, this); + fireEvent(new UnattachedEvent(wasAttachedTo, perm.getId(), perm, null)); + } else if (movePermanentToGraveyardWithInfo(perm)) { somethingHappened = true; } } @@ -2585,6 +2620,17 @@ public abstract class GameImpl implements Game { } } + // Daybound/Nightbound permanents should be transformed according to day/night + // This is not a state-based action but it's unclear where else to put it + if (hasDayNight()) { + for (Permanent permanent : getBattlefield().getAllActivePermanents()) { + if ((permanent.getAbilities(this).containsClass(DayboundAbility.class) && !state.isDaytime()) + || (permanent.getAbilities(this).containsClass(NightboundAbility.class) && state.isDaytime())) { + somethingHappened = permanent.transform(null, this, true) || somethingHappened; + } + } + } + //TODO: implement the rest return somethingHappened; } @@ -2757,6 +2803,8 @@ public abstract class GameImpl implements Game { @Override public void informPlayers(String message) { + // Uncomment to print game messages + // System.out.println(message.replaceAll("\\<.*?\\>", "")); if (simulation) { return; } diff --git a/Mage/src/main/java/mage/game/GameState.java b/Mage/src/main/java/mage/game/GameState.java index 491c26b5876..1bb704405fa 100644 --- a/Mage/src/main/java/mage/game/GameState.java +++ b/Mage/src/main/java/mage/game/GameState.java @@ -105,6 +105,8 @@ public class GameState implements Serializable, Copyable { private final Map usePowerInsteadOfToughnessForDamageLethalityFilters = new HashMap<>(); private Set commandersToStay = new HashSet<>(); // commanders that do not go back to command zone private boolean manaBurn = false; + private boolean hasDayNight = false; + private boolean isDaytime = true; private int applyEffectsCounter; // Upcounting number of each applyEffects execution @@ -193,6 +195,8 @@ public class GameState implements Serializable, Copyable { state.usePowerInsteadOfToughnessForDamageLethalityFilters.forEach((uuid, filter) -> this.usePowerInsteadOfToughnessForDamageLethalityFilters.put(uuid, filter.copy())); this.commandersToStay.addAll(state.commandersToStay); + this.hasDayNight = state.hasDayNight; + this.isDaytime = state.isDaytime; } public void clearOnGameRestart() { @@ -280,6 +284,8 @@ public class GameState implements Serializable, Copyable { state.usePowerInsteadOfToughnessForDamageLethalityFilters.forEach((uuid, filter) -> this.usePowerInsteadOfToughnessForDamageLethalityFilters.put(uuid, filter.copy())); this.commandersToStay = state.commandersToStay; + this.hasDayNight = state.hasDayNight; + this.isDaytime = state.isDaytime; } @Override @@ -872,7 +878,7 @@ public class GameState implements Serializable, Copyable { for (Map.Entry> entry : eventsByKey.entrySet()) { Set movedCards = new LinkedHashSet<>(); Set movedTokens = new LinkedHashSet<>(); - for (Iterator it = entry.getValue().iterator(); it.hasNext();) { + for (Iterator it = entry.getValue().iterator(); it.hasNext(); ) { GameEvent event = it.next(); ZoneChangeEvent castEvent = (ZoneChangeEvent) event; UUID targetId = castEvent.getTargetId(); @@ -946,8 +952,8 @@ public class GameState implements Serializable, Copyable { * span * * @param ability - * @param sourceId - if source object can be moved between zones then you - * must set it here (each game cycle clear all source related triggers) + * @param sourceId - if source object can be moved between zones then you + * must set it here (each game cycle clear all source related triggers) * @param attachedTo */ public void addAbility(Ability ability, UUID sourceId, MageObject attachedTo) { @@ -1153,8 +1159,8 @@ public class GameState implements Serializable, Copyable { * @param attachedTo * @param ability * @param copyAbility copies non MageSingleton abilities before adding to - * state (allows to have multiple instances in one object, e.g. false param - * will simulate keyword/singleton) + * state (allows to have multiple instances in one object, e.g. false param + * will simulate keyword/singleton) */ public void addOtherAbility(Card attachedTo, Ability ability, boolean copyAbility) { checkWrongDynamicAbilityUsage(attachedTo, ability); @@ -1413,6 +1419,21 @@ public class GameState implements Serializable, Copyable { return manaBurn; } + boolean isHasDayNight() { + return hasDayNight; + } + + boolean setDaytime(boolean daytime) { + boolean flag = this.hasDayNight && this.isDaytime != daytime; + this.hasDayNight = true; + this.isDaytime = daytime; + return flag; + } + + boolean isDaytime() { + return isDaytime; + } + @Override public String toString() { return CardUtil.getTurnInfo(this); diff --git a/Mage/src/main/java/mage/game/ZonesHandler.java b/Mage/src/main/java/mage/game/ZonesHandler.java index 9f10fcdbcbc..36203728001 100644 --- a/Mage/src/main/java/mage/game/ZonesHandler.java +++ b/Mage/src/main/java/mage/game/ZonesHandler.java @@ -382,8 +382,8 @@ public final class ZonesHandler { } else if (event.getTarget() != null) { card.setFaceDown(info.faceDown, game); Permanent target = event.getTarget(); - success = game.getPlayer(target.getControllerId()).removeFromBattlefield(target, source, game) - && target.removeFromZone(game, fromZone, source); + success = target.removeFromZone(game, fromZone, source) + && game.getPlayer(target.getControllerId()).removeFromBattlefield(target, source, game); } else { card.setFaceDown(info.faceDown, game); success = card.removeFromZone(game, fromZone, source); diff --git a/Mage/src/main/java/mage/game/combat/Combat.java b/Mage/src/main/java/mage/game/combat/Combat.java index 482d8059a16..a4c4e224f24 100644 --- a/Mage/src/main/java/mage/game/combat/Combat.java +++ b/Mage/src/main/java/mage/game/combat/Combat.java @@ -440,65 +440,76 @@ public class Combat implements Serializable, Copyable { for (Permanent creature : player.getAvailableAttackers(game)) { boolean mustAttack = false; Set defendersForcedToAttack = new HashSet<>(); - - // check if a creature has to attack - for (Map.Entry> entry : game.getContinuousEffects().getApplicableRequirementEffects(creature, false, game).entrySet()) { - RequirementEffect effect = entry.getKey(); - if (effect.mustAttack(game) - && checkAttackRestrictions(player, game)) { // needed for Goad Effect + if (creature.getGoadingPlayers().isEmpty()) { + // check if a creature has to attack + for (Map.Entry> entry : game.getContinuousEffects().getApplicableRequirementEffects(creature, false, game).entrySet()) { + RequirementEffect effect = entry.getKey(); + if (!effect.mustAttack(game)) { + continue; + } mustAttack = true; for (Ability ability : entry.getValue()) { UUID defenderId = effect.mustAttackDefender(ability, game); - if (defenderId != null) { - if (defenders.contains(defenderId)) { - defendersForcedToAttack.add(defenderId); - } + if (defenderId != null && defenders.contains(defenderId)) { + defendersForcedToAttack.add(defenderId); } break; } } + } else { + // if creature is goaded then we start with assumption that it needs to attack any player + mustAttack = true; + defendersForcedToAttack.addAll(defenders); } - if (mustAttack) { - // check which defenders the forced to attack creature can attack without paying a cost - Set defendersCostlessAttackable = new HashSet<>(defenders); - for (UUID defenderId : defenders) { - if (game.getContinuousEffects().checkIfThereArePayCostToAttackBlockEffects( - new DeclareAttackerEvent(defenderId, creature.getId(), creature.getControllerId()), game)) { - defendersCostlessAttackable.remove(defenderId); - defendersForcedToAttack.remove(defenderId); - } - } - // force attack only if a defender can be attacked without paying a cost - if (!defendersCostlessAttackable.isEmpty()) { - creaturesForcedToAttack.put(creature.getId(), defendersForcedToAttack); - // No need to attack a special defender - if (defendersForcedToAttack.isEmpty()) { - if (defendersCostlessAttackable.size() == 1) { - player.declareAttacker(creature.getId(), defendersCostlessAttackable.iterator().next(), game, false); - } else { - TargetDefender target = new TargetDefender(defendersCostlessAttackable, creature.getId()); - target.setRequired(true); - target.setTargetName("planeswalker or player for " + creature.getLogName() + " to attack (must attack effect)"); - if (player.chooseTarget(Outcome.Damage, target, null, game)) { - player.declareAttacker(creature.getId(), target.getFirstTarget(), game, false); - } - } - } else { - if (defendersForcedToAttack.size() == 1) { - player.declareAttacker(creature.getId(), defendersForcedToAttack.iterator().next(), game, false); - } else { - TargetDefender target = new TargetDefender(defendersForcedToAttack, creature.getId()); - target.setRequired(true); - target.setTargetName("planeswalker or player for " + creature.getLogName() + " to attack (must attack effect)"); - if (player.chooseTarget(Outcome.Damage, target, null, game)) { - player.declareAttacker(creature.getId(), target.getFirstTarget(), game, false); - } - } - } - } - + if (!mustAttack) { + continue; + } + // check which defenders the forced to attack creature can attack without paying a cost + Set defendersCostlessAttackable = new HashSet<>(defenders); + for (UUID defenderId : defenders) { + if (game.getContinuousEffects().checkIfThereArePayCostToAttackBlockEffects( + new DeclareAttackerEvent(defenderId, creature.getId(), creature.getControllerId()), game + )) { + defendersCostlessAttackable.remove(defenderId); + defendersForcedToAttack.remove(defenderId); + continue; + } + for (Map.Entry> entry : game.getContinuousEffects().getApplicableRestrictionEffects(creature, game).entrySet()) { + if (entry + .getValue() + .stream() + .anyMatch(ability -> entry.getKey().canAttack( + creature, defenderId, ability, game, false + ))) { + continue; + } + defendersCostlessAttackable.remove(defenderId); + defendersForcedToAttack.remove(defenderId); + break; + } + } + // if creature can attack someone other than a player that goaded them + // then they attack one of those players, otherwise they attack any player + if (!defendersForcedToAttack.stream().allMatch(creature.getGoadingPlayers()::contains)) { + defendersForcedToAttack.removeAll(creature.getGoadingPlayers()); + } + // force attack only if a defender can be attacked without paying a cost + if (defendersCostlessAttackable.isEmpty()) { + continue; + } + creaturesForcedToAttack.put(creature.getId(), defendersForcedToAttack); + // No need to attack a special defender + Set defendersToChooseFrom = defendersForcedToAttack.isEmpty() ? defendersCostlessAttackable : defendersForcedToAttack; + if (defendersToChooseFrom.size() == 1) { + player.declareAttacker(creature.getId(), defendersToChooseFrom.iterator().next(), game, false); + continue; + } + TargetDefender target = new TargetDefender(defendersToChooseFrom, creature.getId()); + target.setRequired(true); + target.setTargetName("planeswalker or player for " + creature.getLogName() + " to attack (must attack effect)"); + if (player.chooseTarget(Outcome.Damage, target, null, game)) { + player.declareAttacker(creature.getId(), target.getFirstTarget(), game, false); } - } } @@ -529,28 +540,30 @@ public class Combat implements Serializable, Copyable { for (Map.Entry> entry : game.getContinuousEffects().getApplicableRestrictionEffects(attackingCreature, game).entrySet()) { RestrictionEffect effect = entry.getKey(); for (Ability ability : entry.getValue()) { - if (!effect.canAttackCheckAfter(numberAttackers, ability, game, true)) { - MageObject sourceObject = ability.getSourceObject(game); - if (attackingPlayer.isHuman()) { - attackingPlayer.resetPlayerPassedActions(); - game.informPlayer(attackingPlayer, attackingCreature.getIdName() + " can't attack this way (" + (sourceObject == null ? "null" : sourceObject.getIdName()) + ')'); - return false; - } else { - // remove attacking creatures for AI that are not allowed to attack - // can create possible not allowed attack scenarios, but not sure how to solve this - for (CombatGroup combatGroup : this.getGroups()) { - if (combatGroup.getAttackers().contains(attackingCreatureId)) { - attackerToRemove = attackingCreatureId; - } - } - check = true; // do the check again - if (numberOfChecks > 50) { - logger.error("Seems to be an AI declare attacker lock (reached 50 check iterations) " + (sourceObject == null ? "null" : sourceObject.getIdName())); - return true; // break the check - } - continue Check; - } + if (effect.canAttackCheckAfter(numberAttackers, ability, game, true)) { + continue; } + MageObject sourceObject = ability.getSourceObject(game); + if (attackingPlayer.isHuman()) { + attackingPlayer.resetPlayerPassedActions(); + game.informPlayer(attackingPlayer, attackingCreature.getIdName() + " can't attack this way (" + (sourceObject == null ? "null" : sourceObject.getIdName()) + ')'); + return false; + } + // remove attacking creatures for AI that are not allowed to attack + // can create possible not allowed attack scenarios, but not sure how to solve this + if (this.getGroups() + .stream() + .map(CombatGroup::getAttackers) + .flatMap(Collection::stream) + .anyMatch(attackingCreatureId::equals)) { + attackerToRemove = attackingCreatureId; + } + check = true; // do the check again + if (numberOfChecks > 50) { + logger.error("Seems to be an AI declare attacker lock (reached 50 check iterations) " + (sourceObject == null ? "null" : sourceObject.getIdName())); + return true; // break the check + } + continue Check; } } } @@ -1300,21 +1313,20 @@ public class Combat implements Serializable, Copyable { @SuppressWarnings("deprecation") public boolean declareAttacker(UUID creatureId, UUID defenderId, UUID playerId, Game game) { Permanent attacker = game.getPermanent(creatureId); - if (attacker != null) { - if (!game.replaceEvent(new DeclareAttackerEvent(defenderId, creatureId, playerId))) { - if (addAttackerToCombat(creatureId, defenderId, game)) { - if (!attacker.hasAbility(VigilanceAbility.getInstance(), game) - && !attacker.hasAbility(JohanVigilanceAbility.getInstance(), game)) { - if (!attacker.isTapped()) { - attacker.setTapped(true); - attackersTappedByAttack.add(attacker.getId()); - } - } - return true; - } - } + if (attacker == null + || game.replaceEvent(new DeclareAttackerEvent(defenderId, creatureId, playerId)) + || !addAttackerToCombat(creatureId, defenderId, game)) { + return false; } - return false; + if (attacker.hasAbility(VigilanceAbility.getInstance(), game) + || attacker.hasAbility(JohanVigilanceAbility.getInstance(), game)) { + return true; + } + if (!attacker.isTapped()) { + attacker.setTapped(true); + attackersTappedByAttack.add(attacker.getId()); + } + return true; } public boolean addAttackerToCombat(UUID attackerId, UUID defenderId, Game game) { diff --git a/Mage/src/main/java/mage/game/command/Commander.java b/Mage/src/main/java/mage/game/command/Commander.java index 08449a16bb8..42b17ec2d76 100644 --- a/Mage/src/main/java/mage/game/command/Commander.java +++ b/Mage/src/main/java/mage/game/command/Commander.java @@ -8,7 +8,6 @@ import mage.abilities.common.CastCommanderAbility; import mage.abilities.common.PlayLandAsCommanderAbility; import mage.abilities.costs.mana.ManaCost; import mage.abilities.costs.mana.ManaCosts; -import mage.abilities.text.TextPart; import mage.cards.Card; import mage.cards.FrameStyle; import mage.constants.CardType; @@ -308,25 +307,17 @@ public class Commander implements CommandObject { @Override public boolean isAllCreatureTypes(Game game) { - return false; + return sourceObject.isAllCreatureTypes(game); } @Override public void setIsAllCreatureTypes(boolean value) { + sourceObject.setIsAllCreatureTypes(value); } @Override public void setIsAllCreatureTypes(Game game, boolean value) { - } - - @Override - public List getTextParts() { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public TextPart addTextPart(TextPart textPart) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + sourceObject.setIsAllCreatureTypes(game, value); } @Override diff --git a/Mage/src/main/java/mage/game/command/Dungeon.java b/Mage/src/main/java/mage/game/command/Dungeon.java index 0170c47b762..cb45625eeee 100644 --- a/Mage/src/main/java/mage/game/command/Dungeon.java +++ b/Mage/src/main/java/mage/game/command/Dungeon.java @@ -12,7 +12,6 @@ import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.Effect; import mage.abilities.hint.HintUtils; -import mage.abilities.text.TextPart; import mage.cards.FrameStyle; import mage.choices.Choice; import mage.choices.ChoiceHintType; @@ -382,16 +381,6 @@ public class Dungeon implements CommandObject { } } - @Override - public List getTextParts() { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public TextPart addTextPart(TextPart textPart) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - @Override public void removePTCDA() { } diff --git a/Mage/src/main/java/mage/game/command/Emblem.java b/Mage/src/main/java/mage/game/command/Emblem.java index 2e4fef05581..9ce49ad88e4 100644 --- a/Mage/src/main/java/mage/game/command/Emblem.java +++ b/Mage/src/main/java/mage/game/command/Emblem.java @@ -11,7 +11,6 @@ import mage.abilities.costs.mana.ManaCosts; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.Effect; -import mage.abilities.text.TextPart; import mage.cards.Card; import mage.cards.FrameStyle; import mage.constants.CardType; @@ -291,16 +290,6 @@ public class Emblem implements CommandObject { } } - @Override - public List getTextParts() { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public TextPart addTextPart(TextPart textPart) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - @Override public void removePTCDA() { } diff --git a/Mage/src/main/java/mage/game/command/Plane.java b/Mage/src/main/java/mage/game/command/Plane.java index ca483d84195..c5cc43c88aa 100644 --- a/Mage/src/main/java/mage/game/command/Plane.java +++ b/Mage/src/main/java/mage/game/command/Plane.java @@ -11,7 +11,6 @@ import mage.abilities.costs.mana.ManaCosts; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.Effect; -import mage.abilities.text.TextPart; import mage.cards.Card; import mage.cards.FrameStyle; import mage.constants.CardType; @@ -300,16 +299,6 @@ public class Plane implements CommandObject { } } - @Override - public List getTextParts() { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public TextPart addTextPart(TextPart textPart) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - @Override public void removePTCDA() { } diff --git a/Mage/src/main/java/mage/game/command/emblems/ChandraDressedToKillEmblem.java b/Mage/src/main/java/mage/game/command/emblems/ChandraDressedToKillEmblem.java new file mode 100644 index 00000000000..92e56f2bd31 --- /dev/null +++ b/Mage/src/main/java/mage/game/command/emblems/ChandraDressedToKillEmblem.java @@ -0,0 +1,76 @@ +package mage.game.command.emblems; + +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterSpell; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.game.Game; +import mage.game.command.Emblem; +import mage.game.permanent.Permanent; +import mage.game.stack.Spell; +import mage.players.Player; +import mage.target.common.TargetAnyTarget; +import mage.watchers.common.ManaPaidSourceWatcher; + +/** + * + * @author weirddan455 + */ +public class ChandraDressedToKillEmblem extends Emblem { + + private static final FilterSpell filter = new FilterSpell("a red spell"); + + static { + filter.add(new ColorPredicate(ObjectColor.RED)); + } + + // Whenever you cast a red spell, this emblem deals X damage to any target, where X is the amount of mana spent to cast that spell. + public ChandraDressedToKillEmblem() { + this.setName("Emblem Chandra"); + Ability ability = new SpellCastControllerTriggeredAbility(Zone.COMMAND, new ChandraDressedToKillEmblemEffect(), filter, false, true); + ability.addTarget(new TargetAnyTarget()); + this.getAbilities().add(ability); + } +} + +class ChandraDressedToKillEmblemEffect extends OneShotEffect { + + public ChandraDressedToKillEmblemEffect() { + super(Outcome.Damage); + staticText = "this emblem deals X damage to any target, where X is the amount of mana spent to cast that spell"; + } + + private ChandraDressedToKillEmblemEffect(final ChandraDressedToKillEmblemEffect effect) { + super(effect); + } + + @Override + public ChandraDressedToKillEmblemEffect copy() { + return new ChandraDressedToKillEmblemEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Spell spell = (Spell) getValue("spellCast"); + if (controller == null || spell == null) { + return false; + } + int manaPaid = ManaPaidSourceWatcher.getTotalPaid(spell.getId(), game); + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent != null) { + permanent.damage(manaPaid, source, game); + return true; + } + Player player = game.getPlayer(source.getFirstTarget()); + if (player != null) { + player.damage(manaPaid, source, game); + return true; + } + return false; + } +} diff --git a/Mage/src/main/java/mage/game/command/emblems/DarettiScrapSavantEmblem.java b/Mage/src/main/java/mage/game/command/emblems/DarettiScrapSavantEmblem.java index 15f3f4e331e..33ebf2dddc9 100644 --- a/Mage/src/main/java/mage/game/command/emblems/DarettiScrapSavantEmblem.java +++ b/Mage/src/main/java/mage/game/command/emblems/DarettiScrapSavantEmblem.java @@ -58,7 +58,7 @@ class DarettiScrapSavantTriggeredAbility extends TriggeredAbilityImpl { if (zEvent.isDiesEvent() && zEvent.getTarget().isArtifact(game) && zEvent.getTarget().isOwnedBy(this.controllerId)) { - this.getEffects().setTargetPointer(new FixedTarget(zEvent.getTargetId())); + this.getEffects().setTargetPointer(new FixedTarget(zEvent.getTargetId(), game)); return true; } return false; @@ -93,7 +93,7 @@ class DarettiScrapSavantEffect extends OneShotEffect { Effect effect = new ReturnFromGraveyardToBattlefieldTargetEffect(); effect.setTargetPointer(new FixedTarget(card, game)); effect.setText("return that card to the battlefield at the beginning of the next end step"); - game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(Zone.COMMAND, effect, TargetController.ANY), source); + game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect, TargetController.ANY), source); return true; } return false; diff --git a/Mage/src/main/java/mage/game/command/emblems/KaitoShizukiEmblem.java b/Mage/src/main/java/mage/game/command/emblems/KaitoShizukiEmblem.java new file mode 100644 index 00000000000..5ef5421733c --- /dev/null +++ b/Mage/src/main/java/mage/game/command/emblems/KaitoShizukiEmblem.java @@ -0,0 +1,39 @@ +package mage.game.command.emblems; + +import mage.ObjectColor; +import mage.abilities.common.DealsDamageToAPlayerAllTriggeredAbility; +import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; +import mage.constants.SetTargetPointer; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.game.command.Emblem; +import mage.target.common.TargetCardInLibrary; + +/** + * @author TheElk801 + */ +public final class KaitoShizukiEmblem extends Emblem { + + private static final FilterCard filter = new FilterCreatureCard("a blue or black creature card"); + + static { + filter.add(Predicates.or( + new ColorPredicate(ObjectColor.BLUE), + new ColorPredicate(ObjectColor.BLACK) + )); + } + + // −7: You get an emblem with "Whenever a creature you control deals combat damage to a player, search your library for a blue or black creature card, put it onto the battlefield, then shuffle." + public KaitoShizukiEmblem() { + this.setName("Emblem Kaito"); + this.setExpansionSetCodeForImage("NEO"); + this.getAbilities().add(new DealsDamageToAPlayerAllTriggeredAbility( + new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(filter)), + StaticFilters.FILTER_CONTROLLED_A_CREATURE, false, + SetTargetPointer.NONE, true + )); + } +} diff --git a/Mage/src/main/java/mage/game/command/emblems/LilianaDefiantNecromancerEmblem.java b/Mage/src/main/java/mage/game/command/emblems/LilianaDefiantNecromancerEmblem.java index abdf853a601..fdc8029e6be 100644 --- a/Mage/src/main/java/mage/game/command/emblems/LilianaDefiantNecromancerEmblem.java +++ b/Mage/src/main/java/mage/game/command/emblems/LilianaDefiantNecromancerEmblem.java @@ -11,7 +11,7 @@ import mage.cards.Card; import mage.constants.Outcome; import mage.constants.TargetController; import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.command.Emblem; import mage.target.targetpointer.FixedTarget; @@ -23,11 +23,9 @@ import mage.target.targetpointer.FixedTarget; public final class LilianaDefiantNecromancerEmblem extends Emblem { // You get an emblem with "Whenever a creature you control dies, return it to the battlefield under your control at the beginning of the next end step." - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature"); - public LilianaDefiantNecromancerEmblem() { this.setName("Emblem Liliana"); - Ability ability = new DiesCreatureTriggeredAbility(Zone.COMMAND, new LilianaDefiantNecromancerEmblemEffect(), false, filter, true); + Ability ability = new DiesCreatureTriggeredAbility(Zone.COMMAND, new LilianaDefiantNecromancerEmblemEffect(), false, StaticFilters.FILTER_PERMANENT_A_CREATURE, true); this.getAbilities().add(ability); } } @@ -55,7 +53,7 @@ class LilianaDefiantNecromancerEmblemEffect extends OneShotEffect { Effect effect = new ReturnFromGraveyardToBattlefieldTargetEffect(); effect.setTargetPointer(new FixedTarget(card, game)); effect.setText("return that card to the battlefield at the beginning of the next end step"); - game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(Zone.COMMAND, effect, TargetController.ANY), source); + game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect, TargetController.ANY), source); return true; } return false; diff --git a/Mage/src/main/java/mage/game/command/emblems/LukkaWaywardBonderEmblem.java b/Mage/src/main/java/mage/game/command/emblems/LukkaWaywardBonderEmblem.java index 7c0b4785a7f..ca2f2127f92 100644 --- a/Mage/src/main/java/mage/game/command/emblems/LukkaWaywardBonderEmblem.java +++ b/Mage/src/main/java/mage/game/command/emblems/LukkaWaywardBonderEmblem.java @@ -23,7 +23,7 @@ public final class LukkaWaywardBonderEmblem extends Emblem { this.setExpansionSetCodeForImage("STX"); Ability ability = new EntersBattlefieldControlledTriggeredAbility( Zone.COMMAND, new LukkaWaywardBonderEmblemEffect(), - StaticFilters.FILTER_PERMANENT_CREATURE_A, false + StaticFilters.FILTER_PERMANENT_A_CREATURE, false ); ability.addTarget(new TargetAnyTarget()); this.getAbilities().add(ability); @@ -57,8 +57,8 @@ class LukkaWaywardBonderEmblemEffect extends OneShotEffect { return targetPermanent.damage(permanent.getPower().getValue(), permanent.getId(), source, game) > 0; } Player targetPlayer = game.getPlayer(source.getFirstTarget()); - if (targetPermanent != null) { - return targetPermanent.damage(permanent.getPower().getValue(), permanent.getId(), source, game) > 0; + if (targetPlayer != null) { + return targetPlayer.damage(permanent.getPower().getValue(), permanent.getId(), source, game) > 0; } return false; } diff --git a/Mage/src/main/java/mage/game/command/emblems/TamiyoTheMoonSageEmblem.java b/Mage/src/main/java/mage/game/command/emblems/TamiyoTheMoonSageEmblem.java index 6034f77158f..cb910858cb4 100644 --- a/Mage/src/main/java/mage/game/command/emblems/TamiyoTheMoonSageEmblem.java +++ b/Mage/src/main/java/mage/game/command/emblems/TamiyoTheMoonSageEmblem.java @@ -1,10 +1,7 @@ - package mage.game.command.emblems; -import mage.abilities.Ability; import mage.abilities.common.PutCardIntoGraveFromAnywhereAllTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.abilities.effects.common.continuous.MaximumHandSizeControllerEffect; import mage.abilities.effects.common.continuous.MaximumHandSizeControllerEffect.HandSizeModification; @@ -12,11 +9,10 @@ import mage.constants.Duration; import mage.constants.SetTargetPointer; import mage.constants.TargetController; import mage.constants.Zone; -import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.game.command.Emblem; /** - * * @author spjspj */ public final class TamiyoTheMoonSageEmblem extends Emblem { @@ -28,11 +24,12 @@ public final class TamiyoTheMoonSageEmblem extends Emblem { public TamiyoTheMoonSageEmblem() { this.setName("Emblem Tamiyo"); - Ability ability = new SimpleStaticAbility(Zone.COMMAND, new MaximumHandSizeControllerEffect(Integer.MAX_VALUE, Duration.EndOfGame, HandSizeModification.SET)); - this.getAbilities().add(ability); - Effect effect = new ReturnToHandTargetEffect(); - effect.setText("return it to your hand"); + this.getAbilities().add(new SimpleStaticAbility(Zone.COMMAND, new MaximumHandSizeControllerEffect( + Integer.MAX_VALUE, Duration.Custom, HandSizeModification.SET + ))); this.getAbilities().add(new PutCardIntoGraveFromAnywhereAllTriggeredAbility( - Zone.COMMAND, effect, true, new FilterCard("a card"), TargetController.YOU, SetTargetPointer.CARD)); + Zone.COMMAND, new ReturnToHandTargetEffect().setText("return it to your hand"), + true, StaticFilters.FILTER_CARD_A, TargetController.YOU, SetTargetPointer.CARD + )); } } diff --git a/Mage/src/main/java/mage/game/command/emblems/TezzeretBetrayerOfFleshEmblem.java b/Mage/src/main/java/mage/game/command/emblems/TezzeretBetrayerOfFleshEmblem.java new file mode 100644 index 00000000000..48a63cb23c9 --- /dev/null +++ b/Mage/src/main/java/mage/game/command/emblems/TezzeretBetrayerOfFleshEmblem.java @@ -0,0 +1,23 @@ +package mage.game.command.emblems; + +import mage.abilities.common.BecomesTappedTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.command.Emblem; + +/** + * @author TheElk801 + */ +public final class TezzeretBetrayerOfFleshEmblem extends Emblem { + + // −6: You get an emblem with "Whenever an artifact you control becomes tapped, draw a card." + public TezzeretBetrayerOfFleshEmblem() { + this.setName("Emblem Tezzeret"); + this.setExpansionSetCodeForImage("NEO"); + this.getAbilities().add(new BecomesTappedTriggeredAbility( + Zone.COMMAND, new DrawCardSourceControllerEffect(1), false, + StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT, false + )); + } +} diff --git a/Mage/src/main/java/mage/game/command/planes/FeedingGroundsPlane.java b/Mage/src/main/java/mage/game/command/planes/FeedingGroundsPlane.java index 6a7ab373dbf..33a3f6dcc28 100644 --- a/Mage/src/main/java/mage/game/command/planes/FeedingGroundsPlane.java +++ b/Mage/src/main/java/mage/game/command/planes/FeedingGroundsPlane.java @@ -20,7 +20,7 @@ import mage.cards.Card; import mage.constants.*; import mage.counters.CounterType; import mage.filter.FilterCard; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ColorPredicate; import mage.game.Game; @@ -36,7 +36,6 @@ import mage.watchers.common.PlanarRollWatcher; */ public class FeedingGroundsPlane extends Plane { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature"); private static final String rule = "put X +1/+1 counters on target creature, where X is that creature's mana value"; public FeedingGroundsPlane() { @@ -49,7 +48,7 @@ public class FeedingGroundsPlane extends Plane { // Active player can roll the planar die: Whenever you roll {CHAOS}, target red or green creature gets X +1/+1 counters Effect chaosEffect = new AddCountersTargetEffect(CounterType.P1P1.createInstance(), TargetManaValue.instance); - Target chaosTarget = new TargetCreaturePermanent(1, 1, filter, false); + Target chaosTarget = new TargetCreaturePermanent(1, 1, StaticFilters.FILTER_PERMANENT_A_CREATURE, false); List chaosEffects = new ArrayList<>(); chaosEffects.add(chaosEffect); diff --git a/Mage/src/main/java/mage/game/draft/BoosterDraft.java b/Mage/src/main/java/mage/game/draft/BoosterDraft.java index 152834226bd..a0063f8ae60 100644 --- a/Mage/src/main/java/mage/game/draft/BoosterDraft.java +++ b/Mage/src/main/java/mage/game/draft/BoosterDraft.java @@ -34,7 +34,6 @@ public class BoosterDraft extends DraftImpl { } boosterNum++; } - resetBufferedCards(); this.fireEndDraftEvent(); } diff --git a/Mage/src/main/java/mage/game/draft/Draft.java b/Mage/src/main/java/mage/game/draft/Draft.java index 8fef779f78d..c9356980474 100644 --- a/Mage/src/main/java/mage/game/draft/Draft.java +++ b/Mage/src/main/java/mage/game/draft/Draft.java @@ -44,8 +44,6 @@ public interface Draft extends MageItem, Serializable { void addPlayerQueryEventListener(Listener listener); void firePickCardEvent(UUID playerId); - void resetBufferedCards(); - boolean isAbort(); void setAbort(boolean abort); diff --git a/Mage/src/main/java/mage/game/draft/DraftImpl.java b/Mage/src/main/java/mage/game/draft/DraftImpl.java index 9a78e7e98dd..2cab35ab608 100644 --- a/Mage/src/main/java/mage/game/draft/DraftImpl.java +++ b/Mage/src/main/java/mage/game/draft/DraftImpl.java @@ -305,16 +305,4 @@ public abstract class DraftImpl implements Draft { started = true; } - @Override - public void resetBufferedCards() { - Set setsDone = new HashSet<>(); - for (ExpansionSet set : sets) { - if (!setsDone.contains(set)) { - set.removeSavedCards(); - setsDone.add(set); - } - } - - } - } diff --git a/Mage/src/main/java/mage/game/draft/RichManBoosterDraft.java b/Mage/src/main/java/mage/game/draft/RichManBoosterDraft.java index 8f7e3ca70e6..7ca3de815f0 100644 --- a/Mage/src/main/java/mage/game/draft/RichManBoosterDraft.java +++ b/Mage/src/main/java/mage/game/draft/RichManBoosterDraft.java @@ -38,7 +38,6 @@ public class RichManBoosterDraft extends DraftImpl { } boosterNum++; } - resetBufferedCards(); this.fireEndDraftEvent(); } diff --git a/Mage/src/main/java/mage/game/draft/RichManCubeBoosterDraft.java b/Mage/src/main/java/mage/game/draft/RichManCubeBoosterDraft.java index e2d9c02361e..b930a0e80c8 100644 --- a/Mage/src/main/java/mage/game/draft/RichManCubeBoosterDraft.java +++ b/Mage/src/main/java/mage/game/draft/RichManCubeBoosterDraft.java @@ -36,7 +36,6 @@ public class RichManCubeBoosterDraft extends DraftImpl { } boosterNum++; } - resetBufferedCards(); this.fireEndDraftEvent(); } diff --git a/Mage/src/main/java/mage/game/events/GameEvent.java b/Mage/src/main/java/mage/game/events/GameEvent.java index 69c54154821..68e7bfcf676 100644 --- a/Mage/src/main/java/mage/game/events/GameEvent.java +++ b/Mage/src/main/java/mage/game/events/GameEvent.java @@ -148,6 +148,12 @@ public class GameEvent implements Serializable { sourceId sourceId of the vehicle playerId the id of the controlling player */ + VEHICLE_CREWED, + /* VEHICLE_CREWED + targetId the id of the vehicle + sourceId sourceId of the vehicle + playerId the id of the controlling player + */ X_MANA_ANNOUNCE, /* X_MANA_ANNOUNCE mana x-costs announced by players (X value can be changed by replace events like Unbound Flourishing) @@ -334,7 +340,7 @@ public class GameEvent implements Serializable { UNTAP, UNTAPPED, FLIP, FLIPPED, UNFLIP, UNFLIPPED, - TRANSFORM, TRANSFORMED, + TRANSFORM, TRANSFORMING, TRANSFORMED, ADAPT, BECOMES_MONSTROUS, /* BECOMES_EXERTED @@ -356,6 +362,7 @@ public class GameEvent implements Serializable { */ BECOME_MONARCH, BECOMES_MONARCH, + BECOMES_DAY_NIGHT, MEDITATED, PHASE_OUT, PHASED_OUT, PHASE_IN, PHASED_IN, @@ -393,6 +400,7 @@ public class GameEvent implements Serializable { EVOLVED_CREATURE, EMBALMED_CREATURE, ETERNALIZED_CREATURE, + TRAINED_CREATURE, ATTACH, ATTACHED, UNATTACH, UNATTACHED, /* ATTACH, ATTACHED, diff --git a/Mage/src/main/java/mage/game/permanent/Permanent.java b/Mage/src/main/java/mage/game/permanent/Permanent.java index 65b47042538..32573f8c066 100644 --- a/Mage/src/main/java/mage/game/permanent/Permanent.java +++ b/Mage/src/main/java/mage/game/permanent/Permanent.java @@ -47,12 +47,16 @@ public interface Permanent extends Card, Controllable { boolean flip(Game game); - boolean transform(Game game); + boolean transform(Ability source, Game game); + + boolean transform(Ability source, Game game, boolean ignoreDayNight); boolean isTransformed(); void setTransformed(boolean value); + int getTransformCount(); + boolean isPhasedIn(); boolean isPhasedOutIndirectly(); @@ -83,6 +87,10 @@ public interface Permanent extends Card, Controllable { */ boolean setClassLevel(int classLevel); + void addGoadingPlayer(UUID playerId); + + Set getGoadingPlayers(); + void setCardNumber(String cid); void setExpansionSetCode(String expansionSetCode); @@ -289,15 +297,6 @@ public interface Permanent extends Card, Controllable { */ boolean canUseActivatedAbilities(Game game); - /** - * Checks by restriction effects if the permanent can transform - * - * @param ability the ability that causes the transform - * @param game - * @return true - permanent can transform - */ - boolean canTransform(Ability ability, Game game); - boolean removeFromCombat(Game game); boolean removeFromCombat(Game game, boolean withInfo); @@ -413,5 +412,4 @@ public interface Permanent extends Card, Controllable { } return getAttachedTo().equals(otherId); } - } diff --git a/Mage/src/main/java/mage/game/permanent/PermanentCard.java b/Mage/src/main/java/mage/game/permanent/PermanentCard.java index 8b813625aaf..460edc15d17 100644 --- a/Mage/src/main/java/mage/game/permanent/PermanentCard.java +++ b/Mage/src/main/java/mage/game/permanent/PermanentCard.java @@ -5,6 +5,7 @@ import mage.abilities.Abilities; import mage.abilities.Ability; import mage.abilities.costs.mana.ManaCost; import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.keyword.NightboundAbility; import mage.abilities.keyword.TransformAbility; import mage.cards.Card; import mage.cards.LevelerCard; @@ -60,6 +61,7 @@ public class PermanentCard extends PermanentImpl { private void init(Card card, Game game) { power = card.getPower().copy(); toughness = card.getToughness().copy(); + startingLoyalty = card.getStartingLoyalty(); copyFromCard(card, game); // if temporary added abilities to the spell/card exist, you need to add it to the permanent derived from that card Abilities otherAbilities = game.getState().getAllOtherAbilities(card.getId()); @@ -70,10 +72,10 @@ public class PermanentCard extends PermanentImpl { maxLevelCounters = ((LevelerCard) card).getMaxLevelCounters(); } if (isTransformable()) { - if (game.getState().getValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + getId()) != null) { + if (game.getState().getValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + getId()) != null + || NightboundAbility.checkCard(this, game)) { game.getState().setValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + getId(), null); - setTransformed(true); - TransformAbility.transform(this, getSecondCardFace(), game, null); + TransformAbility.transformPermanent(this, getSecondCardFace(), game, null); } } } @@ -132,14 +134,10 @@ public class PermanentCard extends PermanentImpl { this.cardNumber = card.getCardNumber(); this.usesVariousArt = card.getUsesVariousArt(); - this.transformable = card.isTransformable(); - if (this.transformable) { - this.nightCard = card.isNightCard(); - if (!this.nightCard) { - this.secondSideCard = card.getSecondCardFace(); - this.secondSideCardClazz = this.secondSideCard.getClass(); - } + if (card.getSecondCardFace() != null) { + this.secondSideCardClazz = card.getSecondCardFace().getClass(); } + this.nightCard = card.isNightCard(); this.flipCard = card.isFlipCard(); this.flipCardName = card.getFlipCardName(); } diff --git a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java index 78dcabfa8be..e0f809d4f83 100644 --- a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java +++ b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java @@ -15,7 +15,6 @@ import mage.abilities.effects.common.RegenerateSourceEffect; import mage.abilities.hint.Hint; import mage.abilities.hint.HintUtils; import mage.abilities.keyword.*; -import mage.abilities.text.TextPart; import mage.cards.Card; import mage.cards.CardImpl; import mage.constants.*; @@ -73,6 +72,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { protected boolean manifested = false; protected boolean morphed = false; protected int classLevel = 1; + protected final Set goadingPlayers = new HashSet<>(); protected UUID originalControllerId; protected UUID controllerId; protected UUID beforeResetControllerId; @@ -103,6 +103,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { protected List markedDamage; protected int markedLifelink; protected int timesLoyaltyUsed = 0; + protected int transformCount = 0; protected Map info; protected int createOrder; @@ -165,9 +166,11 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { this.monstrous = permanent.monstrous; this.renowned = permanent.renowned; this.classLevel = permanent.classLevel; + this.goadingPlayers.addAll(permanent.goadingPlayers); this.pairedPermanent = permanent.pairedPermanent; this.bandedCards.addAll(permanent.bandedCards); this.timesLoyaltyUsed = permanent.timesLoyaltyUsed; + this.transformCount = permanent.transformCount; this.morphed = permanent.morphed; this.manifested = permanent.manifested; @@ -208,9 +211,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { this.minBlockedBy = 1; this.maxBlockedBy = 0; this.copy = false; - for (TextPart textPart : textParts) { - textPart.reset(); - } + this.goadingPlayers.clear(); } @Override @@ -386,6 +387,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { game.getState().addAbility(copyAbility, sourceId, this); } abilities.add(copyAbility); + abilities.addAll(ability.getSubAbilities()); } } @@ -561,16 +563,46 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { } @Override - public boolean transform(Game game) { - if (transformable) { - if (!replaceEvent(EventType.TRANSFORM, game)) { - setTransformed(!transformed); - game.applyEffects(); - game.addSimultaneousEvent(GameEvent.getEvent(GameEvent.EventType.TRANSFORMED, getId(), getControllerId())); - return true; - } + public boolean transform(Ability source, Game game) { + return this.transform(source, game, false); + } + + private boolean checkDayNightBound() { + return this.getAbilities().containsClass(DayboundAbility.class) + || this.getAbilities().containsClass(NightboundAbility.class); + } + + private Card getOtherFace() { + return transformed ? this.getMainCard() : this.getMainCard().getSecondCardFace(); + } + + @Override + public boolean transform(Ability source, Game game, boolean ignoreDayNight) { + if (!this.isTransformable() + || (!ignoreDayNight && this.checkDayNightBound()) + || this.getOtherFace().isInstantOrSorcery() + || (source != null && !source.checkTransformCount(this, game)) + || this.replaceEvent(EventType.TRANSFORM, game)) { + return false; } - return false; + if (this.transformed) { + Card orgCard = this.getMainCard(); + this.getPower().modifyBaseValue(orgCard.getPower().getValue()); + this.getToughness().modifyBaseValue(orgCard.getToughness().getValue()); + } + game.informPlayers(this.getLogName() + " transforms into " + this.getOtherFace().getLogName() + + CardUtil.getSourceLogName(game, source, this.getId())); + this.setTransformed(!this.transformed); + this.transformCount++; + game.applyEffects(); + this.replaceEvent(EventType.TRANSFORMING, game); + game.addSimultaneousEvent(GameEvent.getEvent(EventType.TRANSFORMED, this.getId(), this.getControllerId())); + return true; + } + + @Override + public int getTransformCount() { + return transformCount; } @Override @@ -1094,19 +1126,35 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { MorphAbility.setPermanentToFaceDownCreature(this, game); } - EntersTheBattlefieldEvent event = new EntersTheBattlefieldEvent(this, source, getControllerId(), fromZone, EnterEventType.SELF); + if (game.replaceEvent(new EntersTheBattlefieldEvent(this, source, getControllerId(), fromZone, EnterEventType.SELF))) { + return false; + } + EntersTheBattlefieldEvent event = new EntersTheBattlefieldEvent(this, source, getControllerId(), fromZone); if (game.replaceEvent(event)) { return false; } - event = new EntersTheBattlefieldEvent(this, source, getControllerId(), fromZone); - if (!game.replaceEvent(event)) { - if (fireEvent) { - game.addSimultaneousEvent(event); - return true; + if (this.isPlaneswalker(game)) { + int loyalty; + if (this.getStartingLoyalty() == -2) { + loyalty = source.getManaCostsToPay().getX(); + } else { + loyalty = this.getStartingLoyalty(); + } + int countersToAdd; + if (this.hasAbility(CompleatedAbility.getInstance(), game)) { + countersToAdd = loyalty - 2 * source.getManaCostsToPay().getPhyrexianPaid(); + } else { + countersToAdd = loyalty; + } + if (countersToAdd > 0) { + this.addCounters(CounterType.LOYALTY.createInstance(countersToAdd), source, game); } - } - return false; + if (!fireEvent) { + return false; + } + game.addSimultaneousEvent(event); + return true; } @Override @@ -1156,14 +1204,11 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { @Override public boolean cantBeAttachedBy(MageObject attachment, Ability source, Game game, boolean silentMode) { for (ProtectionAbility ability : this.getAbilities(game).getProtectionAbilities()) { - if (!(attachment.hasSubtype(SubType.AURA, game) - && !ability.removesAuras()) - && !(attachment.hasSubtype(SubType.EQUIPMENT, game) - && !ability.removesEquipment())) { - if (!attachment.getId().equals(ability.getAuraIdNotToBeRemoved()) - && !ability.canTarget(attachment, game)) { - return !ability.getDoesntRemoveControlled() || isControlledBy(game.getControllerId(attachment.getId())); - } + if ((!attachment.hasSubtype(SubType.AURA, game) || ability.removesAuras()) + && (!attachment.hasSubtype(SubType.EQUIPMENT, game) || ability.removesEquipment()) + && !attachment.getId().equals(ability.getAuraIdNotToBeRemoved()) + && !ability.canTarget(attachment, game)) { + return !ability.getDoesntRemoveControlled() || isControlledBy(game.getControllerId(attachment.getId())); } } return game.getContinuousEffects().preventedByRuleModification(new StayAttachedEvent(this.getId(), attachment.getId(), source), null, game, silentMode); @@ -1300,14 +1345,10 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { } //20101001 - 508.1c if (defenderId == null) { - boolean oneCanBeAttacked = false; - for (UUID defenderToCheckId : game.getCombat().getDefenders()) { - if (canAttackCheckRestrictionEffects(defenderToCheckId, game)) { - oneCanBeAttacked = true; - break; - } - } - if (!oneCanBeAttacked) { + if (game.getCombat() + .getDefenders() + .stream() + .noneMatch(defenderToCheckId -> canAttackCheckRestrictionEffects(defenderToCheckId, game))) { return false; } } else if (!canAttackCheckRestrictionEffects(defenderId, game)) { @@ -1405,21 +1446,6 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { return true; } - @Override - public boolean canTransform(Ability source, Game game) { - if (transformable) { - for (Map.Entry> entry : game.getContinuousEffects().getApplicableRestrictionEffects(this, game).entrySet()) { - RestrictionEffect effect = entry.getKey(); - for (Ability ability : entry.getValue()) { - if (!effect.canTransform(this, ability, game, true)) { - return false; - } - } - } - } - return transformable; - } - @Override public void setAttacking(boolean attacking) { this.attacking = attacking; @@ -1552,6 +1578,16 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { return false; } + @Override + public void addGoadingPlayer(UUID playerId) { + this.goadingPlayers.add(playerId); + } + + @Override + public Set getGoadingPlayers() { + return goadingPlayers; + } + @Override public void setPairedCard(MageObjectReference pairedCard) { this.pairedPermanent = pairedCard; @@ -1749,5 +1785,4 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { detachAllAttachments(game); return successfullyMoved; } - } diff --git a/Mage/src/main/java/mage/game/permanent/PermanentToken.java b/Mage/src/main/java/mage/game/permanent/PermanentToken.java index b6407ac0b1b..3229203e6a0 100644 --- a/Mage/src/main/java/mage/game/permanent/PermanentToken.java +++ b/Mage/src/main/java/mage/game/permanent/PermanentToken.java @@ -86,6 +86,7 @@ public class PermanentToken extends PermanentImpl { this.supertype.addAll(token.getSuperType()); this.subtype.copyFrom(token.getSubtype(game)); this.tokenDescriptor = token.getTokenDescriptor(); + this.startingLoyalty = token.getStartingLoyalty(); } @Override diff --git a/Mage/src/main/java/mage/game/permanent/token/AngelToken.java b/Mage/src/main/java/mage/game/permanent/token/AngelToken.java index 790828d7a26..db5056b296c 100644 --- a/Mage/src/main/java/mage/game/permanent/token/AngelToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/AngelToken.java @@ -16,10 +16,11 @@ public final class AngelToken extends TokenImpl { subtype.add(SubType.ANGEL); power = new MageInt(4); toughness = new MageInt(4); + addAbility(FlyingAbility.getInstance()); availableImageSetCodes = Arrays.asList("APC", "AVR", "C14", "C15", "C18", "CON", "DDQ", "GTC", - "ISD", "M14", "MM3", "NEM", "OGW", "ORI", "PC2", "SCG", "SOI", "ZEN", "C20", "M21", "CMR"); + "ISD", "M14", "MM3", "NEM", "OGW", "ORI", "PC2", "SCG", "SOI", "ZEN", "C20", "M21", "CMR", "AFC", "VOC"); } public AngelToken(final AngelToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/AnotherSpiritToken.java b/Mage/src/main/java/mage/game/permanent/token/AnotherSpiritToken.java index e6f0fc6fd9b..9d31a09e05d 100644 --- a/Mage/src/main/java/mage/game/permanent/token/AnotherSpiritToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/AnotherSpiritToken.java @@ -1,13 +1,13 @@ - - package mage.game.permanent.token; -import mage.constants.CardType; -import mage.constants.SubType; + import mage.MageInt; import mage.abilities.keyword.FlyingAbility; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.Arrays; /** - * * @author spjspj */ public final class AnotherSpiritToken extends TokenImpl { @@ -19,8 +19,21 @@ public final class AnotherSpiritToken extends TokenImpl { subtype.add(SubType.SPIRIT); power = new MageInt(3); toughness = new MageInt(3); + this.addAbility(FlyingAbility.getInstance()); + + availableImageSetCodes = Arrays.asList("BOK", "VOC"); } + + @Override + public void setExpansionSetCodeForImage(String code) { + super.setExpansionSetCodeForImage(code); + + if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("VOC")) { + setTokenType(2); + } + } + public AnotherSpiritToken(final AnotherSpiritToken token) { super(token); } diff --git a/Mage/src/main/java/mage/game/permanent/token/BatToken.java b/Mage/src/main/java/mage/game/permanent/token/BatToken.java index 6e22ffb8ce4..fab90e6b54e 100644 --- a/Mage/src/main/java/mage/game/permanent/token/BatToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/BatToken.java @@ -1,24 +1,16 @@ package mage.game.permanent.token; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; import mage.MageInt; import mage.abilities.keyword.FlyingAbility; import mage.constants.CardType; import mage.constants.SubType; -public final class BatToken extends TokenImpl { - - static final private List tokenImageSets = new ArrayList<>(); +import java.util.Arrays; - static { - tokenImageSets.addAll(Arrays.asList("MMA", "C17")); - } +public final class BatToken extends TokenImpl { public BatToken() { super("Bat", "1/1 black Bat creature token with flying"); - availableImageSetCodes = tokenImageSets; cardType.add(CardType.CREATURE); color.setBlack(true); subtype.add(SubType.BAT); @@ -26,8 +18,9 @@ public final class BatToken extends TokenImpl { toughness = new MageInt(1); this.addAbility(FlyingAbility.getInstance()); - this.setOriginalExpansionSetCode("MMA"); + availableImageSetCodes = Arrays.asList("GVL", "DDD", "GPT", "MMA", "M19", "MID", "VOC"); } + public BatToken(final BatToken token) { super(token); } diff --git a/Mage/src/main/java/mage/game/permanent/token/BeastToken.java b/Mage/src/main/java/mage/game/permanent/token/BeastToken.java index daf37552e6a..ae7bd504d88 100644 --- a/Mage/src/main/java/mage/game/permanent/token/BeastToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/BeastToken.java @@ -22,7 +22,7 @@ public final class BeastToken extends TokenImpl { availableImageSetCodes = Arrays.asList("5DN", "C14", "C16", "C19", "CMA", "CMD", "CN2", "GVL", "DD3C", "DD3GVL", "DDD", "DDL", "DST", "E01", "EVE", "LRW", "M10", "M11", "M12", - "M13", "M14", "M15", "MM3", "NPH", "PC2", "USG", "M19", "IKO", "M21", "CMR", "C21"); + "M13", "M14", "M15", "MM3", "NPH", "PC2", "USG", "M19", "IKO", "M21", "CMR", "C21", "AFC", "MIC"); } @Override diff --git a/Mage/src/main/java/mage/game/permanent/token/BeastToken2.java b/Mage/src/main/java/mage/game/permanent/token/BeastToken2.java index 48cb131ed5a..2387f23798b 100644 --- a/Mage/src/main/java/mage/game/permanent/token/BeastToken2.java +++ b/Mage/src/main/java/mage/game/permanent/token/BeastToken2.java @@ -20,7 +20,7 @@ public final class BeastToken2 extends TokenImpl { toughness = new MageInt(4); availableImageSetCodes = Arrays.asList("C13", "C14", "C15", "C19", "CMA", "CMD", "GVL", "DDD", - "E01", "ODY", "SCG", "ZEN", "C20", "ZNC", "CMR", "C21", "MH2"); + "E01", "ODY", "SCG", "ZEN", "C20", "ZNC", "CMR", "C21", "MH2", "MID"); } public BeastToken2(final BeastToken2 token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/BirdSoldierToken.java b/Mage/src/main/java/mage/game/permanent/token/BirdSoldierToken.java index 6360df3ff7d..3ea235fbb88 100644 --- a/Mage/src/main/java/mage/game/permanent/token/BirdSoldierToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/BirdSoldierToken.java @@ -13,7 +13,7 @@ import mage.abilities.keyword.FlyingAbility; public final class BirdSoldierToken extends TokenImpl { public BirdSoldierToken() { - super("Bird Soldier", "1/1 white Bird Soldier creature with flying"); + super("Bird Soldier", "1/1 white Bird Soldier creature token with flying"); cardType.add(CardType.CREATURE); subtype.add(SubType.BIRD); diff --git a/Mage/src/main/java/mage/game/permanent/token/BloodToken.java b/Mage/src/main/java/mage/game/permanent/token/BloodToken.java new file mode 100644 index 00000000000..e7b92e36f79 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/BloodToken.java @@ -0,0 +1,44 @@ +package mage.game.permanent.token; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.Arrays; + +/** + * @author TheElk801 + */ +public final class BloodToken extends TokenImpl { + + public BloodToken() { + super("Blood", "Blood token"); + cardType.add(CardType.ARTIFACT); + subtype.add(SubType.BLOOD); + + // {1}, {T}, Discard a card, Sacrifice this artifact: Draw a card.” + Ability ability = new SimpleActivatedAbility( + new DrawCardSourceControllerEffect(1), new GenericManaCost(1) + ); + ability.addCost(new TapSourceCost()); + ability.addCost(new DiscardCardCost()); + ability.addCost(new SacrificeSourceCost().setText("Sacrifice this artifact")); + this.addAbility(ability); + + availableImageSetCodes = Arrays.asList("VOW"); + } + + public BloodToken(final BloodToken token) { + super(token); + } + + public BloodToken copy() { + return new BloodToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/Boar3Token.java b/Mage/src/main/java/mage/game/permanent/token/Boar3Token.java new file mode 100644 index 00000000000..79f26040bcc --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/Boar3Token.java @@ -0,0 +1,32 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.Arrays; + +/** + * @author TheElk801 + */ +public final class Boar3Token extends TokenImpl { + + public Boar3Token() { + super("Boar", "3/1 green Boar creature token"); + cardType.add(CardType.CREATURE); + color.setGreen(true); + subtype.add(SubType.BOAR); + power = new MageInt(3); + toughness = new MageInt(1); + + availableImageSetCodes = Arrays.asList("VOW"); + } + + public Boar3Token(final Boar3Token token) { + super(token); + } + + public Boar3Token copy() { + return new Boar3Token(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/CentaurToken.java b/Mage/src/main/java/mage/game/permanent/token/CentaurToken.java index 2af5a0e77f3..03a8c83ca71 100644 --- a/Mage/src/main/java/mage/game/permanent/token/CentaurToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/CentaurToken.java @@ -5,32 +5,22 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.util.RandomUtil; -import java.util.ArrayList; import java.util.Arrays; -import java.util.List; /** * @author LevelX2 */ public final class CentaurToken extends TokenImpl { - static final private List tokenImageSets = new ArrayList<>(); - - static { - tokenImageSets.addAll(Arrays.asList("RTR", "MM3", "RNA", "C19")); - } - public CentaurToken() { super("Centaur", "3/3 green Centaur creature token"); cardType.add(CardType.CREATURE); - availableImageSetCodes = tokenImageSets; - if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("RNA")) { - setTokenType(RandomUtil.nextInt(2) + 1); // randomly take image 1 or 2 - } color.setGreen(true); subtype.add(SubType.CENTAUR); power = new MageInt(3); toughness = new MageInt(3); + + availableImageSetCodes = Arrays.asList("C19", "DGM", "ONS", "RTR", "RNA", "MIC"); } public CentaurToken(final CentaurToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/ClueArtifactToken.java b/Mage/src/main/java/mage/game/permanent/token/ClueArtifactToken.java index 26618d43d2e..78d5f3535b0 100644 --- a/Mage/src/main/java/mage/game/permanent/token/ClueArtifactToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/ClueArtifactToken.java @@ -29,7 +29,7 @@ public final class ClueArtifactToken extends TokenImpl { ability.addCost(cost); this.addAbility(ability); - availableImageSetCodes = Arrays.asList("C18", "SOI", "MH2"); + availableImageSetCodes = Arrays.asList("C18", "SOI", "MH2", "AFC", "MID", "VOC"); } @Override diff --git a/Mage/src/main/java/mage/game/permanent/token/ConstructRedToken.java b/Mage/src/main/java/mage/game/permanent/token/ConstructRedToken.java new file mode 100644 index 00000000000..84a3e7df896 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/ConstructRedToken.java @@ -0,0 +1,31 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.abilities.keyword.HasteAbility; +import mage.constants.CardType; +import mage.constants.SubType; + +/** + * @author TheElk801 + */ +public final class ConstructRedToken extends TokenImpl { + + public ConstructRedToken() { + super("Construct token", "3/1 red Construct artifact creature token with haste"); + cardType.add(CardType.ARTIFACT); + cardType.add(CardType.CREATURE); + subtype.add(SubType.CONSTRUCT); + color.setRed(true); + power = new MageInt(3); + toughness = new MageInt(1); + addAbility(HasteAbility.getInstance()); + } + + public ConstructRedToken(final ConstructRedToken token) { + super(token); + } + + public ConstructRedToken copy() { + return new ConstructRedToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/ConsumingBlobToken.java b/Mage/src/main/java/mage/game/permanent/token/ConsumingBlobOozeToken.java similarity index 85% rename from Mage/src/main/java/mage/game/permanent/token/ConsumingBlobToken.java rename to Mage/src/main/java/mage/game/permanent/token/ConsumingBlobOozeToken.java index d3d113fc666..3341105ea9e 100644 --- a/Mage/src/main/java/mage/game/permanent/token/ConsumingBlobToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/ConsumingBlobOozeToken.java @@ -10,14 +10,15 @@ import mage.abilities.hint.common.CardTypesInGraveyardHint; import mage.constants.*; import mage.game.Game; +import java.util.Arrays; + /** * @author ciaccona007 */ -public final class ConsumingBlobToken extends TokenImpl { +public final class ConsumingBlobOozeToken extends TokenImpl { - public ConsumingBlobToken() { + public ConsumingBlobOozeToken() { super("Ooze", "green Ooze creature token with \"This creature's power is equal to the number of card types among cards in your graveyard and its toughness is equal to that number plus 1.\""); - setOriginalExpansionSetCode("MID"); cardType.add(CardType.CREATURE); subtype.add(SubType.OOZE); color.setGreen(true); @@ -28,15 +29,16 @@ public final class ConsumingBlobToken extends TokenImpl { // This creature's power is equal to the number of card types among cards in your graveyard and its toughness is equal to that number plus 1. this.addAbility(new SimpleStaticAbility(Zone.ALL, new ConsumingBlobTokenEffect()).addHint(CardTypesInGraveyardHint.YOU)); + availableImageSetCodes.addAll(Arrays.asList("MID")); } - private ConsumingBlobToken(final ConsumingBlobToken token) { + private ConsumingBlobOozeToken(final ConsumingBlobOozeToken token) { super(token); } @Override - public ConsumingBlobToken copy() { - return new ConsumingBlobToken(this); + public ConsumingBlobOozeToken copy() { + return new ConsumingBlobOozeToken(this); } } diff --git a/Mage/src/main/java/mage/game/permanent/token/CustomIllusionToken.java b/Mage/src/main/java/mage/game/permanent/token/CustomIllusionToken.java index 6b4b7960210..24d24f7f3f3 100644 --- a/Mage/src/main/java/mage/game/permanent/token/CustomIllusionToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/CustomIllusionToken.java @@ -11,6 +11,10 @@ import java.util.Arrays; */ public final class CustomIllusionToken extends TokenImpl { + public CustomIllusionToken() { + this(0); + } + public CustomIllusionToken(int xValue) { super("Illusion", "X/X blue Illusion creature token"); cardType.add(CardType.CREATURE); diff --git a/Mage/src/main/java/mage/game/permanent/token/DevilToken.java b/Mage/src/main/java/mage/game/permanent/token/DevilToken.java index 78708614649..729af8219a0 100644 --- a/Mage/src/main/java/mage/game/permanent/token/DevilToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/DevilToken.java @@ -21,8 +21,6 @@ public final class DevilToken extends TokenImpl { public DevilToken() { super("Devil", "1/1 red Devil creature token with \"When this creature dies, it deals 1 damage to any target.\""); - availableImageSetCodes.addAll(Collections.singletonList("SOI")); - availableImageSetCodes.addAll(Collections.singletonList("WAR")); cardType.add(CardType.CREATURE); subtype.add(SubType.DEVIL); color.setRed(true); @@ -36,7 +34,7 @@ public final class DevilToken extends TokenImpl { ability.addTarget(new TargetAnyTarget()); this.addAbility(ability); - availableImageSetCodes = Arrays.asList("SOI", "WAR", "AFR"); + availableImageSetCodes = Arrays.asList("SOI", "WAR", "AFR", "MID"); } public DevilToken(final DevilToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/DorotheasRetributionSpiritToken.java b/Mage/src/main/java/mage/game/permanent/token/DorotheasRetributionSpiritToken.java new file mode 100644 index 00000000000..de468ca1d17 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/DorotheasRetributionSpiritToken.java @@ -0,0 +1,45 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.abilities.keyword.FlyingAbility; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.Arrays; + +/** + * @author JayDi85 + */ +public final class DorotheasRetributionSpiritToken extends TokenImpl { + + public DorotheasRetributionSpiritToken() { + super("Spirit", "4/4 white Spirit creature token with flying"); + cardType.add(CardType.CREATURE); + subtype.add(SubType.SPIRIT); + color.setWhite(true); + power = new MageInt(4); + toughness = new MageInt(4); + + this.addAbility(FlyingAbility.getInstance()); + + availableImageSetCodes = Arrays.asList("VOW"); + } + + @Override + public void setExpansionSetCodeForImage(String code) { + super.setExpansionSetCodeForImage(code); + + if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("VOW")) { + setTokenType(2); + } + } + + public DorotheasRetributionSpiritToken(final DorotheasRetributionSpiritToken token) { + super(token); + } + + @Override + public DorotheasRetributionSpiritToken copy() { + return new DorotheasRetributionSpiritToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/DragonIllusionToken.java b/Mage/src/main/java/mage/game/permanent/token/DragonIllusionToken.java new file mode 100644 index 00000000000..0a1386500cf --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/DragonIllusionToken.java @@ -0,0 +1,44 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.HasteAbility; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.Arrays; + +/** + * + * @author weirddan455 + */ +public class DragonIllusionToken extends TokenImpl { + + public DragonIllusionToken() { + this(0); + } + + public DragonIllusionToken(int xValue) { + super("Dragon Illusion", "X/X red Dragon Illusion creature token with flying and haste"); + cardType.add(CardType.CREATURE); + color.setRed(true); + subtype.add(SubType.DRAGON); + subtype.add(SubType.ILLUSION); + power = new MageInt(xValue); + toughness = new MageInt(xValue); + + this.addAbility(FlyingAbility.getInstance()); + this.addAbility(HasteAbility.getInstance()); + + availableImageSetCodes = Arrays.asList("VOW"); + } + + private DragonIllusionToken(final DragonIllusionToken token) { + super(token); + } + + @Override + public DragonIllusionToken copy() { + return new DragonIllusionToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/DragonSpiritToken.java b/Mage/src/main/java/mage/game/permanent/token/DragonSpiritToken.java new file mode 100644 index 00000000000..e8dac34bae5 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/DragonSpiritToken.java @@ -0,0 +1,31 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.abilities.keyword.FlyingAbility; +import mage.constants.CardType; +import mage.constants.SubType; + +/** + * @author TheElk801 + */ +public final class DragonSpiritToken extends TokenImpl { + + public DragonSpiritToken() { + super("Dragon Spirit token", "5/5 red Dragon Spirit creature token with flying"); + cardType.add(CardType.CREATURE); + color.setRed(true); + subtype.add(SubType.DRAGON); + subtype.add(SubType.SPIRIT); + power = new MageInt(5); + toughness = new MageInt(5); + addAbility(FlyingAbility.getInstance()); + } + + public DragonSpiritToken(final DragonSpiritToken token) { + super(token); + } + + public DragonSpiritToken copy() { + return new DragonSpiritToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/DragonToken2.java b/Mage/src/main/java/mage/game/permanent/token/DragonToken2.java index a971abf756d..9dd33ac0b8d 100644 --- a/Mage/src/main/java/mage/game/permanent/token/DragonToken2.java +++ b/Mage/src/main/java/mage/game/permanent/token/DragonToken2.java @@ -22,7 +22,7 @@ public final class DragonToken2 extends TokenImpl { addAbility(FlyingAbility.getInstance()); - availableImageSetCodes = Arrays.asList("10E", "BFZ", "C15", "C19", "CMA", "CMD", "ONS", "ROE", "SCG", "WWK", "M19", "KHM"); + availableImageSetCodes = Arrays.asList("10E", "BFZ", "C15", "C19", "CMA", "CMD", "ONS", "ROE", "SCG", "WWK", "M19", "KHM", "AFC"); } public DragonToken2(final DragonToken2 token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/DrakeToken.java b/Mage/src/main/java/mage/game/permanent/token/DrakeToken.java index bfcf8819f1e..9fbd97fa032 100644 --- a/Mage/src/main/java/mage/game/permanent/token/DrakeToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/DrakeToken.java @@ -18,7 +18,7 @@ public final class DrakeToken extends TokenImpl { this.cardType.add(CardType.CREATURE); this.subtype.add(SubType.DRAKE); - this.color = ObjectColor.BLUE; + this.color.setBlue(true); this.power = new MageInt(2); this.toughness = new MageInt(2); diff --git a/Mage/src/main/java/mage/game/permanent/token/EdgarMarkovsCoffinVampireToken.java b/Mage/src/main/java/mage/game/permanent/token/EdgarMarkovsCoffinVampireToken.java new file mode 100644 index 00000000000..d625f5c6732 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/EdgarMarkovsCoffinVampireToken.java @@ -0,0 +1,37 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.abilities.keyword.LifelinkAbility; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.Arrays; + +/** + * @author TheElk801 + */ +public final class EdgarMarkovsCoffinVampireToken extends TokenImpl { + + public EdgarMarkovsCoffinVampireToken() { + super("Vampire", "1/1 white and black Vampire creature token with lifelink"); + cardType.add(CardType.CREATURE); + color.setWhite(true); + color.setBlack(true); + subtype.add(SubType.VAMPIRE); + power = new MageInt(1); + toughness = new MageInt(1); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + availableImageSetCodes = Arrays.asList("VOW"); + } + + public EdgarMarkovsCoffinVampireToken(final EdgarMarkovsCoffinVampireToken token) { + super(token); + } + + public EdgarMarkovsCoffinVampireToken copy() { + return new EdgarMarkovsCoffinVampireToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/EldraziSpawnToken.java b/Mage/src/main/java/mage/game/permanent/token/EldraziSpawnToken.java index dbbc9dcbdad..01f5e695fdd 100644 --- a/Mage/src/main/java/mage/game/permanent/token/EldraziSpawnToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/EldraziSpawnToken.java @@ -1,10 +1,6 @@ package mage.game.permanent.token; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - import mage.MageInt; import mage.Mana; import mage.abilities.costs.common.SacrificeSourceCost; @@ -14,18 +10,13 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.util.RandomUtil; +import java.util.Arrays; + /** - * * @author BetaSteward_at_googlemail.com */ public final class EldraziSpawnToken extends TokenImpl { - static final private List tokenImageSets = new ArrayList<>(); - - static { - tokenImageSets.addAll(Arrays.asList("ROE", "MM2", "DDP", "C17")); - } - public EldraziSpawnToken() { super("Eldrazi Spawn", "0/1 colorless Eldrazi Spawn creature with \"Sacrifice this creature: Add {C}.\""); cardType.add(CardType.CREATURE); @@ -35,9 +26,24 @@ public final class EldraziSpawnToken extends TokenImpl { toughness = new MageInt(1); addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.ColorlessMana(1), new SacrificeSourceCost())); - availableImageSetCodes = tokenImageSets; - // Get one of the four possible token images - this.setTokenType(RandomUtil.nextInt(4) + 1); + availableImageSetCodes = Arrays.asList("CMD", "DDP", "MM2", "PC2", "ROE", "MIC"); + } + + @Override + public void setExpansionSetCodeForImage(String code) { + super.setExpansionSetCodeForImage(code); + + if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("DDP")) { + this.setTokenType(RandomUtil.nextInt(3) + 1); // randomly take image 1, 2 or 3 + } + + if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("MM2")) { + this.setTokenType(RandomUtil.nextInt(3) + 1); // randomly take image 1, 2 or 3 + } + + if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("ROE")) { + this.setTokenType(RandomUtil.nextInt(3) + 1); // randomly take image 1, 2 or 3 + } } public EldraziSpawnToken(final EldraziSpawnToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/ElephantToken.java b/Mage/src/main/java/mage/game/permanent/token/ElephantToken.java index e317c6583d2..b7884146289 100644 --- a/Mage/src/main/java/mage/game/permanent/token/ElephantToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/ElephantToken.java @@ -20,7 +20,7 @@ public final class ElephantToken extends TokenImpl { toughness = new MageInt(3); availableImageSetCodes = Arrays.asList("C13", "C14", "C15", "CMA", "CMD", "CNS", "GVL", "DDD", - "EMA", "INV", "JUD", "MM2", "ODY", "ROE", "TSP", "VMA", "WWK", "MH1", "CMR", "C21"); + "EMA", "INV", "JUD", "MM2", "ODY", "ROE", "TSP", "VMA", "WWK", "MH1", "CMR", "C21", "MIC"); } public ElephantToken(final ElephantToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/ElfDruidToken.java b/Mage/src/main/java/mage/game/permanent/token/ElfDruidToken.java index 38c925dbeb7..04adef5c71a 100644 --- a/Mage/src/main/java/mage/game/permanent/token/ElfDruidToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/ElfDruidToken.java @@ -26,7 +26,7 @@ public final class ElfDruidToken extends TokenImpl { public ElfDruidToken(String setCode, int tokenType) { super("Elf Druid", "1/1 green Elf Druid creature token with \"{T}: Add {G}.\""); this.cardType.add(CardType.CREATURE); - this.color = ObjectColor.GREEN; + this.color.setGreen(true); this.subtype.add(SubType.ELF); this.subtype.add(SubType.DRUID); diff --git a/Mage/src/main/java/mage/game/permanent/token/EtheriumCellToken.java b/Mage/src/main/java/mage/game/permanent/token/EtheriumCellToken.java index ff472627b30..a1bc68568af 100644 --- a/Mage/src/main/java/mage/game/permanent/token/EtheriumCellToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/EtheriumCellToken.java @@ -16,7 +16,7 @@ import mage.constants.Zone; public final class EtheriumCellToken extends TokenImpl { public EtheriumCellToken() { - super("Etherium Cell", "colorless artifact token named Etherium Cell which has \"{T}, Sacrifice this artifact: Add one mana of any color.\""); + super("Etherium Cell", "colorless artifact token named Etherium Cell with \"{T}, Sacrifice this artifact: Add one mana of any color.\""); this.setOriginalExpansionSetCode("AER"); cardType.add(CardType.ARTIFACT); diff --git a/Mage/src/main/java/mage/game/permanent/token/FableOfTheMirrorBreakerToken.java b/Mage/src/main/java/mage/game/permanent/token/FableOfTheMirrorBreakerToken.java new file mode 100644 index 00000000000..bb211bcd3b6 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/FableOfTheMirrorBreakerToken.java @@ -0,0 +1,33 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.constants.CardType; +import mage.constants.SubType; + +/** + * @author TheElk801 + */ +public class FableOfTheMirrorBreakerToken extends TokenImpl { + + public FableOfTheMirrorBreakerToken() { + super("Goblin Shaman Token", "2/2 red Goblin Shaman creature token with \"Whenever this creature attacks, create a Treasure token.\""); + cardType.add(CardType.CREATURE); + color.setRed(true); + subtype.add(SubType.GOBLIN); + subtype.add(SubType.SHAMAN); + power = new MageInt(2); + toughness = new MageInt(2); + addAbility(new AttacksTriggeredAbility(new CreateTokenEffect(new TreasureToken())).setTriggerPhrase("Whenever this creature attacks, ")); + } + + private FableOfTheMirrorBreakerToken(final FableOfTheMirrorBreakerToken token) { + super(token); + } + + @Override + public FableOfTheMirrorBreakerToken copy() { + return new FableOfTheMirrorBreakerToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/GrakmawSkyclaveRavagerHydraToken.java b/Mage/src/main/java/mage/game/permanent/token/GrakmawSkyclaveRavagerHydraToken.java index a19eeb64773..e7b90016008 100644 --- a/Mage/src/main/java/mage/game/permanent/token/GrakmawSkyclaveRavagerHydraToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/GrakmawSkyclaveRavagerHydraToken.java @@ -11,6 +11,10 @@ import java.util.Arrays; */ public final class GrakmawSkyclaveRavagerHydraToken extends TokenImpl { + public GrakmawSkyclaveRavagerHydraToken() { + this(0); + } + public GrakmawSkyclaveRavagerHydraToken(int xValue) { super("Hydra", "X/X black and green Hydra creature token"); cardType.add(CardType.CREATURE); diff --git a/Mage/src/main/java/mage/game/permanent/token/HumanMonkToken.java b/Mage/src/main/java/mage/game/permanent/token/HumanMonkToken.java new file mode 100644 index 00000000000..40160a0bf57 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/HumanMonkToken.java @@ -0,0 +1,35 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.abilities.mana.GreenManaAbility; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.Arrays; + +/** + * @author TheElk801 + */ +public final class HumanMonkToken extends TokenImpl { + + public HumanMonkToken() { + super("Human Monk token", "1/1 green Human Monk creature token with \"{T}: Add {G}.\""); + cardType.add(CardType.CREATURE); + color.setGreen(true); + subtype.add(SubType.HUMAN); + subtype.add(SubType.MONK); + power = new MageInt(1); + toughness = new MageInt(1); + addAbility(new GreenManaAbility()); + + availableImageSetCodes = Arrays.asList("NEO"); + } + + public HumanMonkToken(final HumanMonkToken token) { + super(token); + } + + public HumanMonkToken copy() { + return new HumanMonkToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/HumanSoldierToken.java b/Mage/src/main/java/mage/game/permanent/token/HumanSoldierToken.java index d331b634d5b..8d6dbe2daaa 100644 --- a/Mage/src/main/java/mage/game/permanent/token/HumanSoldierToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/HumanSoldierToken.java @@ -5,6 +5,8 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.util.RandomUtil; +import java.util.Arrays; + public final class HumanSoldierToken extends TokenImpl { public HumanSoldierToken() { @@ -15,6 +17,8 @@ public final class HumanSoldierToken extends TokenImpl { color.setWhite(true); power = new MageInt(1); toughness = new MageInt(1); + + availableImageSetCodes = Arrays.asList("SOI", "THB", "IKO", "MIC"); } @Override diff --git a/Mage/src/main/java/mage/game/permanent/token/HumanSoldierTrainingToken.java b/Mage/src/main/java/mage/game/permanent/token/HumanSoldierTrainingToken.java new file mode 100644 index 00000000000..6f87411e6d9 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/HumanSoldierTrainingToken.java @@ -0,0 +1,39 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.abilities.keyword.TrainingAbility; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.Arrays; + +/** + * @author TheElk801 + */ +public class HumanSoldierTrainingToken extends TokenImpl { + + public HumanSoldierTrainingToken() { + super("Human Soldier", "1/1 green and white Human Soldier creature token with training"); + cardType.add(CardType.CREATURE); + color.setGreen(true); + color.setWhite(true); + subtype.add(SubType.HUMAN); + subtype.add(SubType.SOLDIER); + power = new MageInt(1); + toughness = new MageInt(1); + + // Training (Whenever this creature attacks with another creature with greater power, put a +1/+1 counter on this creature.) + this.addAbility(new TrainingAbility()); + + availableImageSetCodes = Arrays.asList("VOW"); + } + + private HumanSoldierTrainingToken(final HumanSoldierTrainingToken token) { + super(token); + } + + @Override + public HumanSoldierTrainingToken copy() { + return new HumanSoldierTrainingToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/HumanToken.java b/Mage/src/main/java/mage/game/permanent/token/HumanToken.java index f0df12ed622..873aa867551 100644 --- a/Mage/src/main/java/mage/game/permanent/token/HumanToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/HumanToken.java @@ -18,7 +18,8 @@ public final class HumanToken extends TokenImpl { subtype.add(SubType.HUMAN); power = new MageInt(1); toughness = new MageInt(1); - availableImageSetCodes.addAll(Arrays.asList("DKA", "AVR", "FNMP", "RNA", "ELD", "C19", "C20")); + + availableImageSetCodes.addAll(Arrays.asList("DKA", "AVR", "FNMP", "RNA", "ELD", "C19", "C20", "MID", "VOW")); } public HumanToken(final HumanToken token) { @@ -33,8 +34,9 @@ public final class HumanToken extends TokenImpl { @Override public void setExpansionSetCodeForImage(String code) { super.setExpansionSetCodeForImage(code); - if (getOriginalExpansionSetCode().equals("AVR")) { - this.setTokenType(1); + + if (getOriginalExpansionSetCode().equals("VOW")) { + this.setTokenType(2); } } } diff --git a/Mage/src/main/java/mage/game/permanent/token/HungryForMoreToken.java b/Mage/src/main/java/mage/game/permanent/token/HungryForMoreVampireToken.java similarity index 68% rename from Mage/src/main/java/mage/game/permanent/token/HungryForMoreToken.java rename to Mage/src/main/java/mage/game/permanent/token/HungryForMoreVampireToken.java index 6f41a946a4d..30f7d80e98a 100644 --- a/Mage/src/main/java/mage/game/permanent/token/HungryForMoreToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/HungryForMoreVampireToken.java @@ -7,12 +7,14 @@ import mage.abilities.keyword.TrampleAbility; import mage.constants.CardType; import mage.constants.SubType; +import java.util.Arrays; + /** * @author TheElk801 */ -public final class HungryForMoreToken extends TokenImpl { +public final class HungryForMoreVampireToken extends TokenImpl { - public HungryForMoreToken() { + public HungryForMoreVampireToken() { super("Vampire", "3/1 black and red Vampire creature token with trample, lifelink, and haste"); cardType.add(CardType.CREATURE); color.setRed(true); @@ -23,13 +25,15 @@ public final class HungryForMoreToken extends TokenImpl { addAbility(TrampleAbility.getInstance()); addAbility(LifelinkAbility.getInstance()); addAbility(HasteAbility.getInstance()); + + availableImageSetCodes.addAll(Arrays.asList("MID")); } - public HungryForMoreToken(final HungryForMoreToken token) { + public HungryForMoreVampireToken(final HungryForMoreVampireToken token) { super(token); } - public HungryForMoreToken copy() { - return new HungryForMoreToken(this); + public HungryForMoreVampireToken copy() { + return new HungryForMoreVampireToken(this); } } diff --git a/Mage/src/main/java/mage/game/permanent/token/InsectToken.java b/Mage/src/main/java/mage/game/permanent/token/InsectToken.java index 9f7e4d32114..272c30fd96a 100644 --- a/Mage/src/main/java/mage/game/permanent/token/InsectToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/InsectToken.java @@ -24,7 +24,7 @@ public final class InsectToken extends TokenImpl { power = new MageInt(1); toughness = new MageInt(1); - availableImageSetCodes = Arrays.asList("M10", "MM2", "SOI", "ZNR"); + availableImageSetCodes = Arrays.asList("M10", "MM2", "SOI", "ZNR", "VOW"); } public InsectToken(final InsectToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/KeimiToken.java b/Mage/src/main/java/mage/game/permanent/token/KeimiToken.java new file mode 100644 index 00000000000..fb154e2d992 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/KeimiToken.java @@ -0,0 +1,47 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.LoseLifeOpponentsEffect; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.StaticFilters; + +import java.util.Arrays; + +/** + * @author TheElk801 + */ +public final class KeimiToken extends TokenImpl { + + public KeimiToken() { + super("Keimi", "Keimi, a legendary 3/3 black and green Frog creature token with \"Whenever you cast an enchantment spell, each opponent loses 1 life and you gain 1 life.\""); + supertype.add(SuperType.LEGENDARY); + cardType.add(CardType.CREATURE); + color.setBlack(true); + color.setGreen(true); + subtype.add(SubType.FROG); + power = new MageInt(3); + toughness = new MageInt(3); + + Ability ability = new SpellCastControllerTriggeredAbility( + new LoseLifeOpponentsEffect(1), + StaticFilters.FILTER_SPELL_AN_ENCHANTMENT, false + ); + ability.addEffect(new GainLifeEffect(1).concatBy("and")); + this.addAbility(ability); + + availableImageSetCodes = Arrays.asList("NEO"); + } + + private KeimiToken(final KeimiToken token) { + super(token); + } + + public KeimiToken copy() { + return new KeimiToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/KnightToken.java b/Mage/src/main/java/mage/game/permanent/token/KnightToken.java index 780caeb3554..7c3adb84c41 100644 --- a/Mage/src/main/java/mage/game/permanent/token/KnightToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/KnightToken.java @@ -23,7 +23,7 @@ public final class KnightToken extends TokenImpl { toughness = new MageInt(2); this.addAbility(VigilanceAbility.getInstance()); - availableImageSetCodes = Arrays.asList("C13", "C15", "CMA", "DGM", "ORI", "RTR", "M19", "ELD", "M21"); + availableImageSetCodes = Arrays.asList("C13", "C15", "CMA", "DGM", "ORI", "RTR", "M19", "ELD", "M21", "AFC", "MIC"); } public KnightToken(final KnightToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/KrakenHexproofToken.java b/Mage/src/main/java/mage/game/permanent/token/KrakenHexproofToken.java index e4d61dc9ab3..64c9b08ed2b 100644 --- a/Mage/src/main/java/mage/game/permanent/token/KrakenHexproofToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/KrakenHexproofToken.java @@ -16,7 +16,7 @@ public final class KrakenHexproofToken extends TokenImpl { super("Kraken", "8/8 blue Kraken creature token with hexproof"); this.cardType.add(CardType.CREATURE); this.subtype.add(SubType.KRAKEN); - this.color = ObjectColor.BLUE; + this.color.setBlue(true); this.power = new MageInt(8); this.toughness = new MageInt(8); this.addAbility(HexproofAbility.getInstance()); @@ -29,5 +29,4 @@ public final class KrakenHexproofToken extends TokenImpl { public KrakenHexproofToken copy() { return new KrakenHexproofToken(this); } - -} \ No newline at end of file +} diff --git a/Mage/src/main/java/mage/game/permanent/token/KrakenToken.java b/Mage/src/main/java/mage/game/permanent/token/KrakenToken.java index 2dc5194b461..7a28867ffbb 100644 --- a/Mage/src/main/java/mage/game/permanent/token/KrakenToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/KrakenToken.java @@ -15,7 +15,7 @@ public final class KrakenToken extends TokenImpl { super("Kraken", "8/8 blue Kraken creature token"); this.cardType.add(CardType.CREATURE); this.subtype.add(SubType.KRAKEN); - this.color = ObjectColor.BLUE; + this.color.setBlue(true); this.power = new MageInt(8); this.toughness = new MageInt(8); } @@ -27,5 +27,4 @@ public final class KrakenToken extends TokenImpl { public KrakenToken copy() { return new KrakenToken(this); } - -} \ No newline at end of file +} diff --git a/Mage/src/main/java/mage/game/permanent/token/MechtitanToken.java b/Mage/src/main/java/mage/game/permanent/token/MechtitanToken.java new file mode 100644 index 00000000000..7d9851edcb7 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/MechtitanToken.java @@ -0,0 +1,41 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.abilities.keyword.*; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; + +/** + * @author TheElk801 + */ +public final class MechtitanToken extends TokenImpl { + + public MechtitanToken() { + super("Mechtitan", "Mechtitan, a legendary 10/10 Construct artifact creature token with flying, vigilance, trample, lifelink, and haste that's all colors"); + addSuperType(SuperType.LEGENDARY); + cardType.add(CardType.ARTIFACT); + cardType.add(CardType.CREATURE); + subtype.add(SubType.CONSTRUCT); + color.setWhite(true); + color.setBlue(true); + color.setBlack(true); + color.setRed(true); + color.setGreen(true); + power = new MageInt(10); + toughness = new MageInt(10); + addAbility(FlyingAbility.getInstance()); + addAbility(VigilanceAbility.getInstance()); + addAbility(TrampleAbility.getInstance()); + addAbility(LifelinkAbility.getInstance()); + addAbility(HasteAbility.getInstance()); + } + + public MechtitanToken(final MechtitanToken token) { + super(token); + } + + public MechtitanToken copy() { + return new MechtitanToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/MerfolkHexproofToken.java b/Mage/src/main/java/mage/game/permanent/token/MerfolkHexproofToken.java index debaf39b8a9..500cfc2345b 100644 --- a/Mage/src/main/java/mage/game/permanent/token/MerfolkHexproofToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/MerfolkHexproofToken.java @@ -17,7 +17,7 @@ public final class MerfolkHexproofToken extends TokenImpl { super("Merfolk", "1/1 blue Merfolk creature token with hexproof"); this.cardType.add(CardType.CREATURE); this.subtype.add(SubType.MERFOLK); - this.color = ObjectColor.BLUE; + this.color.setBlue(true); this.power = new MageInt(1); this.toughness = new MageInt(1); this.addAbility(HexproofAbility.getInstance()); @@ -30,5 +30,4 @@ public final class MerfolkHexproofToken extends TokenImpl { public MerfolkHexproofToken copy() { return new MerfolkHexproofToken(this); } - -} \ No newline at end of file +} diff --git a/Mage/src/main/java/mage/game/permanent/token/MinnWilyIllusionistToken.java b/Mage/src/main/java/mage/game/permanent/token/MinnWilyIllusionistToken.java index 9f19f8ddd11..26bf97900fd 100644 --- a/Mage/src/main/java/mage/game/permanent/token/MinnWilyIllusionistToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/MinnWilyIllusionistToken.java @@ -13,6 +13,8 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.mageobject.AnotherPredicate; +import java.util.Arrays; + /** * @author TheElk801 */ @@ -36,6 +38,8 @@ public final class MinnWilyIllusionistToken extends TokenImpl { addAbility(new SimpleStaticAbility(new BoostSourceEffect( xValue, StaticValue.get(0), Duration.WhileOnBattlefield ).setText("this creature gets +1/+0 for each other Illusion you control"))); + + availableImageSetCodes = Arrays.asList("AFC"); } public MinnWilyIllusionistToken(final MinnWilyIllusionistToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/MoltenBirthElementalToken.java b/Mage/src/main/java/mage/game/permanent/token/MoltenBirthElementalToken.java deleted file mode 100644 index ee7e3794532..00000000000 --- a/Mage/src/main/java/mage/game/permanent/token/MoltenBirthElementalToken.java +++ /dev/null @@ -1,33 +0,0 @@ - -package mage.game.permanent.token; - -import mage.MageInt; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.util.RandomUtil; - -/** - * - * @author spjspj - */ -public final class MoltenBirthElementalToken extends TokenImpl { - - public MoltenBirthElementalToken() { - super("Elemental", "1/1 red Elemental creature"); - this.setOriginalExpansionSetCode("M14"); - this.setTokenType(RandomUtil.nextInt(2) + 1); - cardType.add(CardType.CREATURE); - color.setRed(true); - subtype.add(SubType.ELEMENTAL); - power = new MageInt(1); - toughness = new MageInt(1); - } - - public MoltenBirthElementalToken(final MoltenBirthElementalToken token) { - super(token); - } - - public MoltenBirthElementalToken copy() { - return new MoltenBirthElementalToken(this); - } -} diff --git a/Mage/src/main/java/mage/game/permanent/token/NinjaToken.java b/Mage/src/main/java/mage/game/permanent/token/NinjaToken.java new file mode 100644 index 00000000000..c877036d55e --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/NinjaToken.java @@ -0,0 +1,35 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.combat.CantBeBlockedSourceEffect; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.Arrays; + +/** + * @author TheElk801 + */ +public final class NinjaToken extends TokenImpl { + + public NinjaToken() { + super("Ninja Token", "1/1 blue Ninja creature token with \"This creature can't be blocked.\""); + cardType.add(CardType.CREATURE); + color.setBlue(true); + subtype.add(SubType.NINJA); + power = new MageInt(1); + toughness = new MageInt(1); + + this.addAbility(new SimpleStaticAbility(new CantBeBlockedSourceEffect().setText("this creature can't be blocked"))); + availableImageSetCodes = Arrays.asList("NEO"); + } + + private NinjaToken(final NinjaToken token) { + super(token); + } + + public NinjaToken copy() { + return new NinjaToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/OminousRoostToken.java b/Mage/src/main/java/mage/game/permanent/token/OminousRoostBirdToken.java similarity index 71% rename from Mage/src/main/java/mage/game/permanent/token/OminousRoostToken.java rename to Mage/src/main/java/mage/game/permanent/token/OminousRoostBirdToken.java index 17e645c212b..9a4579b7caa 100644 --- a/Mage/src/main/java/mage/game/permanent/token/OminousRoostToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/OminousRoostBirdToken.java @@ -6,9 +6,11 @@ import mage.abilities.keyword.FlyingAbility; import mage.constants.CardType; import mage.constants.SubType; -public class OminousRoostToken extends TokenImpl { +import java.util.Arrays; - public OminousRoostToken() { +public class OminousRoostBirdToken extends TokenImpl { + + public OminousRoostBirdToken() { super("Bird", "1/1 blue Bird creature token with flying and \"This creature can block only creatures with flying\""); cardType.add(CardType.CREATURE); color.setBlue(true); @@ -18,14 +20,16 @@ public class OminousRoostToken extends TokenImpl { this.addAbility(FlyingAbility.getInstance()); this.addAbility(new CanBlockOnlyFlyingAbility()); + + availableImageSetCodes = Arrays.asList("MID"); } - public OminousRoostToken(final OminousRoostToken token) { + public OminousRoostBirdToken(final OminousRoostBirdToken token) { super(token); } @Override public Token copy() { - return new OminousRoostToken(this); + return new OminousRoostBirdToken(this); } } diff --git a/Mage/src/main/java/mage/game/permanent/token/OviyaPashiriSageLifecrafterToken.java b/Mage/src/main/java/mage/game/permanent/token/OviyaPashiriSageLifecrafterToken.java index 3ab89538293..282cf8d8e0e 100644 --- a/Mage/src/main/java/mage/game/permanent/token/OviyaPashiriSageLifecrafterToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/OviyaPashiriSageLifecrafterToken.java @@ -3,7 +3,6 @@ package mage.game.permanent.token; import mage.MageInt; import mage.constants.CardType; import mage.constants.SubType; -import mage.filter.common.FilterControlledCreaturePermanent; import java.util.Arrays; @@ -12,8 +11,6 @@ import java.util.Arrays; */ public final class OviyaPashiriSageLifecrafterToken extends TokenImpl { - static final FilterControlledCreaturePermanent filterCreature = new FilterControlledCreaturePermanent("creatures you control"); - public OviyaPashiriSageLifecrafterToken() { this(1); } diff --git a/Mage/src/main/java/mage/game/permanent/token/PhyrexianRebirthHorrorToken.java b/Mage/src/main/java/mage/game/permanent/token/PhyrexianRebirthHorrorToken.java index aaa4c51e60d..fe8c9bb6d17 100644 --- a/Mage/src/main/java/mage/game/permanent/token/PhyrexianRebirthHorrorToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/PhyrexianRebirthHorrorToken.java @@ -11,6 +11,10 @@ import java.util.Arrays; */ public final class PhyrexianRebirthHorrorToken extends TokenImpl { + public PhyrexianRebirthHorrorToken() { + this(0, 0); + } + public PhyrexianRebirthHorrorToken(int power, int toughness) { super("Phyrexian Horror", "X/X colorless Phyrexian Horror artifact creature token"); this.cardType.add(CardType.ARTIFACT); diff --git a/Mage/src/main/java/mage/game/permanent/token/PilotToken.java b/Mage/src/main/java/mage/game/permanent/token/PilotToken.java new file mode 100644 index 00000000000..2f921839c8b --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/PilotToken.java @@ -0,0 +1,29 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.abilities.common.CrewIncreasedPowerAbility; +import mage.constants.CardType; +import mage.constants.SubType; + +/** + * @author TheElk801 + */ +public final class PilotToken extends TokenImpl { + + public PilotToken() { + super("Pilot token", "1/1 colorless Pilot creature token with \"This creature crews Vehicles as though its power were 2 greater.\""); + cardType.add(CardType.CREATURE); + subtype.add(SubType.PILOT); + power = new MageInt(1); + toughness = new MageInt(1); + addAbility(new CrewIncreasedPowerAbility("this creature")); + } + + public PilotToken(final PilotToken token) { + super(token); + } + + public PilotToken copy() { + return new PilotToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/RatRogueToken.java b/Mage/src/main/java/mage/game/permanent/token/RatRogueToken.java new file mode 100644 index 00000000000..38859d9970f --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/RatRogueToken.java @@ -0,0 +1,31 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.constants.CardType; +import mage.constants.SubType; + +/** + * + * @author weirddan455 + */ +public class RatRogueToken extends TokenImpl { + + public RatRogueToken() { + super("Rat Rogue", "1/1 black Rat Rogue creature token"); + cardType.add(CardType.CREATURE); + color.setBlack(true); + subtype.add(SubType.RAT); + subtype.add(SubType.ROGUE); + power = new MageInt(1); + toughness = new MageInt(1); + } + + private RatRogueToken(final RatRogueToken token) { + super(token); + } + + @Override + public RatRogueToken copy() { + return new RatRogueToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/RatToken.java b/Mage/src/main/java/mage/game/permanent/token/RatToken.java index 460967726db..2a2c9dd1f77 100644 --- a/Mage/src/main/java/mage/game/permanent/token/RatToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/RatToken.java @@ -19,7 +19,7 @@ public final class RatToken extends TokenImpl { power = new MageInt(1); toughness = new MageInt(1); - availableImageSetCodes = Arrays.asList("C17", "CHK", "GTC", "SHM", "ELD", "ZNC"); + availableImageSetCodes = Arrays.asList("C17", "CHK", "GTC", "SHM", "ELD", "ZNC", "AFC"); } public RatToken(final RatToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/RedHumanToken.java b/Mage/src/main/java/mage/game/permanent/token/RedHumanToken.java index 6c72f06f392..ef7f4c58daf 100644 --- a/Mage/src/main/java/mage/game/permanent/token/RedHumanToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/RedHumanToken.java @@ -6,6 +6,8 @@ import mage.constants.SubType; import mage.MageInt; import mage.ObjectColor; +import java.util.Arrays; + /** * * @author spjspj @@ -17,9 +19,20 @@ public final class RedHumanToken extends TokenImpl { this.cardType.add(CardType.CREATURE); this.subtype.add(SubType.HUMAN); - this.color = ObjectColor.RED; + this.color.setRed(true); this.power = new MageInt(1); this.toughness = new MageInt(1); + + availableImageSetCodes = Arrays.asList("AVR", "EMN", "VOW"); + } + + @Override + public void setExpansionSetCodeForImage(String code) { + super.setExpansionSetCodeForImage(code); + + if (getOriginalExpansionSetCode().equals("AVR")) { + this.setTokenType(2); + } } public RedHumanToken(final RedHumanToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/RedWolfToken.java b/Mage/src/main/java/mage/game/permanent/token/RedWolfToken.java new file mode 100644 index 00000000000..fe49cf801c0 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/RedWolfToken.java @@ -0,0 +1,43 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.Arrays; + +/** + * @author TheElk801 + */ +public final class RedWolfToken extends TokenImpl { + + public RedWolfToken() { + super("Wolf", "3/2 red Wolf creature token"); + + cardType.add(CardType.CREATURE); + color.setRed(true); + subtype.add(SubType.WOLF); + power = new MageInt(3); + toughness = new MageInt(2); + + availableImageSetCodes = Arrays.asList("VOW"); + } + + @Override + public void setExpansionSetCodeForImage(String code) { + super.setExpansionSetCodeForImage(code); + + if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("VOW")) { + setTokenType(2); + } + } + + private RedWolfToken(final RedWolfToken token) { + super(token); + } + + @Override + public RedWolfToken copy() { + return new RedWolfToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/RhinoToken.java b/Mage/src/main/java/mage/game/permanent/token/RhinoToken.java index 3c08fd7f262..259f3e846f8 100644 --- a/Mage/src/main/java/mage/game/permanent/token/RhinoToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/RhinoToken.java @@ -21,7 +21,7 @@ public final class RhinoToken extends TokenImpl { toughness = new MageInt(4); addAbility(TrampleAbility.getInstance()); - availableImageSetCodes.addAll(Arrays.asList("DGM", "RTR", "MH1", "C19")); + availableImageSetCodes = Arrays.asList("DGM", "RTR", "MH1", "C19", "MIC"); } public RhinoToken(final RhinoToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/RiseOfTheAntsToken.java b/Mage/src/main/java/mage/game/permanent/token/RiseOfTheAntsInsectToken.java similarity index 54% rename from Mage/src/main/java/mage/game/permanent/token/RiseOfTheAntsToken.java rename to Mage/src/main/java/mage/game/permanent/token/RiseOfTheAntsInsectToken.java index 81167ac5669..2a20ab1ee5e 100644 --- a/Mage/src/main/java/mage/game/permanent/token/RiseOfTheAntsToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/RiseOfTheAntsInsectToken.java @@ -4,25 +4,29 @@ import mage.MageInt; import mage.constants.CardType; import mage.constants.SubType; +import java.util.Arrays; + /** * @author TheElk801 */ -public final class RiseOfTheAntsToken extends TokenImpl { +public final class RiseOfTheAntsInsectToken extends TokenImpl { - public RiseOfTheAntsToken() { + public RiseOfTheAntsInsectToken() { super("Insect", "3/3 green Insect creature token"); cardType.add(CardType.CREATURE); color.setGreen(true); subtype.add(SubType.INSECT); power = new MageInt(3); toughness = new MageInt(3); + + availableImageSetCodes.addAll(Arrays.asList("MID")); } - public RiseOfTheAntsToken(final RiseOfTheAntsToken token) { + public RiseOfTheAntsInsectToken(final RiseOfTheAntsInsectToken token) { super(token); } - public RiseOfTheAntsToken copy() { - return new RiseOfTheAntsToken(this); + public RiseOfTheAntsInsectToken copy() { + return new RiseOfTheAntsInsectToken(this); } } \ No newline at end of file diff --git a/Mage/src/main/java/mage/game/permanent/token/SamuraiToken.java b/Mage/src/main/java/mage/game/permanent/token/SamuraiToken.java new file mode 100644 index 00000000000..729c2e432c5 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/SamuraiToken.java @@ -0,0 +1,31 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.abilities.keyword.VigilanceAbility; +import mage.constants.CardType; +import mage.constants.SubType; + +/** + * @author TheElk801 + */ +public class SamuraiToken extends TokenImpl { + + public SamuraiToken() { + super("Samurai token", "2/2 white Samurai creature token with vigilance."); + cardType.add(CardType.CREATURE); + color.setWhite(true); + subtype.add(SubType.SAMURAI); + power = new MageInt(2); + toughness = new MageInt(2); + addAbility(VigilanceAbility.getInstance()); + } + + private SamuraiToken(final SamuraiToken token) { + super(token); + } + + @Override + public SamuraiToken copy() { + return new SamuraiToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/SaprolingToken.java b/Mage/src/main/java/mage/game/permanent/token/SaprolingToken.java index 3f594b22ae8..80c2054d68e 100644 --- a/Mage/src/main/java/mage/game/permanent/token/SaprolingToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/SaprolingToken.java @@ -46,7 +46,8 @@ public final class SaprolingToken extends TokenImpl { "ZNC", "CMR", "TSR", - "C21" + "C21", + "AFC" )); } diff --git a/Mage/src/main/java/mage/game/permanent/token/SeizeTheStormToken.java b/Mage/src/main/java/mage/game/permanent/token/SeizeTheStormElementalToken.java similarity index 69% rename from Mage/src/main/java/mage/game/permanent/token/SeizeTheStormToken.java rename to Mage/src/main/java/mage/game/permanent/token/SeizeTheStormElementalToken.java index fdcc76e93c8..fa33fe5146a 100644 --- a/Mage/src/main/java/mage/game/permanent/token/SeizeTheStormToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/SeizeTheStormElementalToken.java @@ -3,19 +3,27 @@ package mage.game.permanent.token; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.common.continuous.SetPowerToughnessSourceEffect; import mage.abilities.hint.Hint; +import mage.abilities.hint.StaticHint; import mage.abilities.keyword.TrampleAbility; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; +import java.util.Arrays; + /** * @author TheElk801 */ -public final class SeizeTheStormToken extends TokenImpl { +public final class SeizeTheStormElementalToken extends TokenImpl { - public SeizeTheStormToken(DynamicValue xValue, Hint hint) { + public SeizeTheStormElementalToken() { + this(StaticValue.get(0), new StaticHint("")); + } + + public SeizeTheStormElementalToken(DynamicValue xValue, Hint hint) { super("Elemental", "red Elemental creature token with trample and " + "\"This creature's power and toughness are each equal to the number of instant " + "and sorcery cards in your graveyard plus the number of cards with flashback you own in exile.\""); @@ -30,14 +38,16 @@ public final class SeizeTheStormToken extends TokenImpl { ).setText("this creature's power and toughness are each equal to the number of " + "instant and sorcery cards in your graveyard, plus the number of cards with flashback you own in exile") ).addHint(hint)); + + availableImageSetCodes = Arrays.asList("MID"); } - private SeizeTheStormToken(final SeizeTheStormToken token) { + private SeizeTheStormElementalToken(final SeizeTheStormElementalToken token) { super(token); } @Override - public SeizeTheStormToken copy() { - return new SeizeTheStormToken(this); + public SeizeTheStormElementalToken copy() { + return new SeizeTheStormElementalToken(this); } } diff --git a/Mage/src/main/java/mage/game/permanent/token/ServoToken.java b/Mage/src/main/java/mage/game/permanent/token/ServoToken.java index 358ed980fe8..6720a24f2c3 100644 --- a/Mage/src/main/java/mage/game/permanent/token/ServoToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/ServoToken.java @@ -20,7 +20,7 @@ public final class ServoToken extends TokenImpl { power = new MageInt(1); toughness = new MageInt(1); - availableImageSetCodes = Arrays.asList("C18", "KLD", "WAR", "KHC"); + availableImageSetCodes = Arrays.asList("C18", "KLD", "WAR", "KHC", "AFC"); } @Override diff --git a/Mage/src/main/java/mage/game/permanent/token/ShrineToken.java b/Mage/src/main/java/mage/game/permanent/token/ShrineToken.java new file mode 100644 index 00000000000..a5ac7e2e533 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/ShrineToken.java @@ -0,0 +1,33 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.Arrays; + +/** + * @author TheElk801 + */ +public final class ShrineToken extends TokenImpl { + + public ShrineToken() { + super("Spirit", "1/1 colorless Shrine enchantment creature token"); + cardType.add(CardType.ENCHANTMENT); + cardType.add(CardType.CREATURE); + subtype.add(SubType.SHRINE); + power = new MageInt(1); + toughness = new MageInt(1); + + availableImageSetCodes = Arrays.asList("NEC"); + } + + public ShrineToken(final ShrineToken token) { + super(token); + } + + @Override + public ShrineToken copy() { + return new ShrineToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/SlugToken.java b/Mage/src/main/java/mage/game/permanent/token/SlugToken.java new file mode 100644 index 00000000000..941509cc13d --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/SlugToken.java @@ -0,0 +1,32 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.Arrays; + +/** + * @author TheElk801 + */ +public final class SlugToken extends TokenImpl { + + public SlugToken() { + super("Slug", "1/1 black Slug creature token"); + cardType.add(CardType.CREATURE); + color.setBlack(true); + subtype.add(SubType.SLUG); + power = new MageInt(1); + toughness = new MageInt(1); + + availableImageSetCodes = Arrays.asList("VOW"); + } + + public SlugToken(final SlugToken token) { + super(token); + } + + public SlugToken copy() { + return new SlugToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/SmokeBlessingToken.java b/Mage/src/main/java/mage/game/permanent/token/SmokeBlessingToken.java new file mode 100644 index 00000000000..d792076f5c8 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/SmokeBlessingToken.java @@ -0,0 +1,77 @@ +package mage.game.permanent.token; + +import mage.abilities.Ability; +import mage.abilities.common.DiesAttachedTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; + +/** + * @author TheElk801 + */ +public final class SmokeBlessingToken extends TokenImpl { + + public SmokeBlessingToken() { + super( + "Smoke Blessing", "red Aura enchantment token named Smoke Blessing " + + "attached to that creature. Those tokens have enchant creature and " + + "\"When enchanted creature dies, it deals 1 damage to its controller and you create a Treasure token.\"" + ); + cardType.add(CardType.ENCHANTMENT); + color.setRed(true); + subtype.add(SubType.AURA); + + TargetPermanent auraTarget = new TargetPermanent(); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + ability.addTarget(auraTarget); + ability.addEffect(new AttachEffect(Outcome.BoostCreature)); + this.addAbility(ability); + + this.addAbility(new DiesAttachedTriggeredAbility(new SmokeBlessingTokenEffect(), "enchanted creature")); + } + + public SmokeBlessingToken(final SmokeBlessingToken token) { + super(token); + } + + public SmokeBlessingToken copy() { + return new SmokeBlessingToken(this); + } +} + +class SmokeBlessingTokenEffect extends OneShotEffect { + + SmokeBlessingTokenEffect() { + super(Outcome.Benefit); + staticText = "it deals 1 damage to its controller and you create a Treasure token"; + } + + private SmokeBlessingTokenEffect(final SmokeBlessingTokenEffect effect) { + super(effect); + } + + @Override + public SmokeBlessingTokenEffect copy() { + return new SmokeBlessingTokenEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = (Permanent) getValue("attachedTo"); + if (permanent != null) { + Player player = game.getPlayer(permanent.getControllerId()); + if (player != null) { + player.damage(1, permanent.getId(), source, game); + } + } + new TreasureToken().putOntoBattlefield(1, game, source); + return true; + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/SnakeToken.java b/Mage/src/main/java/mage/game/permanent/token/SnakeToken.java index 2b7069db4e4..20b5308cf11 100644 --- a/Mage/src/main/java/mage/game/permanent/token/SnakeToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/SnakeToken.java @@ -4,37 +4,23 @@ import mage.MageInt; import mage.constants.CardType; import mage.constants.SubType; -import java.util.ArrayList; import java.util.Arrays; -import java.util.List; /** * @author BetaSteward_at_googlemail.com */ public final class SnakeToken extends TokenImpl { - static final private List tokenImageSets = new ArrayList<>(); - - static { - tokenImageSets.addAll(Arrays.asList("ZEN", "KTK", "MM2", "C15", "C19")); - } - public SnakeToken() { - this((String) null); - } - - public SnakeToken(String setCode) { super("Snake", "1/1 green Snake creature token"); - availableImageSetCodes = tokenImageSets; - setOriginalExpansionSetCode(setCode); - if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("C15")) { - setTokenType(1); - } cardType.add(CardType.CREATURE); color.setGreen(true); subtype.add(SubType.SNAKE); power = new MageInt(1); toughness = new MageInt(1); + + availableImageSetCodes = Arrays.asList("6ED", "BOK", "C15", "C19", "CHK", "CMD", "KTK", "MM2", "MMQ", "SOK", + "VIS", "WWK", "ZEN", "C20", "MIC"); } public SnakeToken(final SnakeToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/SpiderToken.java b/Mage/src/main/java/mage/game/permanent/token/SpiderToken.java index 6c5dd0e72fb..eab04a5ea3f 100644 --- a/Mage/src/main/java/mage/game/permanent/token/SpiderToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/SpiderToken.java @@ -5,39 +5,24 @@ import mage.abilities.keyword.ReachAbility; import mage.constants.CardType; import mage.constants.SubType; -import java.util.ArrayList; import java.util.Arrays; -import java.util.List; /** * @author fireshoes */ public final class SpiderToken extends TokenImpl { - static final private List tokenImageSets = new ArrayList<>(); - - static { - tokenImageSets.addAll(Arrays.asList("ISD", "EMN", "C15", "SHM", "MH1", "THB")); - } - public SpiderToken() { - this(null, 0); - } - - public SpiderToken(String setCode) { - this(setCode, 0); - } - - public SpiderToken(String setCode, int tokenType) { super("Spider", "1/2 green Spider creature token with reach"); - availableImageSetCodes = tokenImageSets; - setOriginalExpansionSetCode(setCode); cardType.add(CardType.CREATURE); color.setGreen(true); subtype.add(SubType.SPIDER); power = new MageInt(1); toughness = new MageInt(2); - addAbility(ReachAbility.getInstance()); + + this.addAbility(ReachAbility.getInstance()); + + availableImageSetCodes.addAll(Arrays.asList("C15", "EMN", "ISD", "SHM", "MH1", "THB", "MID")); } public SpiderToken(final SpiderToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/SpiritClericToken.java b/Mage/src/main/java/mage/game/permanent/token/SpiritClericToken.java new file mode 100644 index 00000000000..0f69fd243c9 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/SpiritClericToken.java @@ -0,0 +1,77 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.continuous.SetPowerToughnessSourceEffect; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.game.Game; +import mage.game.permanent.Permanent; + +import java.util.Arrays; + +/** + * + * @author weirddan455 + */ +public class SpiritClericToken extends TokenImpl { + + public SpiritClericToken() { + super("Spirit Cleric", "white Spirit Cleric creature token with \"This creature's power and toughness are each equal to the number of Spirits you control.\""); + cardType.add(CardType.CREATURE); + subtype.add(SubType.SPIRIT); + subtype.add(SubType.CLERIC); + color.setWhite(true); + + power = new MageInt(0); + toughness = new MageInt(0); + + // This creature’s power and toughness are each equal to the number of Spirits you control. + this.addAbility(new SimpleStaticAbility(new SetPowerToughnessSourceEffect(SpiritClericTokenValue.instance, Duration.EndOfGame))); + + availableImageSetCodes = Arrays.asList("VOW"); + } + + private SpiritClericToken(final SpiritClericToken token) { + super(token); + } + + @Override + public SpiritClericToken copy() { + return new SpiritClericToken(this); + } +} + +enum SpiritClericTokenValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + int spirits = 0; + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(sourceAbility.getControllerId())) { + if (permanent.hasSubtype(SubType.SPIRIT, game)) { + spirits++; + } + } + return spirits; + } + + @Override + public SpiritClericTokenValue copy() { + return instance; + } + + @Override + public String toString() { + return "X"; + } + + @Override + public String getMessage() { + return "Spirits you control"; + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/SpiritGreenToken.java b/Mage/src/main/java/mage/game/permanent/token/SpiritGreenToken.java new file mode 100644 index 00000000000..25caf91f278 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/SpiritGreenToken.java @@ -0,0 +1,28 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.constants.CardType; +import mage.constants.SubType; + +/** + * @author TheElk801 + */ +public final class SpiritGreenToken extends TokenImpl { + + public SpiritGreenToken() { + super("Spirit token", "4/5 green Spirit creature token"); + cardType.add(CardType.CREATURE); + subtype.add(SubType.SPIRIT); + color.setGreen(true); + power = new MageInt(4); + toughness = new MageInt(5); + } + + public SpiritGreenToken(final SpiritGreenToken token) { + super(token); + } + + public SpiritGreenToken copy() { + return new SpiritGreenToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/SpiritGreenXToken.java b/Mage/src/main/java/mage/game/permanent/token/SpiritGreenXToken.java new file mode 100644 index 00000000000..e329aa24ee0 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/SpiritGreenXToken.java @@ -0,0 +1,28 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.constants.CardType; +import mage.constants.SubType; + +/** + * @author TheElk801 + */ +public final class SpiritGreenXToken extends TokenImpl { + + public SpiritGreenXToken(int xValue) { + super("Spirit token", "X/X green Spirit creature token"); + cardType.add(CardType.CREATURE); + subtype.add(SubType.SPIRIT); + color.setGreen(true); + power = new MageInt(xValue); + toughness = new MageInt(xValue); + } + + public SpiritGreenXToken(final SpiritGreenXToken token) { + super(token); + } + + public SpiritGreenXToken copy() { + return new SpiritGreenXToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/SpiritRedToken.java b/Mage/src/main/java/mage/game/permanent/token/SpiritRedToken.java new file mode 100644 index 00000000000..004ced270ca --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/SpiritRedToken.java @@ -0,0 +1,30 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.abilities.keyword.MenaceAbility; +import mage.constants.CardType; +import mage.constants.SubType; + +/** + * @author TheElk801 + */ +public final class SpiritRedToken extends TokenImpl { + + public SpiritRedToken() { + super("Spirit token", "2/2 red Spirit creature token with menace"); + cardType.add(CardType.CREATURE); + subtype.add(SubType.SPIRIT); + color.setRed(true); + power = new MageInt(2); + toughness = new MageInt(2); + addAbility(new MenaceAbility()); + } + + public SpiritRedToken(final SpiritRedToken token) { + super(token); + } + + public SpiritRedToken copy() { + return new SpiritRedToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/SpiritToken.java b/Mage/src/main/java/mage/game/permanent/token/SpiritToken.java index 7f64cfe9208..fe315474b55 100644 --- a/Mage/src/main/java/mage/game/permanent/token/SpiritToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/SpiritToken.java @@ -4,50 +4,33 @@ import mage.MageInt; import mage.constants.CardType; import mage.constants.SubType; -import java.util.ArrayList; import java.util.Arrays; -import java.util.List; /** * @author Loki */ public final class SpiritToken extends TokenImpl { - static final private List tokenImageSets = new ArrayList<>(); - - static { - tokenImageSets.addAll(Arrays.asList("CHK", "EMA", "C16", "C19")); - } - public SpiritToken() { - this(null, 0); - } - - public SpiritToken(String setCode) { - this(setCode, 0); - } - - public SpiritToken(String setCode, int tokenType) { super("Spirit", "1/1 colorless Spirit creature token"); - availableImageSetCodes = tokenImageSets; - setOriginalExpansionSetCode(setCode); - if (tokenType > 0) { - setTokenType(tokenType); - } cardType.add(CardType.CREATURE); subtype.add(SubType.SPIRIT); power = new MageInt(1); toughness = new MageInt(1); + + availableImageSetCodes = Arrays.asList("C19", "CHK", "EMA", "EXP", "SOK", "V12", "VOC"); } @Override public void setExpansionSetCodeForImage(String code) { super.setExpansionSetCodeForImage(code); - if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("EMA")) { - setTokenType(1); + + if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("BOK")) { + setTokenType(2); } - if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("C16")) { - setTokenType(1); + + if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("EMA")) { + setTokenType(2); } } diff --git a/Mage/src/main/java/mage/game/permanent/token/SpiritWhiteToken.java b/Mage/src/main/java/mage/game/permanent/token/SpiritWhiteToken.java index ece95a952f7..c2d05a2f959 100644 --- a/Mage/src/main/java/mage/game/permanent/token/SpiritWhiteToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/SpiritWhiteToken.java @@ -23,7 +23,8 @@ public final class SpiritWhiteToken extends TokenImpl { addAbility(FlyingAbility.getInstance()); availableImageSetCodes = Arrays.asList("AVR", "C14", "CNS", "DDC", "DDK", "FRF", "ISD", "KTK", "M15", "MM2", "SHM", - "SOI", "EMA", "C16", "MM3", "CMA", "E01", "ANA", "GPT", "RAV", "EMN", "RNA", "M20", "C20", "CMR", "KHM"); + "SOI", "EMA", "C16", "MM3", "CMA", "E01", "ANA", "GPT", "RAV", "EMN", "RNA", "M20", "C20", "CMR", "KHM", + "MID", "VOW"); } @Override diff --git a/Mage/src/main/java/mage/game/permanent/token/StitcherGeralfZombieToken.java b/Mage/src/main/java/mage/game/permanent/token/StitcherGeralfZombieToken.java index bff1d69f124..5b36fa03997 100644 --- a/Mage/src/main/java/mage/game/permanent/token/StitcherGeralfZombieToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/StitcherGeralfZombieToken.java @@ -1,12 +1,12 @@ - - package mage.game.permanent.token; + +import mage.MageInt; import mage.constants.CardType; import mage.constants.SubType; -import mage.MageInt; + +import java.util.Arrays; /** - * * @author spjspj */ public final class StitcherGeralfZombieToken extends TokenImpl { @@ -14,15 +14,33 @@ public final class StitcherGeralfZombieToken extends TokenImpl { public StitcherGeralfZombieToken() { this(1); } + public StitcherGeralfZombieToken(int xValue) { super("Zombie", "X/X blue Zombie creature token"); - setOriginalExpansionSetCode("C14"); - setTokenType(1); cardType.add(CardType.CREATURE); color.setBlue(true); subtype.add(SubType.ZOMBIE); power = new MageInt(xValue); toughness = new MageInt(xValue); + + availableImageSetCodes = Arrays.asList("C14", "MIC", "VOW"); + } + + @Override + public void setExpansionSetCodeForImage(String code) { + super.setExpansionSetCodeForImage(code); + + if (getOriginalExpansionSetCode().equals("C14")) { + this.setTokenType(2); + } + + if (getOriginalExpansionSetCode().equals("MIC")) { + this.setTokenType(2); + } + + if (getOriginalExpansionSetCode().equals("VOW")) { + this.setTokenType(2); + } } public StitcherGeralfZombieToken(final StitcherGeralfZombieToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/TamiyosNotebookToken.java b/Mage/src/main/java/mage/game/permanent/token/TamiyosNotebookToken.java new file mode 100644 index 00000000000..62320faa451 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/TamiyosNotebookToken.java @@ -0,0 +1,34 @@ +package mage.game.permanent.token; + +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; +import mage.constants.CardType; +import mage.constants.SuperType; +import mage.filter.FilterCard; + +/** + * @author TheElk801 + */ +public final class TamiyosNotebookToken extends TokenImpl { + + private static final FilterCard filter = new FilterCard("spells"); + + public TamiyosNotebookToken() { + super("Tamiyo's Notebook", "Tamiyo's Notebook, a legendary colorless artifact token with \"Spells you cast cost {2} less to cast\" and \"{T}: Draw a card.\""); + addSuperType(SuperType.LEGENDARY); + this.cardType.add(CardType.ARTIFACT); + this.addAbility(new SimpleStaticAbility(new SpellsCostReductionControllerEffect(filter, 2))); + this.addAbility(new SimpleActivatedAbility(new DrawCardSourceControllerEffect(1), new TapSourceCost())); + } + + public TamiyosNotebookToken(final TamiyosNotebookToken token) { + super(token); + } + + public TamiyosNotebookToken copy() { + return new TamiyosNotebookToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/ThatcherHumanToken.java b/Mage/src/main/java/mage/game/permanent/token/ThatcherHumanToken.java index 45456e9b3ab..dfa8d843bff 100644 --- a/Mage/src/main/java/mage/game/permanent/token/ThatcherHumanToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/ThatcherHumanToken.java @@ -19,7 +19,7 @@ public final class ThatcherHumanToken extends TokenImpl { this.subtype.add(SubType.HUMAN); addAbility(HasteAbility.getInstance()); - this.color = ObjectColor.RED; + this.color.setRed(true); this.power = new MageInt(1); this.toughness = new MageInt(1); } diff --git a/Mage/src/main/java/mage/game/permanent/token/ThopterColorlessToken.java b/Mage/src/main/java/mage/game/permanent/token/ThopterColorlessToken.java index d20d1282dc8..db8a0b754a3 100644 --- a/Mage/src/main/java/mage/game/permanent/token/ThopterColorlessToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/ThopterColorlessToken.java @@ -24,7 +24,7 @@ public final class ThopterColorlessToken extends TokenImpl { addAbility(FlyingAbility.getInstance()); availableImageSetCodes = Arrays.asList("C18", "EXO", "KLD", "MBS", "ORI", "VMA", "M19", "ZNC", - "KHC", "C21", "MH2"); + "KHC", "C21", "MH2", "AFC", "VOC"); } @Override diff --git a/Mage/src/main/java/mage/game/permanent/token/TitaniaProtectorOfArgothElementalToken.java b/Mage/src/main/java/mage/game/permanent/token/TitaniaProtectorOfArgothElementalToken.java index 846b80acb27..39b7c418008 100644 --- a/Mage/src/main/java/mage/game/permanent/token/TitaniaProtectorOfArgothElementalToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/TitaniaProtectorOfArgothElementalToken.java @@ -15,7 +15,7 @@ public final class TitaniaProtectorOfArgothElementalToken extends TokenImpl { public TitaniaProtectorOfArgothElementalToken() { super("Elemental", "5/3 green Elemental creature token"); this.cardType.add(CardType.CREATURE); - this.color = ObjectColor.GREEN; + this.color.setGreen(true); this.subtype.add(SubType.ELEMENTAL); this.power = new MageInt(5); this.toughness = new MageInt(3); diff --git a/Mage/src/main/java/mage/game/permanent/token/Token.java b/Mage/src/main/java/mage/game/permanent/token/Token.java index dde696755f3..02c5b36bc51 100644 --- a/Mage/src/main/java/mage/game/permanent/token/Token.java +++ b/Mage/src/main/java/mage/game/permanent/token/Token.java @@ -27,6 +27,12 @@ public interface Token extends MageObject { void addAbility(Ability ability); + void removeAbility(Ability abilityToRemove); + + void removeAbilities(List abilitiesToRemove); + + boolean putOntoBattlefield(int amount, Game game, Ability source); + boolean putOntoBattlefield(int amount, Game game, Ability source, UUID controllerId); boolean putOntoBattlefield(int amount, Game game, Ability source, UUID controllerId, boolean tapped, boolean attacking); diff --git a/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java b/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java index fd73338f47b..a323b1af453 100644 --- a/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java +++ b/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java @@ -3,7 +3,13 @@ package mage.game.permanent.token; import mage.MageObject; import mage.MageObjectImpl; import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.keyword.EnchantAbility; import mage.cards.Card; +import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; import mage.game.events.CreateTokenEvent; @@ -12,18 +18,14 @@ import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; import mage.game.permanent.PermanentToken; import mage.players.Player; +import mage.target.Target; import mage.util.RandomUtil; import java.util.*; -import mage.abilities.SpellAbility; -import mage.abilities.effects.Effect; -import mage.abilities.effects.common.AttachEffect; -import mage.abilities.keyword.EnchantAbility; -import mage.constants.Outcome; -import mage.constants.SubType; -import mage.target.Target; - +/** + * Each token must have default constructor without params (GUI require for card viewer) + */ public abstract class TokenImpl extends MageObjectImpl implements Token { protected String description; @@ -125,6 +127,41 @@ public abstract class TokenImpl extends MageObjectImpl implements Token { public void addAbility(Ability ability) { ability.setSourceId(this.getId()); abilities.add(ability); + abilities.addAll(ability.getSubAbilities()); + } + + // Directly from PermanentImpl + @Override + public void removeAbility(Ability abilityToRemove) { + if (abilityToRemove == null) { + return; + } + + // 112.10b Effects that remove an ability remove all instances of it. + List toRemove = new ArrayList<>(); + abilities.forEach(a -> { + if (a.isSameInstance(abilityToRemove)) { + toRemove.add(a); + } + }); + + // TODO: what about triggered abilities? See addAbility above -- triggers adds to GameState + toRemove.forEach(r -> abilities.remove(r)); + } + + // Directly from PermanentImpl + @Override + public void removeAbilities(List abilitiesToRemove) { + if (abilitiesToRemove == null) { + return; + } + + abilitiesToRemove.forEach(a -> removeAbility(a)); + } + + @Override + public boolean putOntoBattlefield(int amount, Game game, Ability source) { + return this.putOntoBattlefield(amount, game, source, source.getControllerId()); } @Override @@ -186,7 +223,7 @@ public abstract class TokenImpl extends MageObjectImpl implements Token { if (amountToRemove > 0) { game.informPlayers( "The token limit per player is " + MAX_TOKENS_PER_GAME + ", " + controller.getName() - + " will only create " + tokenSlots + " tokens." + + " will only create " + tokenSlots + " tokens." ); Iterator> it = event.getTokens().entrySet().iterator(); while (it.hasNext() && amountToRemove > 0) { diff --git a/Mage/src/main/java/mage/game/permanent/token/TreasureToken.java b/Mage/src/main/java/mage/game/permanent/token/TreasureToken.java index 952ed64833f..d436e3005ab 100644 --- a/Mage/src/main/java/mage/game/permanent/token/TreasureToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/TreasureToken.java @@ -27,7 +27,7 @@ public final class TreasureToken extends TokenImpl { ability.addCost(new SacrificeSourceCost()); this.addAbility(ability); - availableImageSetCodes = Arrays.asList("XLN", "RNA", "M20", "C19", "C20", "M21", "CMR", "KHM", "STX", "MH2", "AFR"); + availableImageSetCodes = Arrays.asList("XLN", "RNA", "M20", "C19", "C20", "M21", "CMR", "KHM", "STX", "MH2", "AFR", "VOW"); } public TreasureToken(final TreasureToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/VampireLifelinkToken.java b/Mage/src/main/java/mage/game/permanent/token/VampireLifelinkToken.java new file mode 100644 index 00000000000..c8d7686b335 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/VampireLifelinkToken.java @@ -0,0 +1,48 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.LifelinkAbility; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.Arrays; + +/** + * + * @author weirddan455 + */ +public class VampireLifelinkToken extends TokenImpl { + + public VampireLifelinkToken() { + super("Vampire", "2/3 black Vampire creature token with flying and lifelink"); + cardType.add(CardType.CREATURE); + color.setBlack(true); + subtype.add(SubType.VAMPIRE); + power = new MageInt(2); + toughness = new MageInt(3); + + this.addAbility(FlyingAbility.getInstance()); + this.addAbility(LifelinkAbility.getInstance()); + + availableImageSetCodes = Arrays.asList("VOW"); + } + + @Override + public void setExpansionSetCodeForImage(String code) { + super.setExpansionSetCodeForImage(code); + + if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("VOW")) { + setTokenType(2); + } + } + + private VampireLifelinkToken(final VampireLifelinkToken token) { + super(token); + } + + @Override + public VampireLifelinkToken copy() { + return new VampireLifelinkToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/VrondissRageOfAncientsToken.java b/Mage/src/main/java/mage/game/permanent/token/VrondissRageOfAncientsToken.java index 04d71db2ee3..c1648e89bde 100644 --- a/Mage/src/main/java/mage/game/permanent/token/VrondissRageOfAncientsToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/VrondissRageOfAncientsToken.java @@ -9,6 +9,8 @@ import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; +import java.util.Arrays; + public final class VrondissRageOfAncientsToken extends TokenImpl { public VrondissRageOfAncientsToken() { @@ -20,7 +22,10 @@ public final class VrondissRageOfAncientsToken extends TokenImpl { subtype.add(SubType.SPIRIT); power = new MageInt(5); toughness = new MageInt(4); + this.addAbility(new VrondissRageOfAncientsTokenTriggeredAbility()); + + availableImageSetCodes = Arrays.asList("AFC"); } public VrondissRageOfAncientsToken(final VrondissRageOfAncientsToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/WolfToken.java b/Mage/src/main/java/mage/game/permanent/token/WolfToken.java index 3c96b8bb03c..30870f4a7be 100644 --- a/Mage/src/main/java/mage/game/permanent/token/WolfToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/WolfToken.java @@ -23,7 +23,7 @@ public final class WolfToken extends TokenImpl { availableImageSetCodes = Arrays.asList("BNG", "C14", "C15", "CMA", "CMD", "CNS", "DKA", "EVE", "ISD", "LRW", "M10", "M14", "MM2", "MOR", "SHM", "SOI", "SOM", "V10", "WWK", "ZEN", "WAR", "M20", - "THB", "AFR"); + "THB", "AFR", "MID", "VOW"); } @Override diff --git a/Mage/src/main/java/mage/game/permanent/token/WolvesOfTheHuntToken.java b/Mage/src/main/java/mage/game/permanent/token/WolvesOfTheHuntToken.java index af156cd597b..2228a476658 100644 --- a/Mage/src/main/java/mage/game/permanent/token/WolvesOfTheHuntToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/WolvesOfTheHuntToken.java @@ -1,19 +1,17 @@ - package mage.game.permanent.token; -import mage.constants.CardType; -import mage.constants.SubType; import mage.MageInt; import mage.abilities.keyword.BandsWithOtherAbility; +import mage.constants.CardType; +import mage.constants.SubType; /** - * * @author L_J */ public final class WolvesOfTheHuntToken extends TokenImpl { public WolvesOfTheHuntToken() { - super("Wolves of the Hunt", "1/1 green Wolf creature token named Wolves of the Hunt"); + super("Wolves of the Hunt", "1/1 green Wolf creature token named Wolves of the Hunt. It has \"bands with other creatures named Wolves of the Hunt.\""); cardType.add(CardType.CREATURE); subtype.add(SubType.WOLF); color.setGreen(true); diff --git a/Mage/src/main/java/mage/game/permanent/token/WrennAndSevenToken.java b/Mage/src/main/java/mage/game/permanent/token/WrennAndSevenTreefolkToken.java similarity index 75% rename from Mage/src/main/java/mage/game/permanent/token/WrennAndSevenToken.java rename to Mage/src/main/java/mage/game/permanent/token/WrennAndSevenTreefolkToken.java index 4397ea9050f..e8ac755adef 100644 --- a/Mage/src/main/java/mage/game/permanent/token/WrennAndSevenToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/WrennAndSevenTreefolkToken.java @@ -10,12 +10,14 @@ import mage.constants.Duration; import mage.constants.SubType; import mage.constants.Zone; +import java.util.Arrays; + /** * @author TheElk801 */ -public final class WrennAndSevenToken extends TokenImpl { +public final class WrennAndSevenTreefolkToken extends TokenImpl { - public WrennAndSevenToken() { + public WrennAndSevenTreefolkToken() { super("Treefolk", "green Treefolk creature token with reach and \"This creature's power and toughness are each equal to the number of lands you control.\""); cardType.add(CardType.CREATURE); color.setGreen(true); @@ -26,13 +28,15 @@ public final class WrennAndSevenToken extends TokenImpl { this.addAbility(new SimpleStaticAbility(Zone.ALL, new SetPowerToughnessSourceEffect( LandsYouControlCount.instance, Duration.EndOfGame ).setText("this creature's power and toughness are each equal to the number of lands you control"))); + + availableImageSetCodes.addAll(Arrays.asList("MID")); } - public WrennAndSevenToken(final WrennAndSevenToken token) { + public WrennAndSevenTreefolkToken(final WrennAndSevenTreefolkToken token) { super(token); } - public WrennAndSevenToken copy() { - return new WrennAndSevenToken(this); + public WrennAndSevenTreefolkToken copy() { + return new WrennAndSevenTreefolkToken(this); } } diff --git a/Mage/src/main/java/mage/game/permanent/token/ZombieArmyToken.java b/Mage/src/main/java/mage/game/permanent/token/ZombieArmyToken.java index dda369ef8ce..723d84dd17f 100644 --- a/Mage/src/main/java/mage/game/permanent/token/ZombieArmyToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/ZombieArmyToken.java @@ -22,7 +22,7 @@ public final class ZombieArmyToken extends TokenImpl { power = new MageInt(0); toughness = new MageInt(0); - availableImageSetCodes = Arrays.asList("WAR", "MH2"); + availableImageSetCodes = Arrays.asList("WAR", "MH2", "MIC"); } private ZombieArmyToken(final ZombieArmyToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/ZombieDecayedToken.java b/Mage/src/main/java/mage/game/permanent/token/ZombieDecayedToken.java index e64ab12141b..f36d562da62 100644 --- a/Mage/src/main/java/mage/game/permanent/token/ZombieDecayedToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/ZombieDecayedToken.java @@ -4,6 +4,9 @@ import mage.MageInt; import mage.abilities.keyword.DecayedAbility; import mage.constants.CardType; import mage.constants.SubType; +import mage.util.RandomUtil; + +import java.util.Arrays; /** * @author TheElk801 @@ -18,6 +21,8 @@ public final class ZombieDecayedToken extends TokenImpl { power = new MageInt(2); toughness = new MageInt(2); this.addAbility(new DecayedAbility()); + + availableImageSetCodes.addAll(Arrays.asList("MID")); } public ZombieDecayedToken(final ZombieDecayedToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/ZombieMenaceToken.java b/Mage/src/main/java/mage/game/permanent/token/ZombieMenaceToken.java index 3ebca8811cf..ad4cc7a671a 100644 --- a/Mage/src/main/java/mage/game/permanent/token/ZombieMenaceToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/ZombieMenaceToken.java @@ -5,8 +5,14 @@ import mage.abilities.keyword.MenaceAbility; import mage.constants.CardType; import mage.constants.SubType; +import java.util.Arrays; + public class ZombieMenaceToken extends TokenImpl { + public ZombieMenaceToken() { + this(0); + } + public ZombieMenaceToken(int xValue) { super("Zombie", "X/X blue and black Zombie creature token with menace"); cardType.add(CardType.CREATURE); @@ -15,7 +21,19 @@ public class ZombieMenaceToken extends TokenImpl { subtype.add(SubType.ZOMBIE); power = new MageInt(xValue); toughness = new MageInt(xValue); - addAbility(new MenaceAbility()); + + this.addAbility(new MenaceAbility()); + + availableImageSetCodes.addAll(Arrays.asList("MID")); + } + + @Override + public void setExpansionSetCodeForImage(String code) { + super.setExpansionSetCodeForImage(code); + + if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("MID")) { + this.setTokenType(2); + } } private ZombieMenaceToken(final ZombieMenaceToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/ZombieToken.java b/Mage/src/main/java/mage/game/permanent/token/ZombieToken.java index b16714cc806..3589b3747ad 100644 --- a/Mage/src/main/java/mage/game/permanent/token/ZombieToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/ZombieToken.java @@ -25,7 +25,7 @@ public final class ZombieToken extends TokenImpl { "CNS", "MMA", "BNG", "KTK", "DTK", "ORI", "OGW", "SOI", "EMN", "EMA", "MM3", "AKH", "CMA", "E01", "RNA", "WAR", "MH1", "M20", "C19", "THB", "M21", - "CMR", "C21", "MH2", "AFR"); + "CMR", "C21", "MH2", "AFR", "MIC", "VOW"); } @Override diff --git a/Mage/src/main/java/mage/game/stack/Spell.java b/Mage/src/main/java/mage/game/stack/Spell.java index 6738fd8f1a3..42f2e3db054 100644 --- a/Mage/src/main/java/mage/game/stack/Spell.java +++ b/Mage/src/main/java/mage/game/stack/Spell.java @@ -11,7 +11,7 @@ import mage.abilities.costs.mana.ManaCost; import mage.abilities.costs.mana.ManaCosts; import mage.abilities.keyword.BestowAbility; import mage.abilities.keyword.MorphAbility; -import mage.abilities.text.TextPart; +import mage.abilities.keyword.TransformAbility; import mage.cards.*; import mage.constants.*; import mage.counters.Counter; @@ -66,21 +66,29 @@ public class Spell extends StackObjectImpl implements Card { private ActivationManaAbilityStep currentActivatingManaAbilitiesStep = ActivationManaAbilityStep.BEFORE; public Spell(Card card, SpellAbility ability, UUID controllerId, Zone fromZone, Game game) { - this.card = card; - this.color = card.getColor(null).copy(); - this.frameColor = card.getFrameColor(null).copy(); - this.frameStyle = card.getFrameStyle(); + Card affectedCard = card; + + // TODO: must be removed after transform cards (one side) migrated to MDF engine (multiple sides) + if (ability.getSpellAbilityCastMode() == SpellAbilityCastMode.DISTURB && affectedCard.getSecondCardFace() != null) { + // simulate another side as new card (another code part in continues effect from disturb ability) + affectedCard = TransformAbility.transformCardSpellStatic(card, card.getSecondCardFace(), game); + } + + this.card = affectedCard; + this.color = affectedCard.getColor(null).copy(); + this.frameColor = affectedCard.getFrameColor(null).copy(); + this.frameStyle = affectedCard.getFrameStyle(); this.id = ability.getId(); - this.zoneChangeCounter = card.getZoneChangeCounter(game); // sync card's ZCC with spell (copy spell settings) + this.zoneChangeCounter = affectedCard.getZoneChangeCounter(game); // sync card's ZCC with spell (copy spell settings) this.ability = ability; this.ability.setControllerId(controllerId); if (ability.getSpellAbilityType() == SpellAbilityType.SPLIT_FUSED) { - spellCards.add(((SplitCard) card).getLeftHalfCard()); - spellAbilities.add(((SplitCard) card).getLeftHalfCard().getSpellAbility().copy()); - spellCards.add(((SplitCard) card).getRightHalfCard()); - spellAbilities.add(((SplitCard) card).getRightHalfCard().getSpellAbility().copy()); + spellCards.add(((SplitCard) affectedCard).getLeftHalfCard()); + spellAbilities.add(((SplitCard) affectedCard).getLeftHalfCard().getSpellAbility().copy()); + spellCards.add(((SplitCard) affectedCard).getRightHalfCard()); + spellAbilities.add(((SplitCard) affectedCard).getRightHalfCard().getSpellAbility().copy()); } else { - spellCards.add(card); + spellCards.add(affectedCard); spellAbilities.add(ability); } this.controllerId = controllerId; @@ -907,11 +915,6 @@ public class Spell extends StackObjectImpl implements Card { throw new UnsupportedOperationException("Unsupported operation"); } - @Override - public void setTransformable(boolean value) { - throw new UnsupportedOperationException("Unsupported operation"); - } - @Override public int getZoneChangeCounter(Game game) { // spell's zcc can't be changed after put to stack @@ -1099,16 +1102,6 @@ public class Spell extends StackObjectImpl implements Card { public void setIsAllCreatureTypes(Game game, boolean value) { } - @Override - public List getTextParts() { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public TextPart addTextPart(TextPart textPart) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - @Override public List getAttachments() { throw new UnsupportedOperationException("Not supported."); //To change body of generated methods, choose Tools | Templates. diff --git a/Mage/src/main/java/mage/game/stack/StackAbility.java b/Mage/src/main/java/mage/game/stack/StackAbility.java index e9493bcb317..d3f7bc94894 100644 --- a/Mage/src/main/java/mage/game/stack/StackAbility.java +++ b/Mage/src/main/java/mage/game/stack/StackAbility.java @@ -16,7 +16,6 @@ import mage.abilities.effects.Effect; import mage.abilities.effects.Effects; import mage.abilities.hint.Hint; import mage.abilities.icon.CardIcon; -import mage.abilities.text.TextPart; import mage.cards.Card; import mage.cards.FrameStyle; import mage.constants.*; @@ -576,6 +575,16 @@ public class StackAbility extends StackObjectImpl implements Ability { return ability.getSourcePermanentOrLKI(game); } + @Override + public void setSourcePermanentTransformCount(Game game) { + ability.setSourcePermanentTransformCount(game); + } + + @Override + public boolean checkTransformCount(Permanent permanent, Game game) { + return ability.checkTransformCount(permanent, game); + } + @Override public int getZoneChangeCounter(Game game) { return game.getState().getZoneChangeCounter(getSourceId()); @@ -637,18 +646,9 @@ public class StackAbility extends StackObjectImpl implements Ability { } @Override - public List getTextParts() { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public TextPart addTextPart(TextPart textPart) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public void setTargetAdjuster(TargetAdjuster targetAdjuster) { + public StackAbility setTargetAdjuster(TargetAdjuster targetAdjuster) { this.targetAdjuster = targetAdjuster; + return this; } @Override diff --git a/Mage/src/main/java/mage/game/tournament/TournamentImpl.java b/Mage/src/main/java/mage/game/tournament/TournamentImpl.java index 47f1214b6d4..b7e53a86459 100644 --- a/Mage/src/main/java/mage/game/tournament/TournamentImpl.java +++ b/Mage/src/main/java/mage/game/tournament/TournamentImpl.java @@ -419,20 +419,9 @@ public abstract class TournamentImpl implements Tournament { } } } - resetBufferedCards(); nextStep(); } - public void resetBufferedCards() { - Set setsDone = new HashSet<>(); - for (ExpansionSet set : sets) { - if (!setsDone.contains(set)) { - set.removeSavedCards(); - setsDone.add(set); - } - } - } - public void playMatch(TournamentPairing pair) { options.getMatchOptions().getPlayerTypes().clear(); options.getMatchOptions().getPlayerTypes().add(pair.getPlayer1().getPlayerType()); diff --git a/Mage/src/main/java/mage/game/turn/Turn.java b/Mage/src/main/java/mage/game/turn/Turn.java index 80410f1dba2..577eb988245 100644 --- a/Mage/src/main/java/mage/game/turn/Turn.java +++ b/Mage/src/main/java/mage/game/turn/Turn.java @@ -1,10 +1,5 @@ package mage.game.turn; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.UUID; import mage.abilities.Ability; import mage.constants.PhaseStep; import mage.constants.TurnPhase; @@ -17,6 +12,12 @@ import mage.game.stack.StackObject; import mage.players.Player; import mage.util.ThreadLocalStringBuilder; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.UUID; + /** * @author BetaSteward_at_googlemail.com */ @@ -95,7 +96,7 @@ public class Turn implements Serializable { * @param activePlayer * @return true if turn is skipped */ - public boolean play(Game game, Player activePlayer) { + public boolean play(Game game, Player activePlayer) { // uncomment this to trace triggered abilities and/or continous effects // TraceUtil.traceTriggeredAbilities(game); // game.getState().getContinuousEffects().traceContinuousEffects(game); @@ -121,24 +122,25 @@ public class Turn implements Serializable { if (game.isPaused() || game.checkIfGameIsOver()) { return false; } - if (!isEndTurnRequested() || phase.getType() == TurnPhase.END) { - currentPhase = phase; - game.fireEvent(new PhaseChangedEvent(activePlayer.getId(), null)); - if (!game.getState().getTurnMods().skipPhase(activePlayer.getId(), currentPhase.getType())) { - if (phase.play(game, activePlayer.getId())) { - if (game.executingRollback()) { - return false; - } - //20091005 - 500.4/703.4n - game.emptyManaPools(null); - game.saveState(false); - - //20091005 - 500.8 - while (playExtraPhases(game, phase.getType())) { - } - } - } + if (isEndTurnRequested() && phase.getType() != TurnPhase.END) { + continue; } + currentPhase = phase; + game.fireEvent(new PhaseChangedEvent(activePlayer.getId(), null)); + if (game.getState().getTurnMods().skipPhase( + activePlayer.getId(), currentPhase.getType() + ) || !phase.play(game, activePlayer.getId())) { + continue; + } + if (game.executingRollback()) { + return false; + } + //20091005 - 500.4/703.4n + game.emptyManaPools(null); + game.saveState(false); + + //20091005 - 500.8 + while (playExtraPhases(game, phase.getType())) ; } return false; } diff --git a/Mage/src/main/java/mage/game/turn/UntapStep.java b/Mage/src/main/java/mage/game/turn/UntapStep.java index d9c0c28f38d..658fba2190b 100644 --- a/Mage/src/main/java/mage/game/turn/UntapStep.java +++ b/Mage/src/main/java/mage/game/turn/UntapStep.java @@ -2,14 +2,15 @@ package mage.game.turn; -import java.util.UUID; import mage.constants.PhaseStep; import mage.game.Game; import mage.game.events.GameEvent.EventType; import mage.players.Player; +import mage.watchers.common.CastSpellLastTurnWatcher; + +import java.util.UUID; /** - * * @author BetaSteward_at_googlemail.com */ public class UntapStep extends Step { @@ -28,6 +29,7 @@ public class UntapStep extends Step { @Override public void beginStep(Game game, UUID activePlayerId) { super.beginStep(game, activePlayerId); + handleDayNight(game); Player activePlayer = game.getPlayer(activePlayerId); //20091005 - 502.1/703.4a activePlayer.phasing(game); @@ -41,4 +43,18 @@ public class UntapStep extends Step { return new UntapStep(this); } + private void handleDayNight(Game game) { + if (!game.hasDayNight() || game.getTurnNum() <= 1) { + return; + } + int previousSpells = game + .getState() + .getWatcher(CastSpellLastTurnWatcher.class) + .getActivePlayerPrevTurnCount(); + if (game.checkDayNight(true) && previousSpells == 0) { + game.setDaytime(false); + } else if (game.checkDayNight(false) && previousSpells >= 2) { + game.setDaytime(true); + } + } } diff --git a/Mage/src/main/java/mage/players/Player.java b/Mage/src/main/java/mage/players/Player.java index 1a1f9111da2..76fa8680f71 100644 --- a/Mage/src/main/java/mage/players/Player.java +++ b/Mage/src/main/java/mage/players/Player.java @@ -127,6 +127,8 @@ public interface Player extends MageItem, Copyable { void exchangeLife(Player player, Ability source, Game game); + int damage(int damage, Ability source, Game game); + int damage(int damage, UUID attackerId, Ability source, Game game); int damage(int damage, UUID attackerId, Ability source, Game game, boolean combatDamage, boolean preventable); diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index 2feceac5e79..51fc3e8602e 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -1011,7 +1011,7 @@ public abstract class PlayerImpl implements Player, Serializable { if (cardInLib != null && cardInLib.getId().equals(card.getId())) { // check needed because e.g. commander can go to command zone cardInLib = getLibrary().removeFromTop(game); getLibrary().putCardToTopXPos(cardInLib, xFromTheTop, game); - game.informPlayers(withName ? cardInLib.getLogName() : "A card" + game.informPlayers((withName ? cardInLib.getLogName() : "A card") + " is put into " + getLogName() + "'s library " @@ -2063,11 +2063,11 @@ public abstract class PlayerImpl implements Player, Serializable { game.informPlayers(this.getLogName() + " loses " + event.getAmount() + " life" + (atCombat ? " at combat" : "") + CardUtil.getSourceLogName(game, " from ", needId, "", "")); } - if (amount > 0) { + if (event.getAmount() > 0) { game.fireEvent(new GameEvent(GameEvent.EventType.LOST_LIFE, - playerId, source, playerId, amount, atCombat)); + playerId, source, playerId, event.getAmount(), atCombat)); } - return amount; + return event.getAmount(); } return 0; } @@ -2121,6 +2121,11 @@ public abstract class PlayerImpl implements Player, Serializable { } } + @Override + public int damage(int damage, Ability source, Game game) { + return doDamage(damage, source.getSourceId(), source, game, false, true, null); + } + @Override public int damage(int damage, UUID attackerId, Ability source, Game game) { return doDamage(damage, attackerId, source, game, false, true, null); @@ -2298,6 +2303,7 @@ public abstract class PlayerImpl implements Player, Serializable { public void addAbility(Ability ability) { ability.setSourceId(playerId); this.abilities.add(ability); + this.abilities.addAll(ability.getSubAbilities()); } @Override @@ -2619,10 +2625,9 @@ public abstract class PlayerImpl implements Player, Serializable { Permanent attacker = game.getPermanent(attackerId); if (attacker != null && attacker.canAttack(defenderId, game) - && attacker.isControlledBy(playerId)) { - if (!game.getCombat().declareAttacker(attackerId, defenderId, playerId, game)) { - game.undo(playerId); - } + && attacker.isControlledBy(playerId) + && !game.getCombat().declareAttacker(attackerId, defenderId, playerId, game)) { + game.undo(playerId); } } diff --git a/Mage/src/main/java/mage/target/TargetSpell.java b/Mage/src/main/java/mage/target/TargetSpell.java index b959d8697ea..5523f16363e 100644 --- a/Mage/src/main/java/mage/target/TargetSpell.java +++ b/Mage/src/main/java/mage/target/TargetSpell.java @@ -66,6 +66,9 @@ public class TargetSpell extends TargetObject { @Override public boolean canChoose(UUID sourceId, UUID sourceControllerId, Game game) { + if (this.minNumberOfTargets == 0) { + return true; + } int count = 0; for (StackObject stackObject : game.getStack()) { // rule 114.4. A spell or ability on the stack is an illegal target for itself. diff --git a/Mage/src/main/java/mage/target/common/TargetCreaturePermanentAmount.java b/Mage/src/main/java/mage/target/common/TargetCreaturePermanentAmount.java index 921dea995d9..3367668ccce 100644 --- a/Mage/src/main/java/mage/target/common/TargetCreaturePermanentAmount.java +++ b/Mage/src/main/java/mage/target/common/TargetCreaturePermanentAmount.java @@ -2,19 +2,21 @@ package mage.target.common; import mage.abilities.dynamicvalue.DynamicValue; import mage.filter.FilterPermanent; -import mage.filter.StaticFilters; +import mage.filter.common.FilterCreaturePermanent; /** * @author North */ public class TargetCreaturePermanentAmount extends TargetPermanentAmount { + private static final FilterCreaturePermanent defaultFilter = new FilterCreaturePermanent("target creatures"); + public TargetCreaturePermanentAmount(int amount) { - super(amount, StaticFilters.FILTER_PERMANENT_CREATURE); + super(amount, defaultFilter); } public TargetCreaturePermanentAmount(DynamicValue amount) { - this(amount, StaticFilters.FILTER_PERMANENT_CREATURE); + this(amount, defaultFilter); } public TargetCreaturePermanentAmount(int amount, FilterPermanent filter) { diff --git a/Mage/src/main/java/mage/target/common/TargetDefender.java b/Mage/src/main/java/mage/target/common/TargetDefender.java index d83839e8060..d89d3a35f6e 100644 --- a/Mage/src/main/java/mage/target/common/TargetDefender.java +++ b/Mage/src/main/java/mage/target/common/TargetDefender.java @@ -38,6 +38,7 @@ public class TargetDefender extends TargetImpl { this.filter = new FilterPlaneswalkerOrPlayer(defenders); this.targetName = filter.getMessage(); this.attackerId = attackerId; + this.notTarget = true; } public TargetDefender(final TargetDefender target) { diff --git a/Mage/src/main/java/mage/target/common/TargetEnchantmentPermanent.java b/Mage/src/main/java/mage/target/common/TargetEnchantmentPermanent.java index 2e555a149db..5bcb16bdbe2 100644 --- a/Mage/src/main/java/mage/target/common/TargetEnchantmentPermanent.java +++ b/Mage/src/main/java/mage/target/common/TargetEnchantmentPermanent.java @@ -22,7 +22,7 @@ public class TargetEnchantmentPermanent extends TargetPermanent { } public TargetEnchantmentPermanent(int minNumTargets, int maxNumTargets) { - this(minNumTargets, maxNumTargets, StaticFilters.FILTER_ENCHANTMENT_PERMANENT, false); + this(minNumTargets, maxNumTargets, StaticFilters.FILTER_PERMANENT_ENCHANTMENT, false); } public TargetEnchantmentPermanent(int minNumTargets, int maxNumTargets, FilterEnchantmentPermanent filter, boolean notTarget) { diff --git a/Mage/src/main/java/mage/target/common/TargetLandPermanent.java b/Mage/src/main/java/mage/target/common/TargetLandPermanent.java index 2be545ffbcb..a0c69ef1ab7 100644 --- a/Mage/src/main/java/mage/target/common/TargetLandPermanent.java +++ b/Mage/src/main/java/mage/target/common/TargetLandPermanent.java @@ -23,6 +23,10 @@ public class TargetLandPermanent extends TargetPermanent { this(numTargets, numTargets, StaticFilters.FILTER_LAND, false); } + public TargetLandPermanent(int numTargets, int maxNumTargets) { + this(numTargets, maxNumTargets, StaticFilters.FILTER_LAND, false); + } + public TargetLandPermanent(int minNumTargets, int maxNumTargets, FilterLandPermanent filter, boolean notTarget) { super(minNumTargets, maxNumTargets, filter, notTarget); } diff --git a/Mage/src/main/java/mage/target/common/TargetPermanentAmount.java b/Mage/src/main/java/mage/target/common/TargetPermanentAmount.java index 50b6d967a8d..dae961df917 100644 --- a/Mage/src/main/java/mage/target/common/TargetPermanentAmount.java +++ b/Mage/src/main/java/mage/target/common/TargetPermanentAmount.java @@ -19,11 +19,11 @@ import java.util.stream.Collectors; /** * @author TheElk801 */ -public abstract class TargetPermanentAmount extends TargetAmount { +public class TargetPermanentAmount extends TargetAmount { protected final FilterPermanent filter; - TargetPermanentAmount(int amount, FilterPermanent filter) { + public TargetPermanentAmount(int amount, FilterPermanent filter) { // 107.1c If a rule or ability instructs a player to choose “any number,” that player may choose // any positive number or zero, unless something (such as damage or counters) is being divided // or distributed among “any number” of players and/or objects. In that case, a nonzero number @@ -31,18 +31,23 @@ public abstract class TargetPermanentAmount extends TargetAmount { this(StaticValue.get(amount), filter); } - TargetPermanentAmount(DynamicValue amount, FilterPermanent filter) { + public TargetPermanentAmount(DynamicValue amount, FilterPermanent filter) { super(amount); this.zone = Zone.ALL; this.filter = filter; this.targetName = filter.getMessage(); } - TargetPermanentAmount(final TargetPermanentAmount target) { + protected TargetPermanentAmount(final TargetPermanentAmount target) { super(target); this.filter = target.filter.copy(); } + @Override + public TargetPermanentAmount copy() { + return new TargetPermanentAmount(this); + } + @Override public Filter getFilter() { return this.filter; @@ -132,7 +137,4 @@ public abstract class TargetPermanentAmount extends TargetAmount { }); return sb.toString().trim(); } - - @Override - public abstract TargetPermanentAmount copy(); } diff --git a/Mage/src/main/java/mage/target/targetadjustment/TargetAdjuster.java b/Mage/src/main/java/mage/target/targetadjustment/TargetAdjuster.java index af173fa18f0..20904454459 100644 --- a/Mage/src/main/java/mage/target/targetadjustment/TargetAdjuster.java +++ b/Mage/src/main/java/mage/target/targetadjustment/TargetAdjuster.java @@ -3,11 +3,13 @@ package mage.target.targetadjustment; import mage.abilities.Ability; import mage.game.Game; +import java.io.Serializable; + /** * @author TheElk801 */ -public interface TargetAdjuster { +@FunctionalInterface +public interface TargetAdjuster extends Serializable { void adjustTargets(Ability ability, Game game); - } diff --git a/Mage/src/main/java/mage/target/targetpointer/FixedTargets.java b/Mage/src/main/java/mage/target/targetpointer/FixedTargets.java index 5c8db89f6a9..78d26932f87 100644 --- a/Mage/src/main/java/mage/target/targetpointer/FixedTargets.java +++ b/Mage/src/main/java/mage/target/targetpointer/FixedTargets.java @@ -32,10 +32,11 @@ public class FixedTargets extends TargetPointerImpl { public FixedTargets(Cards cards, Game game) { super(); - - for (UUID targetId : cards) { - MageObjectReference mor = new MageObjectReference(targetId, game); - targets.add(mor); + if (cards != null) { + for (UUID targetId : cards) { + MageObjectReference mor = new MageObjectReference(targetId, game); + targets.add(mor); + } } this.initialized = true; } @@ -92,24 +93,24 @@ public class FixedTargets extends TargetPointerImpl { @Override public List getTargets(Game game, Ability source) { // check target not changed zone - List list = new ArrayList<>(); - for (MageObjectReference mor : targets) { - if (mor.getSourceId() != null && game.getState().getZoneChangeCounter(mor.getSourceId()) == mor.getZoneChangeCounter()) { - list.add(mor.getSourceId()); - } - } - return list; + return targets + .stream() + .filter(mor -> mor.zoneCounterIsCurrent(game)) + .map(MageObjectReference::getSourceId) + .filter(Objects::nonNull) + .collect(Collectors.toList()); } @Override public UUID getFirst(Game game, Ability source) { // check target not changed zone - for (MageObjectReference mor : targets) { - if (game.getState().getZoneChangeCounter(mor.getSourceId()) == mor.getZoneChangeCounter()) { - return mor.getSourceId(); - } - } - return null; + return targets + .stream() + .filter(mor -> mor.zoneCounterIsCurrent(game)) + .map(MageObjectReference::getSourceId) + .filter(Objects::nonNull) + .findFirst() + .orElse(null); } @Override diff --git a/Mage/src/main/java/mage/util/CardUtil.java b/Mage/src/main/java/mage/util/CardUtil.java index 402ecd44191..7d39d184c27 100644 --- a/Mage/src/main/java/mage/util/CardUtil.java +++ b/Mage/src/main/java/mage/util/CardUtil.java @@ -10,6 +10,8 @@ import mage.abilities.SpellAbility; import mage.abilities.condition.Condition; import mage.abilities.costs.VariableCost; import mage.abilities.costs.mana.*; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.Effect; import mage.abilities.effects.common.asthought.CanPlayCardControllerEffect; @@ -852,20 +854,43 @@ public final class CardUtil { } } - public static String getBoostCountAsStr(int power, int toughness) { + public static String getBoostCountAsStr(DynamicValue power, DynamicValue toughness) { // sign fix for zero values // -1/+0 must be -1/-0 // +0/-1 must be -0/-1 - String signedP = String.format("%1$+d", power); - String signedT = String.format("%1$+d", toughness); - if (signedP.equals("+0") && signedT.startsWith("-")) { - signedP = "-0"; + String p = power.toString(); + String t = toughness.toString(); + if (!p.startsWith("-")) { + p = (t.startsWith("-") && p.equals("0") ? "-" : "+") + p; } - if (signedT.equals("+0") && signedP.startsWith("-")) { - signedT = "-0"; + if (!t.startsWith("-")) { + t = (p.startsWith("-") && t.equals("0") ? "-" : "+") + t; } + return p + "/" + t; + } - return signedP + "/" + signedT; + public static String getBoostCountAsStr(int power, int toughness) { + return getBoostCountAsStr(StaticValue.get(power), StaticValue.get(toughness)); + } + + public static String getBoostText(DynamicValue power, DynamicValue toughness, Duration duration) { + String boostCount = getBoostCountAsStr(power, toughness); + StringBuilder sb = new StringBuilder(boostCount); + // don't include "for the rest of the game" for emblems, etc. + if (duration != Duration.EndOfGame) { + String d = duration.toString(); + if (!d.isEmpty()) { + sb.append(" ").append(d); + } + } + String message = power.getMessage(); + if (message.isEmpty()) { + message = toughness.getMessage(); + } + if (!message.isEmpty()) { + sb.append(boostCount.contains("X") ? ", where X is " : " for each ").append(message); + } + return sb.toString(); } public static boolean isSpliceAbility(Ability ability, Game game) { @@ -915,6 +940,14 @@ public final class CardUtil { } } + public static String getTextWithFirstCharLowerCase(String text) { + if (text != null && text.length() >= 1) { + return Character.toLowerCase(text.charAt(0)) + text.substring(1); + } else { + return text; + } + } + private static final String vowels = "aeiouAEIOU"; public static String addArticle(String text) { @@ -964,7 +997,7 @@ public final class CardUtil { // same logic as ZonesHandler->maybeRemoveFromSourceZone // workaround to put real permanent from one side (example: you call mdf card by cheats) - Card permCard = getDefaultCardSideForBattlefield(newCard); + Card permCard = getDefaultCardSideForBattlefield(game, newCard); // prepare card and permanent permCard.setZone(Zone.BATTLEFIELD, game); @@ -1002,12 +1035,17 @@ public final class CardUtil { /** * Choose default card's part to put on battlefield (for cheats and tests only) + * or to find a default card side (for copy effect) * * @param card * @return */ - public static Card getDefaultCardSideForBattlefield(Card card) { - // chose left side all time + public static Card getDefaultCardSideForBattlefield(Game game, Card card) { + if (card instanceof PermanentCard) { + return card; + } + + // must choose left side all time Card permCard; if (card instanceof SplitCard) { permCard = card; @@ -1018,6 +1056,12 @@ public final class CardUtil { } else { permCard = card; } + + // must be creature/planeswalker (if you catch this error then check targeting/copying code) + if (permCard.isInstantOrSorcery(game)) { + throw new IllegalArgumentException("Card side can't be put to battlefield: " + permCard.getName()); + } + return permCard; } @@ -1124,7 +1168,7 @@ public final class CardUtil { } public static void makeCardPlayable(Game game, Ability source, Card card, Duration duration, boolean anyColor) { - makeCardPlayable(game, source, card, duration, anyColor, null); + makeCardPlayable(game, source, card, duration, anyColor, null, null); } /** @@ -1139,16 +1183,16 @@ public final class CardUtil { * @param anyColor * @param condition can be null */ - public static void makeCardPlayable(Game game, Ability source, Card card, Duration duration, boolean anyColor, Condition condition) { + public static void makeCardPlayable(Game game, Ability source, Card card, Duration duration, boolean anyColor, UUID playerId, Condition condition) { // Effect can be used for cards in zones and permanents on battlefield // PermanentCard's ZCC is static, but we need updated ZCC from the card (after moved to another zone) // So there is a workaround to get actual card's ZCC // Example: Hostage Taker UUID objectId = card.getMainCard().getId(); int zcc = game.getState().getZoneChangeCounter(objectId); - game.addEffect(new CanPlayCardControllerEffect(game, objectId, zcc, duration, condition), source); + game.addEffect(new CanPlayCardControllerEffect(game, objectId, zcc, duration, playerId, condition), source); if (anyColor) { - game.addEffect(new YouMaySpendManaAsAnyColorToCastTargetEffect(duration, condition).setTargetPointer(new FixedTarget(objectId, zcc)), source); + game.addEffect(new YouMaySpendManaAsAnyColorToCastTargetEffect(duration, playerId, condition).setTargetPointer(new FixedTarget(objectId, zcc)), source); } } @@ -1185,6 +1229,11 @@ public final class CardUtil { return false; } + public static String getSourceName(Game game, Ability source) { + MageObject sourceObject = source.getSourceObject(game); + return sourceObject != null ? sourceObject.getName() : ""; + } + /** * Generates source log name to insert into log messages * @@ -1235,8 +1284,11 @@ public final class CardUtil { return zcc; } - public static boolean checkCostWords(String text) { - return text != null && costWords.stream().anyMatch(text.toLowerCase(Locale.ENGLISH)::startsWith); + public static String addCostVerb(String text) { + if (costWords.stream().anyMatch(text.toLowerCase(Locale.ENGLISH)::startsWith)) { + return text; + } + return "pay " + text; } /** @@ -1409,4 +1461,7 @@ public final class CardUtil { return true; } + public static int setOrIncrementValue(T u, Integer i) { + return i == null ? 1 : Integer.sum(i, 1); + } } diff --git a/Mage/src/main/java/mage/util/RandomUtil.java b/Mage/src/main/java/mage/util/RandomUtil.java index 5f2458c4dcc..b0424863455 100644 --- a/Mage/src/main/java/mage/util/RandomUtil.java +++ b/Mage/src/main/java/mage/util/RandomUtil.java @@ -3,6 +3,8 @@ package mage.util; import java.awt.*; import java.util.Collection; import java.util.Random; +import java.util.Set; +import java.util.UUID; /** * Created by IGOUDT on 5-9-2016. diff --git a/Mage/src/main/java/mage/util/functions/CopyTokenFunction.java b/Mage/src/main/java/mage/util/functions/CopyTokenFunction.java index c335c43a0fc..48e4c673f6a 100644 --- a/Mage/src/main/java/mage/util/functions/CopyTokenFunction.java +++ b/Mage/src/main/java/mage/util/functions/CopyTokenFunction.java @@ -66,7 +66,7 @@ public class CopyTokenFunction implements Function { target.setName(sourceObj.getName()); target.getColor().setColor(sourceObj.getColor()); target.getManaCost().clear(); - target.getManaCost().add(sourceObj.getManaCost()); + target.getManaCost().add(sourceObj.getManaCost().copy()); target.removeAllCardTypes(); for (CardType type : sourceObj.getCardType()) { target.addCardType(type); @@ -92,6 +92,7 @@ public class CopyTokenFunction implements Function { target.getPower().modifyBaseValue(sourceObj.getPower().getBaseValueModified()); target.getToughness().modifyBaseValue(sourceObj.getToughness().getBaseValueModified()); + target.setStartingLoyalty(sourceObj.getStartingLoyalty()); return target; } diff --git a/Mage/src/main/java/mage/watchers/common/AbilityResolvedWatcher.java b/Mage/src/main/java/mage/watchers/common/AbilityResolvedWatcher.java index 953e4d64df1..fd65767ad3d 100644 --- a/Mage/src/main/java/mage/watchers/common/AbilityResolvedWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/AbilityResolvedWatcher.java @@ -33,7 +33,11 @@ public class AbilityResolvedWatcher extends Watcher { resolutionMap.clear(); } - public int getResolutionCount(Game game, Ability source) { - return resolutionMap.getOrDefault(source.getOriginalId().toString() + game.getState().getZoneChangeCounter(source.getSourceId()), 0); + public static int getResolutionCount(Game game, Ability source) { + return game + .getState() + .getWatcher(AbilityResolvedWatcher.class) + .resolutionMap + .getOrDefault(source.getOriginalId().toString() + game.getState().getZoneChangeCounter(source.getSourceId()), 0); } } diff --git a/Mage/src/main/java/mage/watchers/common/AttackedThisTurnWatcher.java b/Mage/src/main/java/mage/watchers/common/AttackedThisTurnWatcher.java index aa74dad37fb..b199c5b431a 100644 --- a/Mage/src/main/java/mage/watchers/common/AttackedThisTurnWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/AttackedThisTurnWatcher.java @@ -19,6 +19,9 @@ public class AttackedThisTurnWatcher extends Watcher { private final Set attackedThisTurnCreatures = new HashSet<>(); private final Map attackedThisTurnCreaturesCounts = new HashMap<>(); + + // issue with Robber of the Rich. it needs to check the subtype of the LKI of the permanent on the battlefield and this fails with MageObjectReference + private final Set attackedThisTurnCreaturesPermanentLKI = new HashSet<>(); public AttackedThisTurnWatcher() { super(WatcherScope.GAME); @@ -28,9 +31,13 @@ public class AttackedThisTurnWatcher extends Watcher { public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.ATTACKER_DECLARED) { MageObjectReference mor = new MageObjectReference(event.getSourceId(), game); + Permanent permanentOnBattlefield = game.getPermanentOrLKIBattlefield(event.getSourceId()); this.attackedThisTurnCreatures.add(mor); this.attackedThisTurnCreaturesCounts.putIfAbsent(mor, 0); this.attackedThisTurnCreaturesCounts.compute(mor, (k, v) -> (v + 1)); + + // bug with Robber of the Rich as noted above + this.attackedThisTurnCreaturesPermanentLKI.add(permanentOnBattlefield); } } @@ -54,11 +61,16 @@ public class AttackedThisTurnWatcher extends Watcher { } return false; } + + public Set getAttackedThisTurnCreaturesPermanentLKI() { + return this.attackedThisTurnCreaturesPermanentLKI; + } @Override public void reset() { super.reset(); this.attackedThisTurnCreatures.clear(); this.attackedThisTurnCreaturesCounts.clear(); + this.getAttackedThisTurnCreaturesPermanentLKI().clear(); } } diff --git a/Mage/src/main/java/mage/watchers/common/CardsPutIntoGraveyardWatcher.java b/Mage/src/main/java/mage/watchers/common/CardsPutIntoGraveyardWatcher.java index 75ed33cdb59..2c3211c1daa 100644 --- a/Mage/src/main/java/mage/watchers/common/CardsPutIntoGraveyardWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/CardsPutIntoGraveyardWatcher.java @@ -13,16 +13,20 @@ import java.util.*; import java.util.stream.Collectors; /** - * Counts amount of cards put into graveyards of players during the current - * turn. Also the UUIDs of cards that went to graveyard from Battlefield this - * turn. + * Counts how many cards are put into each player's graveyard this turn. + * Keeps track of the UUIDs of the cards that went to graveyard this turn. + * from the battlefield, from anywhere other both from anywhere and from only the battlefield. * * @author LevelX2 */ public class CardsPutIntoGraveyardWatcher extends Watcher { + // Number of cards that have entered each players graveyards private final Map amountOfCardsThisTurn = new HashMap<>(); - private final Set cardsPutToGraveyardFromBattlefield = new HashSet<>(); + // UUID of cards that entered the graveyard from the battlefield + private final Set cardsPutIntoGraveyardFromBattlefield = new HashSet<>(); + // UUID of cards that entered the graveyard from everywhere other than the battlefield + private final Set cardsPutIntoGraveyardFromEverywhereElse = new HashSet<>(); public CardsPutIntoGraveyardWatcher() { super(WatcherScope.GAME); @@ -34,33 +38,102 @@ public class CardsPutIntoGraveyardWatcher extends Watcher { || ((ZoneChangeEvent) event).getToZone() != Zone.GRAVEYARD) { return; } + UUID playerId = event.getPlayerId(); if (playerId == null || game.getCard(event.getTargetId()) == null) { return; } + amountOfCardsThisTurn.compute(playerId, (k, amount) -> amount == null ? 1 : Integer.sum(amount, 1)); + if (((ZoneChangeEvent) event).getFromZone() == Zone.BATTLEFIELD) { - cardsPutToGraveyardFromBattlefield.add(new MageObjectReference(((ZoneChangeEvent) event).getTarget(), game, 1)); + cardsPutIntoGraveyardFromBattlefield.add(new MageObjectReference(((ZoneChangeEvent) event).getTarget(), game, 1)); + } else { + cardsPutIntoGraveyardFromEverywhereElse.add(new MageObjectReference(((ZoneChangeEvent) event).getTarget(), game, 1)); } } + /** + * The number of cards that were put into a specific player's graveyard this turn. + * + * @param playerId The player's UUID. + * @return The number of cards. + */ public int getAmountCardsPutToGraveyard(UUID playerId) { return amountOfCardsThisTurn.getOrDefault(playerId, 0); } - public Set getCardsPutToGraveyardFromBattlefield(Game game) { - return cardsPutToGraveyardFromBattlefield.stream().map(mor -> mor.getCard(game)).filter(Objects::nonNull).collect(Collectors.toSet()); + /** + * The cards put into any graveyard from the battelfield this turn. + * + * @param game The game to check for. + * @return A set containing the card objects. + */ + public Set getCardsPutIntoGraveyardFromBattlefield(Game game) { + return cardsPutIntoGraveyardFromBattlefield.stream().map(mor -> mor.getCard(game)).filter(Objects::nonNull).collect(Collectors.toSet()); } + /** + * The cards put into any graveyard from anywhere other than the battelfield this turn. + * + * @param game The game to check for. + * @return A set containing the card objects. + */ + public Set getCardsPutIntoGraveyardNotFromBattlefield(Game game) { + return cardsPutIntoGraveyardFromEverywhereElse.stream().map(mor -> mor.getCard(game)).filter(Objects::nonNull).collect(Collectors.toSet()); + } + + /** + * The cards put into any graveyard from anywhere this turn. + * + * @param game The game to check for. + * @return A set containing the card objects. + */ + public Set getCardsPutIntoGraveyardFromAnywhere(Game game) { + Set cardsPutIntoGraveyardFromAnywhere = getCardsPutIntoGraveyardFromBattlefield(game); + cardsPutIntoGraveyardFromAnywhere.addAll(getCardsPutIntoGraveyardNotFromBattlefield(game)); + + return cardsPutIntoGraveyardFromAnywhere; + } + + /** + * Check if the passed card was put into the graveyard from the battlefield this turn. + * + * @param card The card to check. + * @param game The game to check for. + * @return Boolean indicating if the card was put into the graveyard from the battlefield this turn. + */ public boolean checkCardFromBattlefield(Card card, Game game) { - return cardsPutToGraveyardFromBattlefield.stream().anyMatch(mor -> mor.refersTo(card, game)); + return cardsPutIntoGraveyardFromBattlefield.stream().anyMatch(mor -> mor.refersTo(card, game)); + } + + /** + * Check if the passed card was put into the graveyard from anywhere other than the battlefield this turn. + * + * @param card The card to check. + * @param game The game to check for. + * @return Boolean indicating if the card was put into the graveyard from anywhere other than the battlefield this turn. + */ + public boolean checkCardNotFromBattlefield(Card card, Game game) { + return cardsPutIntoGraveyardFromEverywhereElse.stream().anyMatch(mor -> mor.refersTo(card, game)); + } + + /** + * Check if the passed card was put into the graveyard from anywhere this turn. + * + * @param card The card to check. + * @param game The game to check for. + * @return Boolean indicating if the card was put into the graveyard from anywhere this turn. + */ + public boolean checkCardFromAnywhere(Card card, Game game) { + return checkCardFromBattlefield(card, game) || checkCardNotFromBattlefield(card, game); } @Override public void reset() { super.reset(); amountOfCardsThisTurn.clear(); - cardsPutToGraveyardFromBattlefield.clear(); + cardsPutIntoGraveyardFromBattlefield.clear(); + cardsPutIntoGraveyardFromEverywhereElse.clear(); } - } diff --git a/Mage/src/main/java/mage/watchers/common/CastSpellLastTurnWatcher.java b/Mage/src/main/java/mage/watchers/common/CastSpellLastTurnWatcher.java index c2fe4644a87..5a6ec9fbecb 100644 --- a/Mage/src/main/java/mage/watchers/common/CastSpellLastTurnWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/CastSpellLastTurnWatcher.java @@ -16,6 +16,8 @@ public class CastSpellLastTurnWatcher extends Watcher { private final Map amountOfSpellsCastOnPrevTurn = new HashMap<>(); private final Map amountOfSpellsCastOnCurrentTurn = new HashMap<>(); private final List spellsCastThisTurnInOrder = new ArrayList<>(); + private int activePlayerPrevTurnCount = 0; + private int activePlayerThisTurnCount = 0; public CastSpellLastTurnWatcher() { super(WatcherScope.GAME); @@ -29,7 +31,9 @@ public class CastSpellLastTurnWatcher extends Watcher { if (playerId != null) { amountOfSpellsCastOnCurrentTurn.putIfAbsent(playerId, 0); amountOfSpellsCastOnCurrentTurn.compute(playerId, (k, a) -> a + 1); - + } + if (game.isActivePlayer(playerId)) { + activePlayerThisTurnCount++; } } } @@ -41,6 +45,8 @@ public class CastSpellLastTurnWatcher extends Watcher { amountOfSpellsCastOnPrevTurn.putAll(amountOfSpellsCastOnCurrentTurn); amountOfSpellsCastOnCurrentTurn.clear(); spellsCastThisTurnInOrder.clear(); + activePlayerPrevTurnCount = activePlayerThisTurnCount; + activePlayerThisTurnCount = 0; } public Map getAmountOfSpellsCastOnPrevTurn() { @@ -69,4 +75,12 @@ public class CastSpellLastTurnWatcher extends Watcher { } return 0; } + + public int getActivePlayerPrevTurnCount() { + return activePlayerPrevTurnCount; + } + + public int getActivePlayerThisTurnCount() { + return activePlayerThisTurnCount; + } } diff --git a/Mage/src/main/java/mage/watchers/common/ChooseBlockersRedundancyWatcher.java b/Mage/src/main/java/mage/watchers/common/ChooseBlockersRedundancyWatcher.java deleted file mode 100644 index 0b9660699c2..00000000000 --- a/Mage/src/main/java/mage/watchers/common/ChooseBlockersRedundancyWatcher.java +++ /dev/null @@ -1,42 +0,0 @@ -package mage.watchers.common; - -import mage.constants.WatcherScope; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.watchers.Watcher; - -/** - * @author L_J - */ - -public class ChooseBlockersRedundancyWatcher extends Watcher { // workaround for solving timestamp issues regarding "you choose which creatures block and how those creatures block" effects - - public int copyCount = 0; - public int copyCountApply = 0; - - public ChooseBlockersRedundancyWatcher() { - super(WatcherScope.GAME); - } - - @Override - public void reset() { - super.reset(); - copyCount = 0; - copyCountApply = 0; - } - - @Override - public void watch(GameEvent event, Game game) { - } - - public void increment() { - copyCount++; - copyCountApply = copyCount; - } - - public void decrement() { - if (copyCountApply > 0) { - copyCountApply--; - } - } -} diff --git a/Mage/src/main/java/mage/watchers/common/ControlCombatRedundancyWatcher.java b/Mage/src/main/java/mage/watchers/common/ControlCombatRedundancyWatcher.java new file mode 100644 index 00000000000..2d4e163c657 --- /dev/null +++ b/Mage/src/main/java/mage/watchers/common/ControlCombatRedundancyWatcher.java @@ -0,0 +1,78 @@ +package mage.watchers.common; + +import mage.constants.Duration; +import mage.constants.WatcherScope; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.watchers.Watcher; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + * @author L_J + */ +public class ControlCombatRedundancyWatcher extends Watcher { // workaround for solving timestamp issues regarding "you choose which creatures block and how those creatures block" effects + + private static final class PlayerDuration implements java.io.Serializable { // class must be serilizable Bug #8497 + + private final Duration duration; + private final UUID playerId; + + private PlayerDuration(Duration duration, UUID playerId) { + this.duration = duration; + this.playerId = playerId; + } + + private boolean isCombat() { + return duration == Duration.EndOfCombat; + } + + private boolean isPlayer(UUID playerId) { + return playerId.equals(this.playerId); + } + } + + private final List attackingControllers = new ArrayList<>(); + private final List blockingControllers = new ArrayList<>(); + + public ControlCombatRedundancyWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void reset() { + super.reset(); + attackingControllers.clear(); + blockingControllers.clear(); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.END_COMBAT_STEP_POST) { + attackingControllers.removeIf(PlayerDuration::isCombat); + blockingControllers.removeIf(PlayerDuration::isCombat); + } + } + + public static void addAttackingController(UUID playerId, Duration duration, Game game) { + ControlCombatRedundancyWatcher watcher = game.getState().getWatcher(ControlCombatRedundancyWatcher.class); + watcher.attackingControllers.add(0, new PlayerDuration(duration, playerId)); + } + + public static void addBlockingController(UUID playerId, Duration duration, Game game) { + ControlCombatRedundancyWatcher watcher = game.getState().getWatcher(ControlCombatRedundancyWatcher.class); + watcher.blockingControllers.add(0, new PlayerDuration(duration, playerId)); + } + + public static boolean checkAttackingController(UUID playerId, Game game) { + ControlCombatRedundancyWatcher watcher = game.getState().getWatcher(ControlCombatRedundancyWatcher.class); + return !watcher.attackingControllers.isEmpty() && watcher.attackingControllers.get(0).isPlayer(playerId); + } + + public static boolean checkBlockingController(UUID playerId, Game game) { + ControlCombatRedundancyWatcher watcher = game.getState().getWatcher(ControlCombatRedundancyWatcher.class); + return !watcher.blockingControllers.isEmpty() && watcher.blockingControllers.get(0).isPlayer(playerId); + } +} diff --git a/Mage/src/main/java/mage/watchers/common/ControlledModifiedCreatureAsSpellCastWatcher.java b/Mage/src/main/java/mage/watchers/common/ControlledModifiedCreatureAsSpellCastWatcher.java new file mode 100644 index 00000000000..e0d6112a228 --- /dev/null +++ b/Mage/src/main/java/mage/watchers/common/ControlledModifiedCreatureAsSpellCastWatcher.java @@ -0,0 +1,52 @@ +package mage.watchers.common; + +import mage.MageObjectReference; +import mage.constants.WatcherScope; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.ModifiedPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.stack.Spell; +import mage.watchers.Watcher; + +import java.util.HashMap; + +/** + * + * @author weirddan455 + */ +public class ControlledModifiedCreatureAsSpellCastWatcher extends Watcher { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); + + static { + filter.add(ModifiedPredicate.instance); + } + + public ControlledModifiedCreatureAsSpellCastWatcher() { + super(WatcherScope.GAME); + } + + private final HashMap modifiedCreaturesWhenCast = new HashMap<>(); + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.SPELL_CAST) { + Spell spell = game.getSpell(event.getTargetId()); + if (spell != null) { + MageObjectReference mor = new MageObjectReference(spell, game); + modifiedCreaturesWhenCast.put(mor, game.getBattlefield().countAll(filter, spell.getControllerId(), game)); + } + } + } + + @Override + public void reset() { + super.reset(); + modifiedCreaturesWhenCast.clear(); + } + + public int getModifiedCreatureCount(MageObjectReference mor) { + return modifiedCreaturesWhenCast.getOrDefault(mor, 0); + } +} diff --git a/Mage/src/main/java/mage/watchers/common/DamagedByControlledWatcher.java b/Mage/src/main/java/mage/watchers/common/DamagedByControlledWatcher.java new file mode 100644 index 00000000000..cfc8b28461b --- /dev/null +++ b/Mage/src/main/java/mage/watchers/common/DamagedByControlledWatcher.java @@ -0,0 +1,45 @@ +package mage.watchers.common; + +import mage.MageObjectReference; +import mage.constants.WatcherScope; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.watchers.Watcher; + +import java.util.HashSet; + +/** + * + * @author weirddan455 + */ +public class DamagedByControlledWatcher extends Watcher { + + private final HashSet damagedPermanents = new HashSet<>(); + + public DamagedByControlledWatcher() { + super(WatcherScope.PLAYER); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.DAMAGED_PERMANENT) { + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent != null && permanent.isCreature(game)) { + if (controllerId != null && controllerId.equals(game.getControllerId(event.getSourceId()))) { + damagedPermanents.add(new MageObjectReference(event.getTargetId(), game)); + } + } + } + } + + @Override + public void reset() { + super.reset(); + damagedPermanents.clear(); + } + + public boolean wasDamaged(Permanent permanent, Game game) { + return damagedPermanents.contains(new MageObjectReference(permanent, game)); + } +} diff --git a/Mage/src/main/java/mage/watchers/common/DiscardedCardWatcher.java b/Mage/src/main/java/mage/watchers/common/DiscardedCardWatcher.java index a6483f67819..692295b655e 100644 --- a/Mage/src/main/java/mage/watchers/common/DiscardedCardWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/DiscardedCardWatcher.java @@ -3,6 +3,7 @@ package mage.watchers.common; import mage.constants.WatcherScope; import mage.game.Game; import mage.game.events.GameEvent; +import mage.util.CardUtil; import mage.watchers.Watcher; import java.util.HashMap; @@ -23,7 +24,7 @@ public class DiscardedCardWatcher extends Watcher { @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.DISCARDED_CARD) { - playerMap.compute(event.getPlayerId(), (u, i) -> i == null ? 1 : Integer.sum(i, 1)); + playerMap.compute(event.getPlayerId(), CardUtil::setOrIncrementValue); } } diff --git a/Mage/src/main/java/mage/watchers/common/LostControlWatcher.java b/Mage/src/main/java/mage/watchers/common/LostControlWatcher.java deleted file mode 100644 index 86f8cf26ddb..00000000000 --- a/Mage/src/main/java/mage/watchers/common/LostControlWatcher.java +++ /dev/null @@ -1,39 +0,0 @@ -package mage.watchers.common; - -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; -import mage.constants.WatcherScope; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.watchers.Watcher; - -/** - * - * @author LevelX2 - */ -public class LostControlWatcher extends Watcher { - - private final Map lastLostControl = new HashMap<>(); - - public LostControlWatcher() { - super(WatcherScope.GAME); - } - - @Override - public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.LOST_CONTROL) { - lastLostControl.put(event.getTargetId(), System.currentTimeMillis()); - } - } - - @Override - public void reset() { - super.reset(); - lastLostControl.clear(); - } - - public long getOrderOfLastLostControl(UUID sourceId) { - return lastLostControl.getOrDefault(sourceId, new Long(0)); - } -} diff --git a/Mage/src/test/java/mage/StaticFilterTest.java b/Mage/src/test/java/mage/StaticFilterTest.java new file mode 100644 index 00000000000..38c7c18fca7 --- /dev/null +++ b/Mage/src/test/java/mage/StaticFilterTest.java @@ -0,0 +1,50 @@ +package mage; + +import mage.filter.Filter; +import mage.filter.StaticFilters; +import org.junit.Assert; +import org.junit.Test; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * @author TheElk801 + */ +public class StaticFilterTest { + + @Test + public void testStaticFilters() throws IllegalAccessException { + List errors = new ArrayList<>(); + for (Field field : StaticFilters.class.getFields()) { + if (!Modifier.isPublic(field.getModifiers())) { + errors.add("Field must be public: " + field); + } + if (!Modifier.isStatic(field.getModifiers())) { + errors.add("Field must be static: " + field); + } + if (!Modifier.isFinal(field.getModifiers())) { + errors.add("Field must be final: " + field); + } + if (!field.getName().startsWith("FILTER_")) { + errors.add("Field name must start with \"FILTER_\""); + } + if (!Objects.equals(field.getName().toUpperCase(), field.getName())) { + errors.add("Field name must be all upper case letters"); + } + Object filter = field.get(field.getType()); + if (!(filter instanceof Filter)) { + errors.add("Field must be a filter: " + field); + } else if (!((Filter) filter).isLockedFilter()) { + errors.add("Field must be locked: " + field); + } + } + if (errors.size() > 0) { + errors.stream().forEach(System.out::println); + } + Assert.assertFalse("There were errors found in Static Filters", errors.size() > 0); + } +} diff --git a/Utils/cardClass.tmpl b/Utils/cardClass.tmpl index 9d165a4cd2d..da22cf54057 100644 --- a/Utils/cardClass.tmpl +++ b/Utils/cardClass.tmpl @@ -3,7 +3,6 @@ package mage.cards.[=$cardNameFirstLetter=]; import java.util.UUID;[= if ($power || $power eq 0) { if ($planeswalker eq 'true') { - $OUT .= "\nimport mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility;" }else { $OUT .= "\nimport mage.MageInt;" } @@ -30,7 +29,7 @@ public final class [=$className=] extends CardImpl { [=$subType=][=$colors=][= if ($power || $power eq 0) { if ($planeswalker eq 'true') { - $OUT .= "\n this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility($power));"; + $OUT .= "\n this.setStartingLoyalty($power);"; } else { $OUT .= "\n this.power = new MageInt($power);"; $OUT .= "\n this.toughness = new MageInt($toughness);"; diff --git a/Utils/gen-card.pl b/Utils/gen-card.pl index 739b0b0fe9e..582ce382030 100755 --- a/Utils/gen-card.pl +++ b/Utils/gen-card.pl @@ -282,9 +282,7 @@ foreach my $ability (@abilities) { } $vars{'abilities'} .= "\n this.getSpellAbility().addTarget(auraTarget);"; $vars{'abilities'} .= "\n this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature));"; - $vars{'abilities'} .= "\n Ability ability = new EnchantAbility(auraTarget.getTargetName());"; - $vars{'abilities'} .= "\n this.addAbility(ability);"; - $vars{'abilitiesImports'} .= "\nimport mage.abilities.Ability;"; + $vars{'abilities'} .= "\n this.addAbility(new EnchantAbility(auraTarget.getTargetName()));"; $vars{'abilitiesImports'} .= "\nimport mage.abilities.effects.common.AttachEffect;"; $vars{'abilitiesImports'} .= "\nimport mage.constants.Outcome;"; $vars{'abilitiesImports'} .= "\nimport mage.target.TargetPermanent;"; diff --git a/Utils/keywords.txt b/Utils/keywords.txt index 617997e4f49..065d96fea20 100644 --- a/Utils/keywords.txt +++ b/Utils/keywords.txt @@ -11,6 +11,8 @@ Bushido|number| Buyback|manaString| Cascade|new| Changeling|new| +Cleave|card, manaString| +Compleated|instance| Convoke|new| Crew|number| Cumulative upkeep|cost| @@ -48,6 +50,7 @@ Flying|instance| Forestcycling|cost| Forestwalk|new| Foretell|card, manaString| +Friends forever|instance| Haste|instance| Hexproof|instance| Improvise|new| @@ -74,7 +77,7 @@ Morph|card, cost| Mutate|card, manaString| Myriad|new| Nightbound|new| -Ninjutsu|cost| +Ninjutsu|manaString| Outlast|cost| Partner|instance| Persist|new| @@ -86,13 +89,14 @@ Provoke|new| Prowess|new| Reach|instance| Rebound|new| +Reconfigure|manaString| Renown|number| Replicate|card, manaString| Riot|new| Scavenge|cost| Shadow|instance| Shroud|instance| -Soulbond|instance| +Soulbond|new| Soulshift|number| Skulk|new| Spectacle|card, cost| @@ -102,6 +106,7 @@ Suspend|number, cost, card| Swampcycling|cost| Swampwalk|new| Totem armor|new| +Training|new| Trample|instance| Tribute|number| Unblockable|new| diff --git a/Utils/known-sets.txt b/Utils/known-sets.txt index 559a63a5863..3c1f24584a1 100644 --- a/Utils/known-sets.txt +++ b/Utils/known-sets.txt @@ -1,3 +1,4 @@ +Alchemy: Innistrad|AlchemyInnistrad| Adventures in the Forgotten Realms|AdventuresInTheForgottenRealms| Forgotten Realms Commander|ForgottenRealmsCommander| Aether Revolt|AetherRevolt| @@ -115,6 +116,8 @@ Ikoria: Lair of Behemoths|IkoriaLairOfBehemoths| Innistrad|Innistrad| Innistrad: Midnight Hunt|InnistradMidnightHunt| Midnight Hunt Commander|MidnightHuntCommander| +Innistrad: Crimson Vow|InnistradCrimsonVow| +Crimson Vow Commander|CrimsonVowCommander| Invasion|Invasion| Ixalan|Ixalan| Journey into Nyx|JourneyIntoNyx| @@ -125,6 +128,8 @@ Jumpstart: Historic Horizons|JumpstartHistoricHorizons| Kaladesh|Kaladesh| Kaldheim|Kaldheim| Kaldheim Commander|KaldheimCommander| +Kamigawa: Neon Dynasty|KamigawaNeonDynasty| +Neon Dynasty Commander|NeonDynastyCommander| Khans of Tarkir|KhansOfTarkir| Launch Party|LaunchParty| Legends|Legends| @@ -196,6 +201,7 @@ Shards of Alara|ShardsOfAlara| Starter 1999|Starter1999| Starter 2000|Starter2000| Star Wars|StarWars| +Streets of New Capenna|StreetsOfNewCapenna| Strixhaven: School of Mages|StrixhavenSchoolOfMages| Stronghold|Stronghold| Super Series|SuperSeries| @@ -212,6 +218,7 @@ Torment|Torment| Ugin's Fate|UginsFate| Unglued|Unglued| Unhinged|Unhinged| +Unfinity|Unfinity| Unlimited Edition|UnlimitedEdition| Unstable|Unstable| Urza's Destiny|UrzasDestiny| diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt index a0457dc5f02..c929f70028f 100644 --- a/Utils/mtg-cards-data.txt +++ b/Utils/mtg-cards-data.txt @@ -42492,25 +42492,25 @@ Island|Innistrad: Midnight Hunt|270|C||Basic Land - Island|||({T}: Add {U}.)| Swamp|Innistrad: Midnight Hunt|272|C||Basic Land - Swamp|||({T}: Add {B}.)| Mountain|Innistrad: Midnight Hunt|274|C||Basic Land - Mountain|||({T}: Add {R}.)| Forest|Innistrad: Midnight Hunt|276|C||Basic Land - Forest|||({T}: Add {G}.)| -Faceless Agent|Jumpstart: Historic Horizons|1|C|{3}|Creature - Shapeshifter|2|1|Changeling$When Faceless Agent enters the battlefield, seek a creature card of the most prevalent creature type in your library.| +Faceless Agent|Jumpstart: Historic Horizons|1|C|{3}|Creature - Shapeshifter|2|2|Changeling$When Faceless Agent enters the battlefield, seek a creature card of the most prevalent creature type in your library.| Baffling Defenses|Jumpstart: Historic Horizons|2|C|{1}{W}|Instant|||Target creature's base power perpetually becomes 0.| Benalish Partisan|Jumpstart: Historic Horizons|3|R|{1}{W}|Creature - Human Soldier|1|2|Lifelink$Whenever you cycle another card, you may pay {1}{W}. If you do, return Benalish Partisan from your graveyard to the battlefield tapped and it perpetually gets +1/+0.$Cycling {1}{W}| Leonin Sanctifier|Jumpstart: Historic Horizons|4|C|{1}{W}|Creature - Cat Cleric|2|1|Lifelink$When Leonin Sanctifier enters the battlefield, choose a creature card in your hand. It perpetually gains lifelink.| Lumbering Lightshield|Jumpstart: Historic Horizons|5|C|{1}{W}|Creature - Illusion|1|4|When Lumbering Lightshield enters the battlefield, target opponent reveals a nonland card at random from their hand. It perpetually gains "This spell costs {1} more to cast."| Teyo, Aegis Adept|Jumpstart: Historic Horizons|6|M|{2}{W}{W}|Legendary Planeswalker - Teyo|4|+1: Up to one target creature's base power perpetually becomes equal to its toughness. It perpetually gains "This creature can attack as though it didn't have defender."$−2: Conjure a Lumbering Lightshield card onto the battlefield.$−6: You get an emblem with "At the beginning of your end step, return target white creature card from your graveyard to the battlefield. You gain life equal to its toughness."| Wingsteed Trainer|Jumpstart: Historic Horizons|7|U|{3}{W}|Creature - Human|2|3|When Wingsteed Trainer enters the battlefield or attacks, conjure a Stormfront Pegasus card into your hand.| -Bounty of the Deep|Jumpstart: Historic Horizons|8|U|{2}{U}|Sorcery|||If you have no land cards in your hand, seek a land card and a nonland card. Otherwise, seek two nonland cards.| +Bounty of the Deep|Jumpstart: Historic Horizons|8|C|{2}{U}|Sorcery|||If you have no land cards in your hand, seek a land card and a nonland card. Otherwise, seek two nonland cards.| Ethereal Grasp|Jumpstart: Historic Horizons|9|C|{2}{U}|Instant|||Tap target creature. That creature perpetually gains "This creature doesn't untap during your untap step" and "{8}: Untap this creature."| Kiora, the Tide's Fury|Jumpstart: Historic Horizons|10|M|{3}{U}|Legendary Planeswalker - Kiora|4|+1: Conjure a Kraken Hatchling into your hand.$+1: Untap target creature or land. Prevent all damage that would be dealt to and dealt by that permanent until your next turn.$−3: You may sacrifice a Kraken. If you do, create a 8/8 blue Kraken creature token.| Mentor of Evos Isle|Jumpstart: Historic Horizons|11|C|{2}{U}|Creature - Bird Wizard|2|1|Flying$When Mentor of Evos Isle enters the battlefield, choose a creature card in your hand. It perpetually gains flying.| Shoreline Scout|Jumpstart: Historic Horizons|12|U|{U}|Creature - Merfolk Scout|1|1|When Shoreline Scout enters the battlefield, you may exile a Merfolk card or a land card from your hand. If you do, conjure a Tropical Island card into your hand.$As long as another Merfolk or an Island entered the battlefield under your control this turn, Shoreline Scout gets +1/+0.| Tome of the Infinite|Jumpstart: Historic Horizons|13|R|{2}{U}|Legendary Artifact|||{U}, {T}: Conjure a random card from Tome of the Infinite's spellbook into your hand. It perpetually gains "You may spend mana as though it were mana of any color to cast this spell."| Boneyard Aberration|Jumpstart: Historic Horizons|14|U|{4}{B}|Creature - Skeleton Dog|3|3|When Boneyard Aberration dies, exile it. If you do, conjure three Reassembling Skeleton cards into your graveyard.| -Davriel, Soul Broker|Jumpstart: Historic Horizons|15|M|{2}{B}{B}|Legendary Planeswalker - Davriel|4|+1: Until your next turn, whenever an opponent attacks you and/or planeswalkers you control, they discard a card. If they can't, they sacrifice an attacking creature.$−2: Accept one of Davriel's offers, then accept one of Davriel's conditions.$−3: Target creature perpetually gets -3/-3.| -Davriel's Withering|Jumpstart: Historic Horizons|16|C|{B}|Instant|||Target creature perpetually gets -1/-2.| +Davriel, Soul Broker|Jumpstart: Historic Horizons|15|M|{2}{B}{B}|Legendary Planeswalker - Davriel|4|+1: Until your next turn, whenever an opponent attacks you and/or planeswalkers you control, they discard a card. If they can't, they sacrifice an attacking creature.$−2: Accept one of Davriel's offers, then accept one of Davriel's conditions.$−3: Target creature an opponent controls perpetually gets -3/-3.| +Davriel's Withering|Jumpstart: Historic Horizons|16|C|{B}|Instant|||Target creature an opponent controls perpetually gets -1/-2.| Manor Guardian|Jumpstart: Historic Horizons|17|U|{2}{B}|Creature - Demon|4|3|When Manor Guardian dies, each player seeks a nonland card with mana value 2 or less.| Plaguecrafter's Familiar|Jumpstart: Historic Horizons|18|C|{1}{B}|Creature - Rat|1|1|Deathtouch$When Plaguecrafter's Familiar enters the battlefield, choose a creature card in your hand. It perpetually gains deathtouch.| -Subversive Acolyte|Jumpstart: Historic Horizons|19|R|{B}{B}|Creature - Human|2|2|{2}, Pay 2 life: Choose one. Activate only once.$• Subversive Acolyte becomes a Human Cleric. It gets +1/+2 and gains lifelink.$• Subversive Acolyte becomes a Phyrexian. It gets +3/+3 and gains trample and "Whenever this creature is dealt damage, sacrifice that many permanents."| +Subversive Acolyte|Jumpstart: Historic Horizons|19|R|{1}{B}|Creature - Human|2|3|{2}, Pay 2 life: Choose one. Activate only once.$• Subversive Acolyte becomes a Human Cleric. It gets +1/+1 and gains lifelink.$• Subversive Acolyte becomes a Phyrexian. It gets +3/+2 and gains trample and "Whenever this creature is dealt damage, sacrifice that many permanents."| Managorger Phoenix|Jumpstart: Historic Horizons|20|R|{R}{R}|Creature - Phoenix|2|2|Flying$Managorger Phoenix can't block.$Whenever you cast a spell, if Managorger Phoenix is in your graveyard, put a flame counter on Managorger Phoenix for each {R} in that spell's mana cost. If Managorger Phoenix has five or more flame counters on it, return it to the battlefield and it perpetually gets +1/+1.| Reckless Ringleader|Jumpstart: Historic Horizons|21|C|{R}|Creature - Goblin Rogue|1|1|Haste$When Reckless Ringleader enters the battlefield, choose a creature card in your hand. It perpetually gains haste.| Sarkhan's Scorn|Jumpstart: Historic Horizons|22|C|{2}{R}|Instant|||Sarkhan's Scorn deals damage equal to the number of turns you have begun to target creature or planeswalker.| @@ -42761,6 +42761,7 @@ Bear Cub|Jumpstart: Historic Horizons|546|C|{1}{G}|Creature - Bear|2|2|| Bestial Menace|Jumpstart: Historic Horizons|547|U|{3}{G}{G}|Sorcery|||Create a 1/1 green Snake creature token, a 2/2 green Wolf creature token, and a 3/3 green Elephant creature token.| Chatterfang, Squirrel General|Jumpstart: Historic Horizons|552|M|{2}{G}|Legendary Creature - Squirrel Warrior|3|3|Forestwalk$If one or more tokens would be created under your control, those tokens plus that many 1/1 green Squirrel creature tokens are created instead.${B}, Sacrifice X Squirrels: Target creature gets +X/-X until end of turn.| Chatter of the Squirrel|Jumpstart: Historic Horizons|553|C|{G}|Sorcery|||Create a 1/1 green Squirrel creature token.$Flashback {1}{G}| +Chatterstorm|Jumpstart: Historic Horizons|554|C|{1}{G}|Sorcery|||Create a 1/1 green Squirrel creature token.$Storm| Chitterspitter|Jumpstart: Historic Horizons|555|R|{2}{G}|Artifact|||At the beginning of your upkeep, you may sacrifice a token. If you do, put an acorn counter on Chitterspitter.$Squirrels you control get +1/+1 for each acorn counter on Chitterspitter.${G}, {T}: Create a 1/1 green Squirrel creature token.| Crocanura|Jumpstart: Historic Horizons|557|C|{2}{G}|Creature - Crocodile Frog|1|3|Reach$Evolve| Deepwood Denizen|Jumpstart: Historic Horizons|558|C|{2}{G}|Creature - Elf Warrior|3|2|Vigilance${5}{G}, {T}: Draw a card. This ability costs {1} less to activate for each +1/+1 counter on creatures you control.| @@ -43014,3 +43015,916 @@ Temple of Deceit|Midnight Hunt Commander|184|R||Land|||Temple of Deceit enters t Temple of Plenty|Midnight Hunt Commander|185|R||Land|||Temple of Plenty enters the battlefield tapped.$When Temple of Plenty enters the battlefield, scry 1.${T}: Add {G} or {W}.| Temple of the False God|Midnight Hunt Commander|186|U||Land|||{T}: Add {C}{C}. Activate only if you control five or more lands.| Unclaimed Territory|Midnight Hunt Commander|187|U||Land|||As Unclaimed Territory enters the battlefield, choose a creature type.${T}: Add {C}.${T}: Add one mana of any color. Spend this mana only to cast a creature spell of the chosen type.| +Adamant Will|Innistrad: Crimson Vow|1|C|{1}{W}|Instant|||Target creature gets +2/+2 and gains indestructible until end of turn.| +Angelic Quartermaster|Innistrad: Crimson Vow|2|U|{3}{W}{W}|Creature - Angel Soldier|3|3|Flying$When Angelic Quartermaster enters the battlefield, put a +1/+1 counter on each of up to two other target creatures.| +Arm the Cathars|Innistrad: Crimson Vow|3|U|{1}{W}{W}|Sorcery|||Until end of turn, target creature gets +3/+3, up to one other target creature gets +2/+2, and up to one other target creature gets +1/+1. Those creatures gain vigilance until end of turn.| +Bride's Gown|Innistrad: Crimson Vow|4|U|{1}{W}|Artifact - Equipment|||Equipped creature gets +2/+0. It gets an additional +0/+2 and has first strike as long as an Equipment named Groom's Finery is attached to a creature you control.$Equip {2}| +By Invitation Only|Innistrad: Crimson Vow|5|R|{3}{W}{W}|Sorcery|||Choose a number between 0 and 13. Each player sacrifices that many creatures.| +Cemetery Protector|Innistrad: Crimson Vow|6|M|{2}{W}{W}|Creature - Human Soldier|3|4|Flash$When Cemetery Protector enters the battlefield, exile a card from a graveyard.$Whenever you play a land or cast a spell, if it shares a card type with the exiled card, create a 1/1 white Human creature token.| +Circle of Confinement|Innistrad: Crimson Vow|7|U|{1}{W}|Enchantment|||When Circle of Confinement enters the battlefield, exile target creature an opponent controls with mana value 3 or less until Circle of Confinement leaves the battlefield.$Whenever an opponent casts a Vampire spell with the same name as a card exiled with Circle of Confinement, you gain 2 life.| +Dawnhart Geist|Innistrad: Crimson Vow|8|U|{1}{W}|Creature - Spirit Warlock|1|3|Whenever you cast an enchantment spell, you gain 2 life.| +Distracting Geist|Innistrad: Crimson Vow|9|U|{2}{W}|Creature - Spirit|2|1|Whenever Distracting Geist attacks, tap target creature defending player controls.$Disturb {4}{W}| +Clever Distraction|Innistrad: Crimson Vow|9|U||Enchantment - Aura|||Enchant creature$Enchanted creature has "Whenever this creature attacks, tap target creature defending player controls."$If Clever Distraction would be put into a graveyard from anywhere, exile it instead.| +Drogskol Infantry|Innistrad: Crimson Vow|10|C|{1}{W}|Creature - Spirit Soldier|2|2|Disturb {3}{W}| +Drogskol Armaments|Innistrad: Crimson Vow|10|C||Enchantment - Aura|||Enchant creature$Enchanted creature gets +2/+2.$If Drogskol Armaments would be put into a graveyard from anywhere, exile it instead.| +Estwald Shieldbasher|Innistrad: Crimson Vow|11|C|{3}{W}|Creature - Human Soldier|4|2|Whenever Estwald Shieldbasher attacks, you may pay {1}. If you do, it gains indestructible until end of turn.| +Faithbound Judge|Innistrad: Crimson Vow|12|M|{1}{W}{W}|Creature - Spirit Soldier|4|4|Defender, flying, vigilance$At the beginning of your upkeep, if Faithbound Judge has two or fewer judgment counters on it, put a judgment counter on it.$As long as Faithbound Judge has three or more judgment counters on it, it can attack as though it didn't have defender.$Disturb {5}{W}{W}| +Sinner's Judgment|Innistrad: Crimson Vow|12|M||Enchantment - Aura Curse|||Enchant player$At the beginning of your upkeep, put a judgment counter on Sinner's Judgment. Then if there are three or more judgment counters on it, enchanted player loses the game.$If Sinner's Judgment would be put into a graveyard from anywhere, exile it instead.| +Fierce Retribution|Innistrad: Crimson Vow|13|C|{1}{W}|Instant|||Cleave {5}{W}$Destroy target [attacking] creature.| +Fleeting Spirit|Innistrad: Crimson Vow|14|U|{1}{W}|Creature - Spirit|3|1|{W}, Exile three cards from your graveyard: Fleeting Spirit gains first strike until end of turn.$Discard a card: Exile Fleeting Spirit. Return it to the battlefield under its owner's control at the beginning of the next end step.| +Gryff Rider|Innistrad: Crimson Vow|15|C|{2}{W}|Creature - Human Knight|2|1|Flying$Training| +Gryffwing Cavalry|Innistrad: Crimson Vow|16|U|{3}{W}|Creature - Human Knight|2|2|Flying$Training$Whenever Gryffwing Cavalry attacks, you may pay {1}{W}. If you do, target attacking creature without flying gains flying until end of turn.| +Hallowed Haunting|Innistrad: Crimson Vow|17|M|{2}{W}{W}|Enchantment|||As long as you control seven or more enchantments, creatures you control have flying and vigilance.$Whenever you cast an enchantment spell, create a white Spirit Cleric creature token with "This creature's power and toughness are each equal to the number of Spirits you control."| +Heron of Hope|Innistrad: Crimson Vow|18|C|{3}{W}|Creature - Bird|2|3|Flying$If you would gain life, you gain that much life plus 1 instead.${1}{W}: Heron of Hope gains lifelink until end of turn.| +Heron-Blessed Geist|Innistrad: Crimson Vow|19|C|{4}{W}|Creature - Spirit|3|3|Flying${3}{W}, Exile Heron-Blessed Geist from your graveyard: Create two 1/1 white Spirit creature tokens with flying. Activate only if you control an enchantment and only as a sorcery.| +Hopeful Initiate|Innistrad: Crimson Vow|20|R|{W}|Creature - Human Warlock|1|2|Training${2}{W}, Remove two +1/+1 counters from among creatures you control: Destroy target artifact or enchantment.| +Katilda, Dawnhart Martyr|Innistrad: Crimson Vow|21|R|{1}{W}{W}|Legendary Creature - Spirit Warlock|*|*|Flying, lifelink, protection from Vampires$Katilda, Dawnhart Martyr's power and toughness are each equal to the number of permanents you control that are Spirits and/or enchantments.$Disturb {3}{W}{W}| +Katilda's Rising Dawn|Innistrad: Crimson Vow|21|R||Legendary Enchantment - Aura|||Enchant creature$Enchanted creature has flying, lifelink, and protection from Vampires, and it gets +X/+X, where X is the number of permanents you control that are Spirits and/or enchantments.$If Katilda's Rising Dawn would be put into a graveyard from anywhere, exile it instead.| +Kindly Ancestor|Innistrad: Crimson Vow|22|C|{2}{W}|Creature - Spirit|2|3|Lifelink$Disturb {1}{W}| +Ancestor's Embrace|Innistrad: Crimson Vow|22|C||Enchantment - Aura|||Enchant creature$Enchanted creature has lifelink.$If Ancestor's Embrace would be put into a graveyard from anywhere, exile it instead.| +Lantern Flare|Innistrad: Crimson Vow|23|R|{1}{W}|Instant|||Cleave {X}{R}{W}$Lantern Flare deals X damage to target creature or planeswalker and you gain X life. [X is the number of creatures you control.]| +Militia Rallier|Innistrad: Crimson Vow|24|C|{2}{W}|Creature - Human Soldier|3|3|Militia Rallier can't attack alone.$Whenever Militia Rallier attacks, untap target creature.| +Nebelgast Beguiler|Innistrad: Crimson Vow|25|C|{4}{W}|Creature - Spirit|2|5|{W}, {T}: Tap target creature.| +Nurturing Presence|Innistrad: Crimson Vow|26|C|{1}{W}|Enchantment - Aura|||Enchant creature$Enchanted creature has "Whenever a creature enters the battlefield under your control, this creature gets +1/+1 until end of turn."$When Nurturing Presence enters the battlefield, create a 1/1 white Spirit creature token with flying.| +Ollenbock Escort|Innistrad: Crimson Vow|27|U|{W}|Creature - Human Cleric|1|1|Vigilance$Sacrifice Ollenbock Escort: Target creature you control with a +1/+1 counter on it gains lifelink and indestructible until end of turn.| +Panicked Bystander|Innistrad: Crimson Vow|28|U|{1}{W}|Creature - Human Peasant|2|2|Whenever Panicked Bystander or another creature you control dies, you gain 1 life.$At the beginning of your end step, if you gained 3 or more life this turn, transform Panicked Bystander.| +Cackling Culprit|Innistrad: Crimson Vow|28|U||Creature - Human Rogue|3|5|Whenever Cackling Culprit or another creature you control dies, you gain 1 life.${1}{B}: Cackling Culprit gains deathtouch until end of turn.| +Parish-Blade Trainee|Innistrad: Crimson Vow|29|C|{1}{W}|Creature - Human Soldier|1|2|Training$When Parish-Blade Trainee dies, put its counters on target creature you control.| +Piercing Light|Innistrad: Crimson Vow|30|C|{W}|Instant|||Piercing Light deals 2 damage to target attacking or blocking creature. Scry 1.| +Radiant Grace|Innistrad: Crimson Vow|31|U|{W}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +1/+0 and has vigilance.$When enchanted creature dies, return Radiant Grace to the battlefield transformed under your control attached to target opponent.| +Radiant Restraints|Innistrad: Crimson Vow|31|U||Enchantment - Aura Curse|||Enchant player$Creatures enchanted player controls enter the battlefield tapped.| +Resistance Squad|Innistrad: Crimson Vow|32|U|{2}{W}|Creature - Human Soldier|3|2|When Resistance Squad enters the battlefield, if you control another Human, draw a card.| +Sanctify|Innistrad: Crimson Vow|33|C|{1}{W}|Sorcery|||Destroy target artifact or enchantment. You gain 3 life.| +Savior of Ollenbock|Innistrad: Crimson Vow|34|M|{1}{W}{W}|Creature - Human Soldier|1|2|Training$Whenever Savior of Ollenbock trains, exile up to one other target creature from the battlefield or creature card from a graveyard.$When Savior of Ollenbock leaves the battlefield, put the exiled cards onto the battlefield under their owners' control.| +Sigarda's Imprisonment|Innistrad: Crimson Vow|35|C|{2}{W}|Enchantment - Aura|||Enchant creature$Enchanted creature can't attack or block.${4}{W}: Exile enchanted creature. Create a Blood token.| +Sigarda's Summons|Innistrad: Crimson Vow|36|R|{4}{W}{W}|Enchantment|||Creatures you control with +1/+1 counters on them have base power and toughness 4/4, have flying, and are Angels in addition to their other types.| +Supernatural Rescue|Innistrad: Crimson Vow|37|C|{3}{W}|Enchantment - Aura|||This spell has flash as long as you control a Spirit.$When you cast this spell, tap up to two target creatures you don't control.$Enchant creature you control$Enchanted creature gets +1/+2.| +Thalia, Guardian of Thraben|Innistrad: Crimson Vow|38|R|{1}{W}|Legendary Creature - Human Soldier|2|1|First strike$Noncreature spells cost {1} more to cast.| +Traveling Minister|Innistrad: Crimson Vow|39|C|{W}|Creature - Human Cleric|1|1|{T}: Target creature gets +1/+0 until end of turn. You gain 1 life. Activate only as a sorcery.| +Twinblade Geist|Innistrad: Crimson Vow|40|U|{1}{W}|Creature - Spirit Warrior|1|1|Double strike$Disturb {2}{W}| +Twinblade Invocation|Innistrad: Crimson Vow|40|U||Enchantment - Aura|||Enchant creature$Enchanted creature has double strike.$If Twinblade Invocation would be put into a graveyard from anywhere, exile it instead.| +Unholy Officiant|Innistrad: Crimson Vow|41|C|{W}|Creature - Vampire Cleric|1|2|Vigilance${4}{W}: Put a +1/+1 counter on Unholy Officiant.| +Valorous Stance|Innistrad: Crimson Vow|42|U|{1}{W}|Instant|||Choose one —$• Target creature gains indestructible until end of turn.$• Destroy target creature with toughness 4 or greater.| +Vampire Slayer|Innistrad: Crimson Vow|43|C|{1}{W}|Creature - Human Soldier|2|2|Whenever Vampire Slayer deals damage to a Vampire, destroy that creature.| +Voice of the Blessed|Innistrad: Crimson Vow|44|R|{W}{W}|Creature - Spirit Cleric|2|2|Whenever you gain life, put a +1/+1 counter on Voice of the Blessed.$As long as Voice of the Blessed has four or more +1/+1 counters on it, it has flying and vigilance.$As long as Voice of the Blessed has ten or more +1/+1 counters on it, it has indestructible.| +Wedding Announcement|Innistrad: Crimson Vow|45|R|{2}{W}|Enchantment|||At the beginning of your end step, put an invitation counter on Wedding Announcement. If you attacked with two or more creatures this turn, draw a card. Otherwise, create a 1/1 white Human creature token. Then if Wedding Announcement has three or more invitation counters on it, transform it.| +Wedding Festivity|Innistrad: Crimson Vow|45|R||Enchantment|||Creatures you control get +1/+1.| +Welcoming Vampire|Innistrad: Crimson Vow|46|R|{2}{W}|Creature - Vampire|2|3|Flying$Whenever one or more other creatures with power 2 or less enter the battlefield under your control, draw a card. This ability triggers only once each turn.| +Alchemist's Retrieval|Innistrad: Crimson Vow|47|C|{U}|Instant|||Cleave {1}{U}$Return target nonland permanent [you control] to its owner's hand.| +Binding Geist|Innistrad: Crimson Vow|48|C|{2}{U}|Creature - Spirit|3|1|Whenever Binding Geist attacks, target creature an opponent controls gets -2/-0 until end of turn.$Disturb {1}{U}| +Spectral Binding|Innistrad: Crimson Vow|48|C||Enchantment - Aura|||Enchant creature$Enchanted creature gets -2/-0.$If Spectral Binding would be put into a graveyard from anywhere, exile it instead.| +Biolume Egg|Innistrad: Crimson Vow|49|U|{2}{U}|Creature - Serpent Egg|0|4|Defender$When Biolume Egg enters the battlefield, scry 2.$When you sacrifice Biolume Egg, return it to the battlefield transformed under its owner's control at the beginning of the next end step.| +Biolume Serpent|Innistrad: Crimson Vow|49|U||Creature - Serpent|4|4|Sacrifice two Islands: Biolume Serpent can't be blocked this turn.| +Cemetery Illuminator|Innistrad: Crimson Vow|50|M|{1}{U}{U}|Creature - Spirit|2|3|Flying$Whenever Cemetery Illuminator enters the battlefield or attacks, exile a card from a graveyard.$You may look at the top card of your library any time.$Once each turn, you may cast a spell from the top of your library if it shares a card type with a card exiled with Cemetery Illuminator.| +Chill of the Grave|Innistrad: Crimson Vow|51|C|{2}{U}|Instant|||This spell costs {1} less to cast if you control a Zombie.$Tap target creature. It doesn't untap during its controller's next untap step.$Draw a card.| +Cobbled Lancer|Innistrad: Crimson Vow|52|U|{U}|Creature - Zombie Horse|3|3|As an additional cost to cast this spell, exile a creature card from your graveyard.${3}{U}, Exile Cobbled Lancer from your graveyard: Draw a card.| +Consuming Tide|Innistrad: Crimson Vow|53|R|{2}{U}{U}|Sorcery|||Each player chooses a nonland permanent they control. Return all nonland permanents not chosen this way to their owners' hands. Then you draw a card for each opponent who has more cards in their hand than you.| +Cradle of Safety|Innistrad: Crimson Vow|54|C|{1}{U}|Enchantment - Aura|||Flash$Enchant creature you control$When Cradle of Safety enters the battlefield, enchanted creature gains hexproof until end of turn.$Enchanted creature gets +1/+1.| +Cruel Witness|Innistrad: Crimson Vow|55|C|{2}{U}{U}|Creature - Bird Horror|3|3|Flying$Whenever you cast a noncreature spell, look at the top card of your library. You may put that card into your graveyard.| +Diver Skaab|Innistrad: Crimson Vow|56|U|{3}{U}{U}|Creature - Zombie|3|5|Exploit$When Diver Skaab exploits a creature, target creature's owner puts it on the top or bottom of their library.| +Dreadlight Monstrosity|Innistrad: Crimson Vow|57|C|{4}{U}{U}|Creature - Crab Horror|5|5|Ward {2}${3}{U}{U}: Dreadlight Monstrosity can't be blocked this turn. Activate only if you own a card in exile.| +Dreamshackle Geist|Innistrad: Crimson Vow|58|R|{1}{U}{U}|Creature - Spirit|3|1|Flying$At the beginning of combat on your turn, choose up to one —$• Tap target creature.$• Target creature doesn't untap during its controller's next untap step.| +Fear of Death|Innistrad: Crimson Vow|59|C|{1}{U}|Enchantment - Aura|||Enchant creature$When Fear of Death enters the battlefield, mill two cards.$Enchanted creature gets -X/-0, where X is the number of cards in your graveyard.| +Geistlight Snare|Innistrad: Crimson Vow|60|U|{2}{U}|Instant|||This spell costs {1} less to cast if you control a Spirit. It also costs {1} less to cast if you control an enchantment.$Counter target spell unless its controller pays {3}.| +Geralf, Visionary Stitcher|Innistrad: Crimson Vow|61|R|{2}{U}|Legendary Creature - Human Wizard|1|4|Zombies you control have flying.${U}, {T}, Sacrifice another nontoken creature: Create an X/X blue Zombie creature token, where X is the sacrificed creature's toughness.| +Gutter Skulker|Innistrad: Crimson Vow|62|U|{3}{U}|Creature - Spirit|3|3|Gutter Skulker can't be blocked as long as it's attacking alone.$Disturb {3}{U}| +Gutter Shortcut|Innistrad: Crimson Vow|62|U||Enchantment - Aura|||Enchant creature$Enchanted creature can't be blocked as long as it's attacking alone.$If Gutter Shortcut would be put into a graveyard from anywhere, exile it instead.| +Hullbreaker Horror|Innistrad: Crimson Vow|63|R|{5}{U}{U}|Creature - Kraken Horror|7|8|Flash$This spell can't be countered.$Whenever you cast a spell, choose up to one —$• Return target spell you don't control to its owner's hand.$• Return target nonland permanent to its owner's hand.| +Inspired Idea|Innistrad: Crimson Vow|64|R|{2}{U}|Sorcery|||Cleave {3}{U}{U}$Draw three cards. [Your maximum hand size is reduced by three for the rest of the game.]| +Jacob Hauken, Inspector|Innistrad: Crimson Vow|65|M|{1}{U}|Legendary Creature - Human Advisor|0|2|{T}: Draw a card, then exile a card from your hand face down. You may look at that card for as long as it remains exiled. You may pay {4}{U}{U}. If you do, transform Jacob Hauken, Inspector.| +Hauken's Insight|Innistrad: Crimson Vow|65|M||Legendary Enchantment|||At the beginning of your upkeep, exile the top card of your library face down. You may look at that card for as long as it remains exiled.$Once during each of your turns, you may play a land or cast a spell from among the cards exiled with this permanent without paying its mana cost.| +Lantern Bearer|Innistrad: Crimson Vow|66|C|{U}|Creature - Spirit|1|1|Flying$Disturb {2}{U}| +Lanterns' Lift|Innistrad: Crimson Vow|66|C||Enchantment - Aura|||Enchant creature$Enchanted creature gets +1/+1 and has flying.$If Lanterns' Lift would be put into a graveyard from anywhere, exile it instead.| +Lunar Rejection|Innistrad: Crimson Vow|67|U|{1}{U}|Instant|||Cleave {3}{U}$Return target [Wolf or Werewolf] creature to its owner's hand.$Draw a card.| +Mirrorhall Mimic|Innistrad: Crimson Vow|68|R|{3}{U}|Creature - Spirit|0|0|You may have Mirrorhall Mimic enter the battlefield as a copy of any creature on the battlefield, except it's a Spirit in addition to its other types.$Disturb {3}{U}{U}| +Ghastly Mimicry|Innistrad: Crimson Vow|68|R||Enchantment - Aura|||Enchant creature$At the beginning of your upkeep, create a token that's a copy of enchanted creature, except it's a Spirit in addition to its other types.$If Ghastly Mimicry would be put into a graveyard from anywhere, exile it instead.| +Mischievous Catgeist|Innistrad: Crimson Vow|69|U|{1}{U}|Creature - Cat Spirit|1|1|Whenever Mischievous Catgeist deals combat damage to a player, draw a card.$Disturb {2}{U}| +Catlike Curiosity|Innistrad: Crimson Vow|69|U||Enchantment - Aura|||Enchant creature$Enchanted creature has "Whenever this creature deals combat damage to a player, draw a card."$If Catlike Curiosity would be put into a graveyard from anywhere, exile it instead.| +Necroduality|Innistrad: Crimson Vow|70|M|{3}{U}|Enchantment|||Whenever a nontoken Zombie enters the battlefield under your control, create a token that's a copy of that creature.| +Overcharged Amalgam|Innistrad: Crimson Vow|71|R|{2}{U}{U}|Creature - Zombie Horror|3|3|Flash$Flying$Exploit$When Overcharged Amalgam exploits a creature, counter target spell, activated ability, or triggered ability.| +Patchwork Crawler|Innistrad: Crimson Vow|72|R|{1}{U}|Creature - Zombie Horror|1|2|{2}{U}: Exile target creature card from your graveyard and put a +1/+1 counter on Patchwork Crawler.$Patchwork Crawler has all activated abilities of all creature cards exiled with it.| +Repository Skaab|Innistrad: Crimson Vow|73|C|{3}{U}|Creature - Zombie|3|3|Exploit$When Repository Skaab exploits a creature, return target instant or sorcery card from your graveyard to your hand.| +Scattered Thoughts|Innistrad: Crimson Vow|74|C|{3}{U}|Instant|||Look at the top four cards of your library. Put two of those cards into your hand and the rest into your graveyard.| +Screaming Swarm|Innistrad: Crimson Vow|75|U|{5}{U}|Creature - Bird Horror|4|4|Flying$Whenever you attack with one or more creatures, target player mills that many cards.${2}{U}: Put Screaming Swarm from your graveyard into your library second from the top.| +Selhoff Entomber|Innistrad: Crimson Vow|76|C|{1}{U}|Creature - Zombie|1|3|{T}, Discard a creature card: Draw a card.| +Serpentine Ambush|Innistrad: Crimson Vow|77|C|{1}{U}|Instant|||Until end of turn, target creature becomes a blue Serpent with base power and toughness 5/5.| +Skywarp Skaab|Innistrad: Crimson Vow|78|C|{3}{U}{U}|Creature - Zombie Drake|2|5|Flying$When Skywarp Skaab enters the battlefield, you may exile two creature cards from your graveyard. If you do, draw a card.| +Soulcipher Board|Innistrad: Crimson Vow|79|U|{1}{U}|Artifact|||Soulcipher Board enters the battlefield with three omen counters on it.${1}{U}, {T}: Look at the top two cards of your library. Put one of them into your graveyard.$Whenever a creature card is put into your graveyard from anywhere, remove an omen counter from Soulcipher Board. Then if it has no omen counters on it, transform it.| +Cipherbound Spirit|Innistrad: Crimson Vow|79|U||Creature - Spirit|3|2|Flying$Cipherbound Spirit can block only creatures with flying.${3}{U}: Draw two cards, then discard a card.| +Steelclad Spirit|Innistrad: Crimson Vow|80|C|{1}{U}|Creature - Spirit|3|3|Defender$Whenever an enchantment enters the battlefield under your control, Steelclad Spirit can attack this turn as though it didn't have defender.| +Stitched Assistant|Innistrad: Crimson Vow|81|C|{2}{U}|Creature - Zombie|3|2|Exploit$When Stitched Assistant exploits a creature, scry 1, then draw a card.| +Stormchaser Drake|Innistrad: Crimson Vow|82|U|{1}{U}|Creature - Drake|2|1|Flying$Whenever Stormchaser Drake becomes the target of a spell you control, draw a card.| +Syncopate|Innistrad: Crimson Vow|83|C|{X}{U}|Instant|||Counter target spell unless its controller pays {X}. If that spell is countered this way, exile it instead of putting it into its owner's graveyard.| +Syphon Essence|Innistrad: Crimson Vow|84|C|{2}{U}|Instant|||Counter target creature or planeswalker spell. Create a Blood token.| +Thirst for Discovery|Innistrad: Crimson Vow|85|U|{2}{U}|Instant|||Draw three cards. Then discard two cards unless you discard a basic land card.| +Wanderlight Spirit|Innistrad: Crimson Vow|86|C|{2}{U}|Creature - Spirit|2|3|Flying$Wanderlight Spirit can block only creatures with flying.| +Wash Away|Innistrad: Crimson Vow|87|U|{U}|Instant|||Cleave {1}{U}{U}$Counter target spell [that wasn't cast from its owner's hand].| +Whispering Wizard|Innistrad: Crimson Vow|88|U|{3}{U}|Creature - Human Wizard|3|2|Whenever you cast a noncreature spell, create a 1/1 white Spirit creature token with flying. This ability triggers only once each turn.| +Winged Portent|Innistrad: Crimson Vow|89|R|{1}{U}{U}|Instant|||Cleave {4}{G}{U}$Draw a card for each creature [with flying] you control.| +Witness the Future|Innistrad: Crimson Vow|90|U|{2}{U}|Sorcery|||Target player shuffles up to four target cards from their graveyard into their library. You look at the top four cards of your library, then put one of those cards into your hand and the rest on the bottom of your library in a random order.| +Wretched Throng|Innistrad: Crimson Vow|91|C|{1}{U}|Creature - Zombie Horror|2|1|When Wretched Throng dies, you may search your library for a card named Wretched Throng, reveal it, put it into your hand, then shuffle.| +Aim for the Head|Innistrad: Crimson Vow|92|C|{2}{B}|Sorcery|||Choose one —$• Exile target Zombie.$• Target opponent exiles two cards from their hand.| +Archghoul of Thraben|Innistrad: Crimson Vow|93|U|{2}{B}|Creature - Zombie Cleric|3|2|Whenever Archghoul of Thraben or another Zombie you control dies, look at the top card of your library. If it's a Zombie card, you may reveal it and put it into your hand. If you don't put the card into your hand, you may put it into your graveyard.| +Bleed Dry|Innistrad: Crimson Vow|94|C|{2}{B}{B}|Instant|||Target creature gets -13/-13 until end of turn. If that creature would die this turn, exile it instead.| +Blood Fountain|Innistrad: Crimson Vow|95|C|{B}|Artifact|||When Blood Fountain enters the battlefield, create a Blood token.${3}{B}, {T}, Sacrifice Blood Fountain: Return up to two target creature cards from your graveyard to your hand.| +Bloodcrazed Socialite|Innistrad: Crimson Vow|96|C|{3}{B}|Creature - Vampire|3|3|Menace$When Bloodcrazed Socialite enters the battlefield, create a Blood token.$Whenever Bloodcrazed Socialite attacks, you may sacrifice a Blood token. If you do, it gets +2/+2 until end of turn.| +Bloodsworn Squire|Innistrad: Crimson Vow|97|U|{3}{B}|Creature - Vampire Soldier|3|3|{1}{B}, Discard a card: Bloodsworn Squire gains indestructible until end of turn. Tap it. Then if there are four or more creature cards in your graveyard, transform Bloodsworn Squire.| +Bloodsworn Knight|Innistrad: Crimson Vow|97|U||Creature - Vampire Knight|*|*|Bloodsworn Knight's power and toughness are each equal to the number of creature cards in your graveyard.${1}{B}, Discard a card: Bloodsworn Knight gains indestructible until end of turn. Tap it.| +Bloodvial Purveyor|Innistrad: Crimson Vow|98|R|{2}{B}{B}|Creature - Vampire|5|6|Flying, trample$Whenever an opponent casts a spell, that player creates a Blood token.$Whenever Bloodvial Purveyor attacks, it gets +1/+0 until end of turn for each Blood token defending player controls.| +Catapult Fodder|Innistrad: Crimson Vow|99|U|{2}{B}|Creature - Zombie|1|5|At the beginning of combat on your turn, if you control three or more creatures that each have toughness greater than their power, transform Catapult Fodder.| +Catapult Captain|Innistrad: Crimson Vow|99|U||Creature - Zombie|2|6|{2}{B}, {T}, Sacrifice another creature: Target opponent loses life equal to the sacrificed creature's toughness.| +Cemetery Desecrator|Innistrad: Crimson Vow|100|M|{4}{B}{B}|Creature - Zombie|4|4|Menace$When Cemetery Desecrator enters the battlefield or dies, exile another card from a graveyard. When you do, choose one —$• Remove X counters from target permanent, where X is the mana value of the exiled card.$• Target creature an opponent controls gets -X/-X until end of turn, where X is the mana value of the exiled card.| +Concealing Curtains|Innistrad: Crimson Vow|101|R|{B}|Creature - Wall|0|4|Defender${2}{B}: Transform Concealing Curtains. Activate only as a sorcery.| +Revealing Eye|Innistrad: Crimson Vow|101|R||Creature - Eye Horror|3|4|Menace$When this creature transforms into Revealing Eye, target opponent reveals their hand. You may choose a nonland card from it. If you do, that player discards that card, then draws a card.| +Courier Bat|Innistrad: Crimson Vow|102|C|{2}{B}|Creature - Bat|2|2|Flying$When Courier Bat enters the battlefield, if you gained life this turn, return up to one target creature card from your graveyard to your hand.| +Demonic Bargain|Innistrad: Crimson Vow|103|R|{2}{B}|Sorcery|||Exile the top thirteen cards of your library, then search your library for a card. Put that card into your hand, then shuffle.| +Desperate Farmer|Innistrad: Crimson Vow|104|C|{2}{B}|Creature - Human Peasant|2|2|Lifelink$When another creature you control dies, transform Desperate Farmer.| +Depraved Harvester|Innistrad: Crimson Vow|104|C||Creature - Human Knight|4|3|Lifelink| +Diregraf Scavenger|Innistrad: Crimson Vow|105|C|{3}{B}|Creature - Zombie Bear|2|3|Deathtouch$When Diregraf Scavenger enters the battlefield, exile up to one target card from a graveyard. If a creature card was exiled this way, each opponent loses 2 life and you gain 2 life.| +Doomed Dissenter|Innistrad: Crimson Vow|106|C|{1}{B}|Creature - Human|1|1|When Doomed Dissenter dies, create a 2/2 black Zombie creature token.| +Dread Fugue|Innistrad: Crimson Vow|107|U|{B}|Sorcery|||Cleave {2}{B}$Target player reveals their hand. You choose a nonland card from it [with mana value 2 or less]. That player discards that card.| +Dreadfeast Demon|Innistrad: Crimson Vow|108|R|{5}{B}{B}|Creature - Demon|6|6|Flying$At the beginning of your end step, sacrifice a non-Demon creature. If you do, create a token that's a copy of Dreadfeast Demon.| +Dying to Serve|Innistrad: Crimson Vow|109|R|{2}{B}|Enchantment|||Whenever you discard one or more cards, create a tapped 2/2 black Zombie creature token. This ability triggers only once each turn.| +Edgar's Awakening|Innistrad: Crimson Vow|110|U|{3}{B}{B}|Sorcery|||Return target creature card from your graveyard to the battlefield.$When you discard Edgar's Awakening, you may pay {B}. When you do, return target creature card from your graveyard to your hand.| +Falkenrath Forebear|Innistrad: Crimson Vow|111|R|{2}{B}|Creature - Vampire|3|1|Flying$Falkenrath Forebear can't block.$Whenever Falkenrath Forebear deals combat damage to a player, create a Blood token.${B}, Sacrifice two Blood tokens: Return Falkenrath Forebear from your graveyard to the battlefield.| +Fell Stinger|Innistrad: Crimson Vow|112|U|{2}{B}|Creature - Zombie Scorpion|3|2|Deathtouch$Exploit$When Fell Stinger exploits a creature, target player draws two cards and loses 2 life.| +Gift of Fangs|Innistrad: Crimson Vow|113|C|{B}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +2/+2 as long as it's a Vampire. Otherwise, it gets -2/-2.| +Gluttonous Guest|Innistrad: Crimson Vow|114|C|{2}{B}|Creature - Vampire|1|4|When Gluttonous Guest enters the battlefield, create a Blood token.$Whenever you sacrifice a Blood token, you gain 1 life.| +Graf Reaver|Innistrad: Crimson Vow|115|R|{1}{B}|Creature - Zombie Warrior|3|3|Exploit$When Graf Reaver exploits a creature, destroy target planeswalker.$At the beginning of your upkeep, Graf Reaver deals 1 damage to you.| +Grisly Ritual|Innistrad: Crimson Vow|116|C|{5}{B}|Sorcery|||Destroy target creature or planeswalker. Create two Blood tokens.| +Groom's Finery|Innistrad: Crimson Vow|117|U|{1}{B}|Artifact - Equipment|||Equipped creature gets +2/+0. It gets an additional +0/+2 and has deathtouch as long as an Equipment named Bride's Gown is attached to a creature you control.$Equip {2}| +Headless Rider|Innistrad: Crimson Vow|118|R|{2}{B}|Creature - Zombie|3|1|Whenever Headless Rider or another nontoken Zombie you control dies, create a 2/2 black Zombie creature token.| +Henrika Domnathi|Innistrad: Crimson Vow|119|M|{2}{B}{B}|Legendary Creature - Vampire|1|3|Flying$At the beginning of combat on your turn, choose one that hasn't been chosen —$• Each player sacrifices a creature.$• You draw a card and you lose 1 life.$• Transform Henrika Domnathi.| +Henrika, Infernal Seer|Innistrad: Crimson Vow|119|M||Legendary Creature - Vampire|3|4|Flying, deathtouch, lifelink${1}{B}{B}: Each creature you control with flying, deathtouch, and/or lifelink gets +1/+0 until end of turn.| +Hero's Downfall|Innistrad: Crimson Vow|120|U|{1}{B}{B}|Instant|||Destroy target creature or planeswalker.| +Innocent Traveler|Innistrad: Crimson Vow|121|U|{2}{B}{B}|Creature - Human|1|3|At the beginning of your upkeep, any opponent may sacrifice a creature. If no one does, transform Innocent Traveler.| +Malicious Invader|Innistrad: Crimson Vow|121|U||Creature - Vampire|3|3|Flying$Malicious Invader gets +2/+0 as long as an opponent controls a Human.| +Mindleech Ghoul|Innistrad: Crimson Vow|122|C|{1}{B}|Creature - Zombie|2|2|Exploit$When Mindleech Ghoul exploits a creature, each opponent exiles a card from their hand.| +Parasitic Grasp|Innistrad: Crimson Vow|123|U|{1}{B}|Instant|||Cleave {1}{B}{B}$Parasitic Grasp deals 3 damage to target [Human] creature. You gain 3 life.| +Path of Peril|Innistrad: Crimson Vow|124|R|{1}{B}{B}|Sorcery|||Cleave {4}{W}{B}$Destroy all creatures [with mana value 2 or less].| +Persistent Specimen|Innistrad: Crimson Vow|125|C|{B}|Creature - Skeleton|1|1|{2}{B}: Return Persistent Specimen from your graveyard to the battlefield tapped.| +Pointed Discussion|Innistrad: Crimson Vow|126|C|{2}{B}|Sorcery|||You draw two cards, lose 2 life, then create a Blood token.| +Ragged Recluse|Innistrad: Crimson Vow|127|C|{1}{B}|Creature - Human Peasant|2|1|At the beginning of your end step, if you discarded a card this turn, transform Ragged Recluse.| +Odious Witch|Innistrad: Crimson Vow|127|C||Creature - Human Warlock|3|3|Whenever Odious Witch attacks, defending player loses 1 life and you gain 1 life.| +Restless Bloodseeker|Innistrad: Crimson Vow|128|U|{1}{B}|Creature - Vampire|1|3|At the beginning of your end step, if you gained life this turn, create a Blood token.$Sacrifice two Blood tokens: Transform Restless Bloodseeker. Activate only as a sorcery.| +Bloodsoaked Reveler|Innistrad: Crimson Vow|128|U||Creature - Vampire|3|3|At the beginning of your end step, if you gained life this turn, create a Blood token.${4}{B}: Each opponent loses 2 life and you gain 2 life.| +Rot-Tide Gargantua|Innistrad: Crimson Vow|129|C|{3}{B}{B}|Creature - Zombie Kraken|5|4|Exploit$When Rot-Tide Gargantua exploits a creature, each opponent sacrifices a creature.| +Skulking Killer|Innistrad: Crimson Vow|130|U|{3}{B}|Creature - Vampire Assassin|4|2|When Skulking Killer enters the battlefield, target creature an opponent controls gets -2/-2 until end of turn if that opponent controls no other creatures.| +Sorin the Mirthless|Innistrad: Crimson Vow|131|M|{2}{B}{B}|Legendary Planeswalker - Sorin|4|+1: Look at the top card of your library. You may reveal that card and put it into your hand. If you do, you lose life equal to its mana value.$−2: Create a 2/3 black Vampire creature token with flying and lifelink.$−7: Sorin the Mirthless deals 13 damage to any target. You gain 13 life.| +Toxrill, the Corrosive|Innistrad: Crimson Vow|132|M|{5}{B}{B}|Legendary Creature - Slug Horror|7|7|At the beginning of each end step, put a slime counter on each creature you don't control.$Creatures you don't control get -1/-1 for each slime counter on them.$Whenever a creature you don't control with a slime counter on it dies, create a 1/1 black Slug creature token.${U}{B}, Sacrifice a Slug: Draw a card.| +Undead Butler|Innistrad: Crimson Vow|133|U|{1}{B}|Creature - Zombie|1|2|When Undead Butler enters the battlefield, mill three cards.$When Undead Butler dies, you may exile it. When you do, return target creature card from your graveyard to your hand.| +Undying Malice|Innistrad: Crimson Vow|134|C|{B}|Instant|||Until end of turn, target creature gains "When this creature dies, return it to the battlefield tapped under its owner's control with a +1/+1 counter on it."| +Unhallowed Phalanx|Innistrad: Crimson Vow|135|C|{4}{B}|Creature - Zombie Soldier|1|13|Unhallowed Phalanx enters the battlefield tapped.| +Vampire's Kiss|Innistrad: Crimson Vow|136|C|{1}{B}|Sorcery|||Target player loses 2 life and you gain 2 life. Create two Blood tokens.| +Voldaren Bloodcaster|Innistrad: Crimson Vow|137|R|{1}{B}|Creature - Vampire Wizard|2|1|Flying$Whenever Voldaren Bloodcaster or another nontoken creature you control dies, create a Blood token.$Whenever you create a Blood token, if you control five or more Blood tokens, transform Voldaren Bloodcaster.| +Bloodbat Summoner|Innistrad: Crimson Vow|137|R||Creature - Vampire Wizard|3|3|Flying$At the beginning of combat on your turn, up to one target Blood token you control becomes a 2/2 black Bat creature with flying and haste in addition to its other types.| +Wedding Security|Innistrad: Crimson Vow|138|U|{3}{B}{B}|Creature - Vampire Soldier|4|4|Whenever Wedding Security attacks, you may sacrifice a Blood token. If you do, put a +1/+1 counter on Wedding Security and draw a card.| +Abrade|Innistrad: Crimson Vow|139|C|{1}{R}|Instant|||Choose one —$• Abrade deals 3 damage to target creature.$• Destroy target artifact.| +Alchemist's Gambit|Innistrad: Crimson Vow|140|R|{1}{R}{R}|Sorcery|||Cleave {4}{U}{U}{R}$Take an extra turn after this one. During that turn, damage can't be prevented. [At the beginning of that turn's end step, you lose the game.]$Exile Alchemist's Gambit.| +Alluring Suitor|Innistrad: Crimson Vow|141|U|{2}{R}|Creature - Vampire|2|3|When you attack with exactly two creatures, transform Alluring Suitor.| +Deadly Dancer|Innistrad: Crimson Vow|141|U||Creature - Vampire|3|3|Trample$When this creature transforms into Deadly Dancer, add {R}{R}. Until end of turn, you don't lose this mana as steps and phases end.${R}{R}: Deadly Dancer and another target creature each get +1/+0 until end of turn.| +Ancestral Anger|Innistrad: Crimson Vow|142|C|{R}|Sorcery|||Target creature gains trample and gets +X/+0 until end of turn, where X is 1 plus the number of cards named Ancestral Anger in your graveyard.$Draw a card.| +Ballista Watcher|Innistrad: Crimson Vow|143|U|{2}{R}{R}|Creature - Human Soldier Werewolf|4|3|{2}{R}, {T}: Ballista Watcher deals 1 damage to any target.$Daybound| +Ballista Wielder|Innistrad: Crimson Vow|143|U||Creature - Werewolf|5|5|{2}{R}: Ballista Wielder deals 1 damage to any target. A creature dealt damage this way can't block this turn.$Nightbound| +Belligerent Guest|Innistrad: Crimson Vow|144|C|{2}{R}|Creature - Vampire|3|2|Trample$Whenever Belligerent Guest deals combat damage to a player, create a Blood token.| +Blood Hypnotist|Innistrad: Crimson Vow|145|U|{2}{R}|Creature - Vampire|3|3|Blood Hypnotist can't block.$Whenever you sacrifice one or more Blood tokens, target creature can't block this turn. This ability triggers only once each turn.| +Blood Petal Celebrant|Innistrad: Crimson Vow|146|C|{1}{R}|Creature - Vampire|2|1|Blood Petal Celebrant has first strike as long as it's attacking.$When Blood Petal Celebrant dies, create a Blood token.| +Bloody Betrayal|Innistrad: Crimson Vow|147|C|{2}{R}|Sorcery|||Gain control of target creature until end of turn. Untap that creature. It gains haste until end of turn. Create a Blood token.| +Cemetery Gatekeeper|Innistrad: Crimson Vow|148|M|{1}{R}|Creature - Vampire|2|1|First strike$When Cemetery Gatekeeper enters the battlefield, exile a card from a graveyard.$Whenever a player plays a land or casts a spell, if it shares a card type with the exiled card, Cemetery Gatekeeper deals 2 damage to that player.| +Chandra, Dressed to Kill|Innistrad: Crimson Vow|149|M|{1}{R}{R}|Legendary Planeswalker - Chandra|3|+1: Add {R}. Chandra, Dressed to Kill deals 1 damage to up to one target player or planeswalker.$+1: Exile the top card of your library. If it's red, you may cast it this turn.$−7: Exile the top five cards of your library. You may cast red spells from among them this turn. You get an emblem with "Whenever you cast a red spell, this emblem deals X damage to any target, where X is the amount of mana spent to cast that spell."| +Change of Fortune|Innistrad: Crimson Vow|150|R|{3}{R}|Sorcery|||Discard your hand, then draw a card for each card you've discarded this turn.| +Creepy Puppeteer|Innistrad: Crimson Vow|151|R|{3}{R}|Creature - Human Rogue|4|3|Haste$Whenever Creepy Puppeteer attacks, if you attacked with exactly one other creature this combat, you may have that creature's base power and toughness become 4/3 until end of turn.| +Curse of Hospitality|Innistrad: Crimson Vow|152|R|{2}{R}|Enchantment - Aura Curse|||Enchant player$Creatures attacking enchanted player have trample.$Whenever a creature deals combat damage to enchanted player, that player exiles the top card of their library. Until end of turn, that creature's controller may play that card and they may spend mana as though it were mana of any color to cast that spell.| +Daybreak Combatants|Innistrad: Crimson Vow|153|C|{2}{R}|Creature - Human Warrior|2|2|Haste$When Daybreak Combatants enters the battlefield, target creature gets +2/+0 until end of turn.| +Dominating Vampire|Innistrad: Crimson Vow|154|R|{1}{R}{R}|Creature - Vampire|3|3|When Dominating Vampire enters the battlefield, gain control of target creature with mana value less than or equal to the number of Vampires you control until end of turn. Untap that creature. It gains haste until end of turn.| +End the Festivities|Innistrad: Crimson Vow|155|C|{R}|Sorcery|||End the Festivities deals 1 damage to each opponent and each creature and planeswalker they control.| +Falkenrath Celebrants|Innistrad: Crimson Vow|156|C|{4}{R}|Creature - Vampire|4|4|Menace$When Falkenrath Celebrants enters the battlefield, create two Blood tokens.| +Fearful Villager|Innistrad: Crimson Vow|157|C|{2}{R}|Creature - Human Werewolf|2|3|Menace$Daybound| +Fearsome Werewolf|Innistrad: Crimson Vow|157|C||Creature - Werewolf|4|3|Menace$Nightbound| +Flame-Blessed Bolt|Innistrad: Crimson Vow|158|C|{R}|Instant|||Flame-Blessed Bolt deals 2 damage to target creature or planeswalker. If that creature or planeswalker would die this turn, exile it instead.| +Frenzied Devils|Innistrad: Crimson Vow|159|U|{4}{R}|Creature - Devil|3|3|Haste$Whenever you cast a noncreature spell, Frenzied Devils gets +2/+2 until end of turn.| +Honeymoon Hearse|Innistrad: Crimson Vow|160|U|{2}{R}|Artifact - Vehicle|5|5|Trample$Tap two untapped creatures you control: Honeymoon Hearse becomes an artifact creature until end of turn.| +Hungry Ridgewolf|Innistrad: Crimson Vow|161|C|{1}{R}|Creature - Wolf|2|2|As long as you control another Wolf or Werewolf, Hungry Ridgewolf gets +1/+0 and has trample.| +Ill-Tempered Loner|Innistrad: Crimson Vow|162|R|{2}{R}{R}|Creature - Human Werewolf|3|3|Whenever Ill-Tempered Loner is dealt damage, it deals that much damage to any target.${1}{R}: Ill-Tempered Loner gets +2/+0 until end of turn.$Daybound| +Howlpack Avenger|Innistrad: Crimson Vow|162|R||Creature - Werewolf|4|4|Whenever a permanent you control is dealt damage, Howlpack Avenger deals that much damage to any target.${1}{R}: Howlpack Avenger gets +2/+0 until end of turn.$Nightbound| +Into the Night|Innistrad: Crimson Vow|163|U|{3}{R}|Sorcery|||It becomes night. Discard any number of cards, then draw that many cards plus one.| +Kessig Flamebreather|Innistrad: Crimson Vow|164|C|{1}{R}|Creature - Human Shaman|1|3|Whenever you cast a noncreature spell, Kessig Flamebreather deals 1 damage to each opponent.| +Kessig Wolfrider|Innistrad: Crimson Vow|165|R|{R}|Creature - Human Knight|1|2|Menace${2}{R}, {T}, Exile three cards from your graveyard: Create a 3/2 red Wolf creature token.| +Lacerate Flesh|Innistrad: Crimson Vow|166|C|{4}{R}|Sorcery|||Lacerate Flesh deals 4 damage to target creature. Create a number of Blood tokens equal to the amount of excess damage dealt to that creature this way.| +Lambholt Raconteur|Innistrad: Crimson Vow|167|U|{3}{R}|Creature - Human Werewolf|2|4|Whenever you cast a noncreature spell, Lambholt Raconteur deals 1 damage to each opponent.$Daybound| +Lambholt Ravager|Innistrad: Crimson Vow|167|U||Creature - Werewolf|4|4|Whenever you cast a noncreature spell, Lambholt Ravager deals 2 damage to each opponent.$Nightbound| +Lightning Wolf|Innistrad: Crimson Vow|168|C|{3}{R}|Creature - Wolf|4|3|{1}{R}: Lightning Wolf gains first strike until end of turn. Activate only as a sorcery.| +Magma Pummeler|Innistrad: Crimson Vow|169|U|{X}{R}{R}|Creature - Elemental|0|0|Magma Pummeler enters the battlefield with X +1/+1 counters on it.$If damage would be dealt to Magma Pummeler while it has a +1/+1 counter on it, prevent that damage and remove that many +1/+1 counters from it. When one or more counters are removed from Magma Pummeler this way, it deals that much damage to any target.| +Manaform Hellkite|Innistrad: Crimson Vow|170|M|{2}{R}{R}|Creature - Dragon|4|4|Flying$Whenever you cast a noncreature spell, create an X/X red Dragon Illusion creature token with flying and haste, where X is the amount of mana spent to cast that spell. Exile that token at the beginning of the next end step.| +Markov Retribution|Innistrad: Crimson Vow|171|U|{2}{R}|Sorcery|||Choose one or both —$• Creatures you control get +1/+0 until end of turn.$• Target Vampire you control deals damage equal to its power to another target creature.| +Olivia's Attendants|Innistrad: Crimson Vow|172|R|{4}{R}{R}|Creature - Vampire|6|6|Menace$Whenever Olivia's Attendants deals damage, create that many Blood tokens.${2}{R}: Olivia's Attendants deals 1 damage to any target.| +Pyre Spawn|Innistrad: Crimson Vow|173|C|{4}{R}{R}|Creature - Elemental|6|4|When Pyre Spawn dies, it deals 3 damage to any target.| +Reckless Impulse|Innistrad: Crimson Vow|174|C|{1}{R}|Sorcery|||Exile the top two cards of your library. Until the end of your next turn, you may play those cards.| +Rending Flame|Innistrad: Crimson Vow|175|U|{2}{R}|Instant|||Rending Flame deals 5 damage to target creature or planeswalker. If that permanent is a Spirit, Rending Flame also deals 2 damage to that permanent's controller.| +Runebound Wolf|Innistrad: Crimson Vow|176|U|{1}{R}|Creature - Wolf|2|2|{3}{R}, {T}: Runebound Wolf deals damage equal to the number of Wolves and Werewolves you control to target opponent.| +Sanguine Statuette|Innistrad: Crimson Vow|177|U|{1}{R}|Artifact|||When Sanguine Statuette enters the battlefield, create a Blood token.$Whenever you sacrifice a Blood token, you may have Sanguine Statuette become a 3/3 Vampire artifact creature with haste until end of turn.| +Stensia Uprising|Innistrad: Crimson Vow|178|R|{2}{R}{R}|Enchantment|||At the beginning of your end step, create a 1/1 red Human creature token. Then if you control exactly thirteen permanents, you may sacrifice Stensia Uprising. When you do, it deals 7 damage to any target.| +Sure Strike|Innistrad: Crimson Vow|179|C|{1}{R}|Instant|||Target creature gets +3/+0 and gains first strike until end of turn.| +Vampires' Vengeance|Innistrad: Crimson Vow|180|U|{2}{R}|Instant|||Vampires' Vengeance deals 2 damage to each non-Vampire creature. Create a Blood token.| +Volatile Arsonist|Innistrad: Crimson Vow|181|M|{3}{R}{R}|Creature - Human Werewolf|4|4|Menace, haste$Whenever Volatile Arsonist attacks, it deals 1 damage to each of up to one target creature, up to one target player, and/or up to one target planeswalker.$Daybound| +Dire-Strain Anarchist|Innistrad: Crimson Vow|181|M||Creature - Werewolf|5|5|Menace, haste$Whenever Dire-Strain Anarchist attacks, it deals 2 damage to each of up to one target creature, up to one target player, and/or up to one target planeswalker.$Nightbound| +Voldaren Epicure|Innistrad: Crimson Vow|182|C|{R}|Creature - Vampire|1|1|When Voldaren Epicure enters the battlefield, it deals 1 damage to each opponent. Create a Blood token.| +Voltaic Visionary|Innistrad: Crimson Vow|183|U|{1}{R}|Creature - Human Wizard|3|1|{T}: Voltaic Visionary deals 2 damage to you. Exile the top card of your library. You may play that card this turn. Activate only as a sorcery.$When you play a card exiled with Voltaic Visionary, transform Voltaic Visionary.| +Volt-Charged Berserker|Innistrad: Crimson Vow|183|U||Creature - Human Berserker|4|3|Volt-Charged Berserker can't block.| +Weary Prisoner|Innistrad: Crimson Vow|184|C|{3}{R}|Creature - Human Werewolf|2|6|Defender$Daybound| +Wrathful Jailbreaker|Innistrad: Crimson Vow|184|C||Creature - Werewolf|6|6|Wrathful Jailbreaker attacks each combat if able.$Nightbound| +Apprentice Sharpshooter|Innistrad: Crimson Vow|185|C|{2}{G}|Creature - Human Archer|1|4|Reach$Training| +Ascendant Packleader|Innistrad: Crimson Vow|186|R|{G}|Creature - Wolf|2|1|Ascendant Packleader enters the battlefield with a +1/+1 counter on it if you control a permanent with mana value 4 or greater.$Whenever you cast a spell with mana value 4 or greater, put a +1/+1 counter on Ascendant Packleader.| +Avabruck Caretaker|Innistrad: Crimson Vow|187|M|{4}{G}{G}|Creature - Human Werewolf|4|4|Hexproof$At the beginning of combat on your turn, put two +1/+1 counters on another target creature you control.$Daybound| +Hollowhenge Huntmaster|Innistrad: Crimson Vow|187|M||Creature - Werewolf|6|6|Hexproof$Other permanents you control have hexproof.$At the beginning of combat on your turn, put two +1/+1 counters on each creature you control.$Nightbound| +Bramble Armor|Innistrad: Crimson Vow|188|C|{1}{G}|Artifact - Equipment|||When Bramble Armor enters the battlefield, attach it to target creature you control.$Equipped creature gets +2/+1.$Equip {4}| +Bramble Wurm|Innistrad: Crimson Vow|189|U|{6}{G}|Creature - Wurm|7|6|Reach, trample$When Bramble Wurm enters the battlefield, you gain 5 life.${2}{G}, Exile Bramble Wurm from your graveyard: You gain 5 life.| +Cartographer's Survey|Innistrad: Crimson Vow|190|U|{3}{G}|Sorcery|||Look at the top seven cards of your library. Put up to two land cards from among them onto the battlefield tapped. Put the rest on the bottom of your library in a random order.| +Cemetery Prowler|Innistrad: Crimson Vow|191|M|{1}{G}{G}|Creature - Wolf|3|4|Vigilance$Whenever Cemetery Prowler enters the battlefield or attacks, exile a card from a graveyard.$Spells you cast cost {1} less to cast for each card type they share with cards exiled with Cemetery Prowler.| +Cloaked Cadet|Innistrad: Crimson Vow|192|U|{4}{G}|Creature - Human Ranger|2|4|Training$Whenever one or more +1/+1 counters are put on one or more Humans you control, draw a card. This ability triggers only once each turn.| +Crawling Infestation|Innistrad: Crimson Vow|193|U|{2}{G}|Enchantment|||At the beginning of your upkeep, you may mill two cards.$Whenever one or more creature cards are put into your graveyard from anywhere during your turn, create a 1/1 green Insect creature token. This ability triggers only once each turn.| +Crushing Canopy|Innistrad: Crimson Vow|194|C|{2}{G}|Instant|||Choose one —$• Destroy target creature with flying.$• Destroy target enchantment.| +Cultivator Colossus|Innistrad: Crimson Vow|195|M|{4}{G}{G}{G}|Creature - Plant Beast|*|*|Trample$Cultivator Colossus's power and toughness are each equal to the number of lands you control.$When Cultivator Colossus enters the battlefield, you may put a land card from your hand onto the battlefield tapped. If you do, draw a card and repeat this process.| +Dawnhart Disciple|Innistrad: Crimson Vow|196|C|{1}{G}|Creature - Human Warlock|2|2|Whenever another Human enters the battlefield under your control, Dawnhart Disciple gets +1/+1 until end of turn.| +Dig Up|Innistrad: Crimson Vow|197|R|{G}|Sorcery|||Cleave {1}{B}{B}{G}$Search your library for a [basic land] card, [reveal it,] put it into your hand, then shuffle.| +Dormant Grove|Innistrad: Crimson Vow|198|U|{3}{G}|Enchantment|||At the beginning of combat on your turn, put a +1/+1 counter on target creature you control. Then if that creature has toughness 6 or greater, transform Dormant Grove.| +Gnarled Grovestrider|Innistrad: Crimson Vow|198|U||Creature - Treefolk|3|6|Vigilance$Other creatures you control have vigilance.| +Flourishing Hunter|Innistrad: Crimson Vow|199|C|{4}{G}{G}|Creature - Wolf Spirit|6|6|When Flourishing Hunter enters the battlefield, you gain life equal to the greatest toughness among other creatures you control.| +Glorious Sunrise|Innistrad: Crimson Vow|200|R|{3}{G}{G}|Enchantment|||At the beginning of combat on your turn, choose one —$• Creatures you control get +1/+1 and gain trample until end of turn.$• Target land gains "{T}: Add {G}{G}{G}" until end of turn.$• Draw a card if you control a creature with power 3 or greater.$• You gain 3 life.| +Hamlet Vanguard|Innistrad: Crimson Vow|201|R|{2}{G}|Creature - Human Warrior|1|1|Ward {2}$Hamlet Vanguard enters the battlefield with two +1/+1 counters on it for each other nontoken Human you control.| +Hiveheart Shaman|Innistrad: Crimson Vow|202|R|{3}{G}|Creature - Human Shaman|3|5|Whenever Hiveheart Shaman attacks, you may search your library for a basic land card that doesn't share a land type with a land you control, put that card onto the battlefield, then shuffle.${5}{G}: Create a 1/1 green Insect creature token. Put X +1/+1 counters on it, where X is the number of basic land types among lands you control. Activate only as a sorcery.| +Hookhand Mariner|Innistrad: Crimson Vow|203|C|{3}{G}|Creature - Human Werewolf|4|4|Daybound| +Riphook Raider|Innistrad: Crimson Vow|203|C||Creature - Werewolf|6|4|Riphook Raider can't be blocked by creatures with power 2 or less.$Nightbound| +Howling Moon|Innistrad: Crimson Vow|204|R|{2}{G}|Enchantment|||At the beginning of combat on your turn, target Wolf or Werewolf you control gets +2/+2 until end of turn.$Whenever an opponent casts their second spell each turn, create a 2/2 green Wolf creature token.| +Howlpack Piper|Innistrad: Crimson Vow|205|R|{3}{G}|Creature - Human Werewolf|2|2|This spell can't be countered.${1}{G}, {T}: You may put a creature card from your hand onto the battlefield. If it's a Wolf or Werewolf, untap Howlpack Piper. Activate only as a sorcery.$Daybound| +Wildsong Howler|Innistrad: Crimson Vow|205|R||Creature - Werewolf|4|4|Whenever this creature enters the battlefield or transforms into Wildsong Howler, look at the top six cards of your library. You may reveal a creature card from among them and put it into your hand. Put the rest on the bottom of your library in a random order.$Nightbound| +Infestation Expert|Innistrad: Crimson Vow|206|U|{4}{G}|Creature - Human Werewolf|3|4|Whenever Infestation Expert enters the battlefield or attacks, create a 1/1 green Insect creature token.$Daybound| +Infested Werewolf|Innistrad: Crimson Vow|206|U||Creature - Werewolf|4|5|Whenever Infested Werewolf enters the battlefield or attacks, create two 1/1 green Insect creature tokens.$Nightbound| +Laid to Rest|Innistrad: Crimson Vow|207|U|{3}{G}|Enchantment|||Whenever a Human you control dies, draw a card.$Whenever a creature you control with a +1/+1 counter on it dies, you gain 2 life.| +Massive Might|Innistrad: Crimson Vow|208|C|{G}|Instant|||Target creature gets +2/+2 and gains trample until end of turn.| +Moldgraf Millipede|Innistrad: Crimson Vow|209|C|{4}{G}|Creature - Insect Horror|2|2|When Moldgraf Millipede enters the battlefield, mill three cards, then put a +1/+1 counter on Moldgraf Millipede for each creature card in your graveyard.| +Mulch|Innistrad: Crimson Vow|210|C|{1}{G}|Sorcery|||Reveal the top four cards of your library. Put all land cards revealed this way into your hand and the rest into your graveyard.| +Nature's Embrace|Innistrad: Crimson Vow|211|C|{2}{G}|Enchantment - Aura|||Enchant creature or land$As long as enchanted permanent is a creature, it gets +2/+2.$As long as enchanted permanent is a land, it has "{T}: Add two mana of any one color."| +Oakshade Stalker|Innistrad: Crimson Vow|212|U|{2}{G}|Creature - Human Ranger Werewolf|3|3|You may cast this spell as though it had flash if you pay {2} more to cast it.$Daybound| +Moonlit Ambusher|Innistrad: Crimson Vow|212|U||Creature - Werewolf|6|3|Nightbound| +Packsong Pup|Innistrad: Crimson Vow|213|U|{1}{G}|Creature - Wolf|1|1|At the beginning of combat on your turn, if you control another Wolf or Werewolf, put a +1/+1 counter on Packsong Pup.$When Packsong Pup dies, you gain life equal to its power.| +Reclusive Taxidermist|Innistrad: Crimson Vow|214|U|{1}{G}|Creature - Human Druid|1|2|Reclusive Taxidermist gets +3/+2 as long as there are four or more creature cards in your graveyard.${T}: Add one mana of any color.| +Retrieve|Innistrad: Crimson Vow|215|U|{2}{G}|Sorcery|||Return up to one target creature card and up to one target noncreature permanent card from your graveyard to your hand. Exile Retrieve.| +Rural Recruit|Innistrad: Crimson Vow|216|C|{3}{G}|Creature - Human Peasant|1|1|Training$When Rural Recruit enters the battlefield, create a 3/1 green Boar creature token.| +Sawblade Slinger|Innistrad: Crimson Vow|217|U|{3}{G}|Creature - Human Archer|4|3|When Sawblade Slinger enters the battlefield, choose up to one —$• Destroy target artifact an opponent controls.$• Sawblade Slinger fights target Zombie an opponent controls.| +Sheltering Boughs|Innistrad: Crimson Vow|218|C|{2}{G}|Enchantment - Aura|||Enchant creature$When Sheltering Boughs enters the battlefield, draw a card.$Enchanted creature gets +1/+3.| +Snarling Wolf|Innistrad: Crimson Vow|219|C|{G}|Creature - Wolf|1|1|{1}{G}: Snarling Wolf gets +2/+2 until end of turn. Activate only once each turn.| +Spiked Ripsaw|Innistrad: Crimson Vow|220|U|{2}{G}|Artifact - Equipment|||Equipped creature gets +3/+3.$Whenever equipped creature attacks, you may sacrifice a Forest. If you do, that creature gains trample until end of turn.$Equip {3}| +Splendid Reclamation|Innistrad: Crimson Vow|221|R|{3}{G}|Sorcery|||Return all land cards from your graveyard to the battlefield tapped.| +Spore Crawler|Innistrad: Crimson Vow|222|C|{2}{G}|Creature - Fungus|3|2|When Spore Crawler dies, draw a card.| +Sporeback Wolf|Innistrad: Crimson Vow|223|C|{1}{G}|Creature - Wolf|2|2|As long as it's your turn, Sporeback Wolf gets +0/+2.| +Toxic Scorpion|Innistrad: Crimson Vow|224|C|{1}{G}|Creature - Scorpion|1|1|Deathtouch$When Toxic Scorpion enters the battlefield, another target creature you control gains deathtouch until end of turn.| +Ulvenwald Oddity|Innistrad: Crimson Vow|225|R|{2}{G}{G}|Creature - Beast|4|4|Trample, haste${5}{G}{G}: Transform Ulvenwald Oddity.| +Ulvenwald Behemoth|Innistrad: Crimson Vow|225|R||Creature - Beast Horror|8|8|Trample, haste$Other creatures you control get +1/+1 and have trample and haste.| +Weaver of Blossoms|Innistrad: Crimson Vow|226|C|{2}{G}|Creature - Human Werewolf|2|3|{T}: Add one mana of any color.$Daybound| +Blossom-Clad Werewolf|Innistrad: Crimson Vow|226|C||Creature - Werewolf|3|4|{T}: Add two mana of any one color.$Nightbound| +Witch's Web|Innistrad: Crimson Vow|227|C|{1}{G}|Instant|||Target creature gets +3/+3 and gains reach until end of turn. Untap it.| +Wolf Strike|Innistrad: Crimson Vow|228|C|{2}{G}|Instant|||Target creature you control gets +2/+0 until end of turn if it's night. Then it deals damage equal to its power to target creature you don't control.| +Wolfkin Outcast|Innistrad: Crimson Vow|229|U|{5}{G}|Creature - Human Werewolf|5|4|This spell costs {2} less to cast if you control a Wolf or Werewolf.$Daybound| +Wedding Crasher|Innistrad: Crimson Vow|229|U||Creature - Werewolf|6|5|Whenever Wedding Crasher or another Wolf or Werewolf you control dies, draw a card.$Nightbound| +Ancient Lumberknot|Innistrad: Crimson Vow|230|U|{2}{B}{G}|Creature - Treefolk|1|4|Each creature you control with toughness greater than its power assigns combat damage equal to its toughness rather than its power.| +Anje, Maid of Dishonor|Innistrad: Crimson Vow|231|R|{2}{B}{R}|Legendary Creature - Vampire|4|5|Whenever Anje, Maid of Dishonor and/or one or more other Vampires enter the battlefield under your control, create a Blood token. This ability triggers only once each turn.${2}, Sacrifice another creature or a Blood token: Each opponent loses 2 life and you gain 2 life.| +Bloodtithe Harvester|Innistrad: Crimson Vow|232|U|{B}{R}|Creature - Vampire|3|2|When Bloodtithe Harvester enters the battlefield, create a Blood token.${T}, Sacrifice Bloodtithe Harvester: Target creature gets -X/-X until end of turn, where X is twice the number of Blood tokens you control. Activate only as a sorcery.| +Brine Comber|Innistrad: Crimson Vow|233|U|{1}{W}{U}|Creature - Spirit|1|1|Whenever Brine Comber enters the battlefield or becomes the target of an Aura spell, create a 1/1 white Spirit creature token with flying.$Disturb {W}{U}| +Brinebound Gift|Innistrad: Crimson Vow|233|U||Enchantment - Aura|||Enchant creature$Whenever Brinebound Gift enters the battlefield or enchanted creature becomes the target of an Aura spell, create a 1/1 white Spirit creature token with flying.$If Brinebound Gift would be put into a graveyard from anywhere, exile it instead.| +Child of the Pack|Innistrad: Crimson Vow|234|U|{2}{R}{G}|Creature - Human Werewolf|2|5|{2}{R}{G}: Create a 2/2 green Wolf creature token.$Daybound| +Savage Packmate|Innistrad: Crimson Vow|234|U||Creature - Werewolf|5|5|Trample$Other creatures you control get +1/+0.$Nightbound| +Dorothea, Vengeful Victim|Innistrad: Crimson Vow|235|R|{W}{U}|Legendary Creature - Spirit|4|4|Flying$When Dorothea, Vengeful Victim attacks or blocks, sacrifice it at end of combat.$Disturb {1}{W}{U}| +Dorothea's Retribution|Innistrad: Crimson Vow|235|R||Enchantment - Aura|||Enchant creature$Enchanted creature has "Whenever this creature attacks, create a 4/4 white Spirit creature token with flying that's tapped and attacking. Sacrifice that token at end of combat."$If Dorothea's Retribution would be put into a graveyard from anywhere, exile it instead.| +Edgar, Charmed Groom|Innistrad: Crimson Vow|236|R|{2}{W}{B}|Legendary Creature - Vampire Noble|4|4|Other Vampires you control get +1/+1.$When Edgar, Charmed Groom dies, return it to the battlefield transformed under its owner's control.| +Edgar Markov's Coffin|Innistrad: Crimson Vow|236|R||Legendary Artifact|||At the beginning of your upkeep, create a 1/1 white and black Vampire creature token with lifelink and put a bloodline counter on Edgar Markov's Coffin. Then if there are three or more bloodline counters on it, remove those counters and transform it.| +Eruth, Tormented Prophet|Innistrad: Crimson Vow|237|R|{1}{U}{R}|Legendary Creature - Human Wizard|2|4|If you would draw a card, exile the top two cards of your library instead. You may play those cards this turn.| +Grolnok, the Omnivore|Innistrad: Crimson Vow|238|R|{2}{G}{U}|Legendary Creature - Frog|3|3|Whenever a Frog you control attacks, mill three cards.$Whenever a permanent card is put into your graveyard from your library, exile it with a croak counter on it.$You may play lands and cast spells from among cards you own in exile with croak counters on them.| +Halana and Alena, Partners|Innistrad: Crimson Vow|239|R|{2}{R}{G}|Legendary Creature - Human Ranger|2|3|First strike, reach$At the beginning of combat on your turn, put X +1/+1 counters on another target creature you control, where X is Halana and Alena's power. That creature gains haste until end of turn.| +Kaya, Geist Hunter|Innistrad: Crimson Vow|240|M|{1}{W}{B}|Legendary Planeswalker - Kaya|3|+1: Creatures you control gain deathtouch until end of turn. Put a +1/+1 counter on up to one target creature token you control.$−2: Until end of turn, if one or more tokens would be created under your control, twice that many of those tokens are created instead.$−6: Exile all cards from all graveyards, then create a 1/1 white Spirit creature token with flying for each card exiled this way.| +Markov Purifier|Innistrad: Crimson Vow|241|U|{1}{W}{B}|Creature - Vampire Cleric|2|3|Lifelink$At the beginning of your end step, if you gained life this turn, you may pay {2}. If you do, draw a card.| +Markov Waltzer|Innistrad: Crimson Vow|242|U|{2}{R}{W}|Creature - Vampire|1|3|Flying, haste$At the beginning of combat on your turn, up to two target creatures you control each get +1/+0 until end of turn.| +Odric, Blood-Cursed|Innistrad: Crimson Vow|243|R|{1}{R}{W}|Legendary Creature - Vampire Soldier|3|3|When Odric, Blood-Cursed enters the battlefield, create X Blood tokens, where X is the number of abilities from among flying, first strike, double strike, deathtouch, haste, hexproof, indestructible, lifelink, menace, reach, trample, and vigilance found among creatures you control.| +Old Rutstein|Innistrad: Crimson Vow|244|R|{1}{B}{G}|Legendary Creature - Human Peasant|1|4|When Old Rutstein enters the battlefield or at the beginning of your upkeep, mill a card. If a land card is milled this way, create a Treasure token. If a creature card is milled this way, create a 1/1 green Insect creature token. If a noncreature, nonland card is milled this way, create a Blood token.| +Olivia, Crimson Bride|Innistrad: Crimson Vow|245|M|{4}{B}{R}|Legendary Creature - Vampire Noble|3|4|Flying, haste$Whenever Olivia, Crimson Bride attacks, return target creature card from your graveyard to the battlefield tapped and attacking. It gains "When you don't control a legendary Vampire, exile this creature."| +Runo Stromkirk|Innistrad: Crimson Vow|246|R|{1}{U}{B}|Legendary Creature - Vampire Cleric|1|4|Flying$When Runo Stromkirk enters the battlefield, put up to one target creature card from your graveyard on top of your library.$At the beginning of your upkeep, look at the top card of your library. You may reveal that card. If a creature card with mana value 6 or greater is revealed this way, transform Runo Stromkirk.| +Krothuss, Lord of the Deep|Innistrad: Crimson Vow|246|R||Legendary Creature - Kraken Horror|3|5|Flying$Whenever Krothuss, Lord of the Deep attacks, create a tapped and attacking token that's a copy of another target attacking creature. If that creature is a Kraken, Leviathan, Octopus, or Serpent, create two of those tokens instead.| +Sigardian Paladin|Innistrad: Crimson Vow|247|U|{2}{G}{W}|Creature - Human Knight|4|4|As long as you've put one or more +1/+1 counters on a creature this turn, Sigardian Paladin has trample and lifelink.${1}{G}{W}: Target creature you control with a +1/+1 counter on it gains trample and lifelink until end of turn.| +Skull Skaab|Innistrad: Crimson Vow|248|U|{U}{B}|Creature - Zombie|2|2|Exploit$Whenever a creature you control exploits a nontoken creature, create a 2/2 black Zombie creature token.| +Torens, Fist of the Angels|Innistrad: Crimson Vow|249|R|{1}{G}{W}|Legendary Creature - Human Cleric|2|2|Training$Whenever you cast a creature spell, create a 1/1 green and white Human Soldier creature token with training.| +Vilespawn Spider|Innistrad: Crimson Vow|250|U|{G}{U}|Creature - Spider|2|3|Reach$At the beginning of your upkeep, mill a card.${2}{G}{U}, {T}, Sacrifice Vilespawn Spider: Create a 1/1 green Insect creature token for each creature card in your graveyard. Activate only as a sorcery.| +Wandering Mind|Innistrad: Crimson Vow|251|U|{1}{U}{R}|Creature - Horror|2|1|Flying$When Wandering Mind enters the battlefield, look at the top six cards of your library. You may reveal a noncreature, nonland card from among them and put it into your hand. Put the rest on the bottom of your library in a random order.| +Blood Servitor|Innistrad: Crimson Vow|252|C|{3}|Artifact Creature - Construct|2|2|When Blood Servitor enters the battlefield, create a Blood token.| +Boarded Window|Innistrad: Crimson Vow|253|U|{3}|Artifact|||Creatures attacking you get -1/-0.$At the beginning of each end step, if you were dealt 4 or more damage this turn, exile Boarded Window.| +Ceremonial Knife|Innistrad: Crimson Vow|254|C|{1}|Artifact - Equipment|||Equipped creature gets +1/+0 and has "Whenever this creature deals combat damage, create a Blood token."$Equip {2}| +Dollhouse of Horrors|Innistrad: Crimson Vow|255|R|{5}|Artifact|||{1}, {T}, Exile a creature card from your graveyard: Create a token that's a copy of the exiled card, except it's a 0/0 Construct artifact in addition to its other types and it has "This creature gets +1/+1 for each Construct you control." It gains haste until end of turn. Activate only as a sorcery.| +Foreboding Statue|Innistrad: Crimson Vow|256|U|{3}|Artifact Creature - Construct|1|2|{T}: Add one mana of any color. Put an omen counter on Foreboding Statue.$At the beginning of your end step, if there are three or more omen counters on Foreboding Statue, untap it, then transform it.| +Forsaken Thresher|Innistrad: Crimson Vow|256|U||Artifact Creature - Construct|5|5|At the beginning of your precombat main phase, add one mana of any color.| +Honored Heirloom|Innistrad: Crimson Vow|257|C|{3}|Artifact|||{T}: Add one mana of any color.${2}, {T}: Exile target card from a graveyard.| +Investigator's Journal|Innistrad: Crimson Vow|258|R|{2}|Artifact - Clue|||Investigator's Journal enters the battlefield with a number of suspect counters on it equal to the greatest number of creatures a player controls.${2}, {T}, Remove a suspect counter from Investigator's Journal: Draw a card.${2}, Sacrifice Investigator's Journal: Draw a card.| +Lantern of the Lost|Innistrad: Crimson Vow|259|U|{1}|Artifact|||When Lantern of the Lost enters the battlefield, exile target card from a graveyard.${1}, {T}, Exile Lantern of the Lost: Exile all cards from all graveyards, then draw a card.| +Wedding Invitation|Innistrad: Crimson Vow|260|C|{2}|Artifact|||When Wedding Invitation enters the battlefield, draw a card.${T}, Sacrifice Wedding Invitation: Target creature can't be blocked this turn. If it's a Vampire, it also gains lifelink until end of turn.| +Deathcap Glade|Innistrad: Crimson Vow|261|R||Land|||Deathcap Glade enters the battlefield tapped unless you control two or more other lands.${T}: Add {B} or {G}.| +Dreamroot Cascade|Innistrad: Crimson Vow|262|R||Land|||Dreamroot Cascade enters the battlefield tapped unless you control two or more other lands.${T}: Add {G} or {U}.| +Evolving Wilds|Innistrad: Crimson Vow|263|C||Land|||{T}, Sacrifice Evolving Wilds: Search your library for a basic land card, put it onto the battlefield tapped, then shuffle.| +Shattered Sanctum|Innistrad: Crimson Vow|264|R||Land|||Shattered Sanctum enters the battlefield tapped unless you control two or more other lands.${T}: Add {W} or {B}.| +Stormcarved Coast|Innistrad: Crimson Vow|265|R||Land|||Stormcarved Coast enters the battlefield tapped unless you control two or more other lands.${T}: Add {U} or {R}.| +Sundown Pass|Innistrad: Crimson Vow|266|R||Land|||Sundown Pass enters the battlefield tapped unless you control two or more other lands.${T}: Add {R} or {W}.| +Voldaren Estate|Innistrad: Crimson Vow|267|R||Land|||{T}: Add {C}.${T}, Pay 1 life: Add one mana of any color. Spend this mana only to cast a Vampire spell.${5}, {T}: Create a Blood token. This ability costs {1} less to activate for each Vampire you control.| +Plains|Innistrad: Crimson Vow|398|C||Basic Land - Plains|||({T}: Add {W}.)| +Island|Innistrad: Crimson Vow|399|C||Basic Land - Island|||({T}: Add {U}.)| +Swamp|Innistrad: Crimson Vow|400|C||Basic Land - Swamp|||({T}: Add {B}.)| +Mountain|Innistrad: Crimson Vow|401|C||Basic Land - Mountain|||({T}: Add {R}.)| +Forest|Innistrad: Crimson Vow|402|C||Basic Land - Forest|||({T}: Add {G}.)| +Millicent, Restless Revenant|Crimson Vow Commander|1|M|{5}{W}{U}|Legendary Creature - Spirit Soldier|4|4|This spell costs {1} less to cast for each Spirit you control.$Flying$Whenever Millicent, Restless Revenant or another nontoken Spirit you control dies or deals combat damage to a player, create a 1/1 white Spirit creature token with flying.| +Strefan, Maurer Progenitor|Crimson Vow Commander|2|M|{2}{B}{R}|Legendary Creature - Vampire Noble|3|2|Flying$At the beginning of your end step, create a Blood token for each player who lost life this turn.$Whenever Strefan attacks, you may sacrifice two Blood tokens. If you do, you may put a Vampire card from your hand onto the battlefield tapped and attacking. It gains indestructible until end of turn.| +Donal, Herald of Wings|Crimson Vow Commander|3|M|{2}{U}{U}|Legendary Creature - Human Wizard|3|3|Whenever you cast a nonlegendary creature spell with flying, you may copy it, except the copy is a 1/1 Spirit in addition to its other types. Do this only once each turn.| +Timothar, Baron of Bats|Crimson Vow Commander|4|M|{4}{B}{B}|Legendary Creature - Vampire Noble|4|4|Ward—Discard a card.$Whenever another nontoken Vampire you control dies, you may pay {1} and exile it. If you do, create a 1/1 black Bat creature token with flying. It gains "When this creature deals combat damage to a player, sacrifice it and return the exiled card to the battlefield tapped."| +Drogskol Reinforcements|Crimson Vow Commander|5|R|{3}{W}|Creature - Spirit Soldier|2|2|Melee$Other Spirits you control have melee.$Prevent all noncombat damage that would be dealt to Spirits you control.| +Haunted Library|Crimson Vow Commander|6|R|{1}{W}|Enchantment|||Whenever a creature an opponent controls dies, you may pay {1}. If you do, create a 1/1 white Spirit creature token with flying.| +Priest of the Blessed Graf|Crimson Vow Commander|7|R|{2}{W}|Creature - Human Cleric|1|2|At the beginning of your end step, create X 1/1 white Spirit creature tokens with flying, where X is the number of opponents who control more lands than you.| +Rhoda, Geist Avenger|Crimson Vow Commander|8|R|{3}{W}|Legendary Creature - Human Soldier|3|3|Partner with Timin, Youthful Geist$Vigilance$Whenever a creature an opponent controls becomes tapped, if it isn't being declared as an attacker, put a +1/+1 counter on Rhoda, Geist Avenger.| +Storm of Souls|Crimson Vow Commander|9|R|{4}{W}{W}|Sorcery|||Return all creature cards from your graveyard to the battlefield. Each of them is a 1/1 Spirit with flying in addition to its other types. Exile Storm of Souls.| +Sudden Salvation|Crimson Vow Commander|10|R|{2}{W}{W}|Instant|||Choose up to three target permanent cards in graveyards that were put there from the battlefield this turn. Return them to the battlefield tapped under their owners' control. You draw a card for each opponent who controls one or more of those permanents.| +Breath of the Sleepless|Crimson Vow Commander|11|R|{3}{U}|Enchantment|||You may cast Spirit spells as though they had flash.$Whenever you cast a creature spell during an opponent's turn, tap up to one target creature.| +Ethereal Investigator|Crimson Vow Commander|12|R|{3}{U}|Creature - Spirit|2|3|Flying$When Ethereal Investigator enters the battlefield, investigate X times, where X is the number of opponents you have.$Whenever you draw your second card each turn, create a 1/1 white Spirit creature token with flying.| +Haunting Imitation|Crimson Vow Commander|13|R|{2}{U}|Sorcery|||Each player reveals the top card of their library. For each creature card revealed this way, create a token that's a copy of that card, except it's 1/1, it's a Spirit in addition to its other types, and it has flying. If no creature cards were revealed this way, return Haunting Imitation to its owner's hand.| +Occult Epiphany|Crimson Vow Commander|14|R|{X}{U}|Instant|||Draw X cards, then discard X cards. Create a 1/1 white Spirit creature token with flying for each card type among cards discarded this way.| +Spectral Arcanist|Crimson Vow Commander|15|R|{3}{U}|Creature - Spirit Wizard|3|2|Flying$When Spectral Arcanist enters the battlefield, you may cast an instant or sorcery spell with mana value less than or equal to the number of Spirits you control from a graveyard without paying its mana cost. If that spell would be put into a graveyard, exile it instead.| +Timin, Youthful Geist|Crimson Vow Commander|16|R|{4}{U}|Legendary Creature - Spirit|3|4|Partner with Rhoda, Geist Avenger$Flying$At the beginning of each combat, tap up to one target creature.| +Crossway Troublemakers|Crimson Vow Commander|17|R|{5}{B}|Creature - Vampire|5|5|Attacking Vampires you control have deathtouch and lifelink.$Whenever a Vampire you control dies, you may pay 2 life. If you do, draw a card.| +Glass-Cast Heart|Crimson Vow Commander|18|R|{2}{B}|Artifact|||Whenever one or more Vampires you control attack, create a Blood token.${B}, {T}, Pay 1 life: Create a 1/1 white and black Vampire creature token with lifelink.${B}{B, {T}, Sacrifice Glass-Cast Heart and thirteen Blood tokens: Each opponent loses 13 life and you gain 13 life.| +Kamber, the Plunderer|Crimson Vow Commander|19|R|{3}{B}|Legendary Creature - Vampire Rogue|3|4|Partner with Laurine, the Diversion$Lifelink$Whenever a creature an opponent controls dies, you gain 1 life and create a Blood token.| +Olivia's Wrath|Crimson Vow Commander|20|R|{4}{B}|Sorcery|||Each non-Vampire creature gets -X/-X until end of turn, where X is the number of Vampires you control.| +Predators' Hour|Crimson Vow Commander|21|R|{1}{B}|Sorcery|||Until end of turn, creatures you control gain menace and "Whenever this creature deals combat damage to a player, exile the top card of that player's library face down. You may look at and play that card for as long as it remains exiled, and you may spend mana as though it were mana of any color to cast that spell."| +Shadowgrange Archfiend|Crimson Vow Commander|22|R|{6}{B}|Creature - Demon|8|4|When Shadowgrange Archfiend enters the battlefield, each opponent sacrifices a creature with the greatest power among creatures they control. You gain life equal to the greatest power among creatures sacrificed this way.$Madness—{2}{B}, Pay 8 life.| +Arterial Alchemy|Crimson Vow Commander|23|R|{2}{R}|Enchantment|||When Arterial Alchemy enters the battlefield, create a Blood token for each opponent you have.$Blood tokens you control are Equipment in addition to their other types and have "Equipped creature gets +2/+0" and equip {2}.| +Imposing Grandeur|Crimson Vow Commander|24|R|{4}{R}|Sorcery|||Each player may discard their hand and draw cards equal to the greatest mana value of a commander they own on the battlefield or in the command zone.| +Laurine, the Diversion|Crimson Vow Commander|25|R|{2}{R}|Legendary Creature - Human Rogue|3|3|Partner with Kamber, the Plunderer$First strike${2}, Sacrifice an artifact or creature: Goad target creature.| +Markov Enforcer|Crimson Vow Commander|26|R|{4}{R}{R}|Creature - Vampire Soldier|6|6|Whenever Markov Enforcer or another Vampire enters the battlefield under your control, Markov Enforcer fights up to one target creature an opponent controls.$Whenever a creature dealt damage by Markov Enforcer this turn dies, create a Blood token.| +Midnight Arsonist|Crimson Vow Commander|27|R|{3}{R}|Creature - Vampire|3|2|When Midnight Arsonist enters the battlefield, destroy up to X target artifacts without mana abilities, where X is the number of Vampires you control.| +Scion of Opulence|Crimson Vow Commander|28|R|{2}{R}|Creature - Vampire Noble|3|1|Whenever Scion of Opulence or another nontoken Vampire you control dies, create a Treasure token.${R}, Sacrifice two artifacts: Exile the top card of your library. You may play that card this turn.| +Disorder in the Court|Crimson Vow Commander|29|R|{X}{W}{U}|Instant|||Exile X target creatures, then investigate X times. Return the exiled cards to the battlefield tapped under their owners' control at the beginning of the next end step.| +Sinister Waltz|Crimson Vow Commander|30|R|{3}{B}{R}|Sorcery|||Choose three target creature cards in your graveyard. Return two of them at random to the battlefield and put the other on the bottom of your library.| +Breathkeeper Seraph|Crimson Vow Commander|31|R|{4}{W}{W}|Creature - Angel|4|4|Flying, soulbond$As long as Breathkeeper Seraph is paired with another creature, each of those creatures has "When this creature dies, you may return it to the battlefield under its owner's control at the beginning of your next upkeep."| +Wedding Ring|Crimson Vow Commander|32|M|{2}{W}{W}|Artifact|||When Wedding Ring enters the battlefield, if it was cast, target opponent creates a token that's a copy of it.$Whenever an opponent who controls an artifact named Wedding Ring draws a card during their turn, you draw a card.$Whenever an opponent who controls an artifact named Wedding Ring gains life during their turn, you gain that much life.| +Imperious Mindbreaker|Crimson Vow Commander|33|R|{1}{U}{U}|Creature - Human Wizard|1|4|Soulbond$As long as Imperious Mindbreaker is paired with another creature, each of those creatures has "Whenever this creature attacks, each opponent mills cards equal to its toughness."| +Doom Weaver|Crimson Vow Commander|34|R|{4}{B}{B}|Creature - Spider Horror|1|8|Reach$Soulbond$As long as Doom Weaver is paired with another creature, each of those creatures has "When this creature dies, draw cards equal to its power."| +Mirage Phalanx|Crimson Vow Commander|35|R|{4}{R}{R}|Creature - Human Soldier|4|4|Soulbond$As long as Mirage Phalanx is paired with another creature, each of those creatures has "At the beginning of combat on your turn, create a token that's a copy of this creature, except it has haste and loses soulbond. Exile it at end of combat."| +Hollowhenge Overlord|Crimson Vow Commander|36|R|{4}{G}{G}|Creature - Wolf|4|4|Flash$At the beginning of your upkeep, for each creature you control that's a Wolf or a Werewolf, create a 2/2 green Wolf creature token.| +Thundering Mightmare|Crimson Vow Commander|37|R|{4}{G}|Creature - Horse Spirit|3|3|Soulbond$As long as Thundering Mightmare is paired with another creature, each of those creatures has "Whenever an opponent casts a spell, put a +1/+1 counter on this creature."| +Umbris, Fear Manifest|Crimson Vow Commander|38|M|{3}{U}{B}|Legendary Creature - Nightmare Horror|1|1|Umbris, Fear Manifest gets +1/+1 for each card your opponents own in exile.$Whenever Umbris or another Nightmare or Horror enters the battlefield under your control, target opponent exiles cards from the top of their library until they exile a land card.| +Angel of Flight Alabaster|Crimson Vow Commander|77|R|{4}{W}|Creature - Angel|4|4|Flying$At the beginning of your upkeep, return target Spirit card from your graveyard to your hand.| +Benevolent Offering|Crimson Vow Commander|78|R|{3}{W}|Instant|||Choose an opponent. You and that player each create three 1/1 white Spirit creature tokens with flying.$Choose an opponent. You gain 2 life for each creature you control and that player gains 2 life for each creature they control.| +Boreas Charger|Crimson Vow Commander|79|R|{2}{W}|Creature - Pegasus|2|1|Flying$When Boreas Charger leaves the battlefield, choose an opponent who controls more lands than you. Search your library for a number of Plains cards equal to the difference, reveal those cards, put one of them onto the battlefield tapped and the rest into your hand, then shuffle.| +Bygone Bishop|Crimson Vow Commander|80|R|{2}{W}|Creature - Spirit Cleric|2|3|Flying$Whenever you cast a creature spell with mana value 3 or less, investigate.| +Crush Contraband|Crimson Vow Commander|81|U|{3}{W}|Instant|||Choose one or both —$• Exile target artifact.$• Exile target enchantment.| +Custodi Soulbinders|Crimson Vow Commander|82|R|{3}{W}|Creature - Human Cleric|0|0|Custodi Soulbinders enters the battlefield with X +1/+1 counters on it, where X is the number of other creatures on the battlefield.${2}{W}, Remove a +1/+1 counter from Custodi Soulbinders: Create a 1/1 white Spirit creature token with flying.| +Custodi Squire|Crimson Vow Commander|83|C|{4}{W}|Creature - Spirit Cleric|3|3|Flying$Will of the council — When Custodi Squire enters the battlefield, starting with you, each player votes for an artifact, creature, or enchantment card in your graveyard. Return each card with the most votes or tied for most votes to your hand.| +Darksteel Mutation|Crimson Vow Commander|84|U|{1}{W}|Enchantment - Aura|||Enchant creature$Enchanted creature is an Insect artifact creature with base power and toughness 0/1 and has indestructible, and it loses all other abilities, card types, and creature types.| +Fell the Mighty|Crimson Vow Commander|85|R|{4}{W}|Sorcery|||Destroy all creatures with power greater than target creature's power.| +Field of Souls|Crimson Vow Commander|86|U|{2}{W}{W}|Enchantment|||Whenever a nontoken creature is put into your graveyard from the battlefield, create a 1/1 white Spirit creature token with flying.| +Ghostly Prison|Crimson Vow Commander|87|U|{2}{W}|Enchantment|||Creatures can't attack you unless their controller pays {2} for each creature they control that's attacking you.| +Hallowed Spiritkeeper|Crimson Vow Commander|88|R|{1}{W}{W}|Creature - Avatar|3|2|Vigilance$When Hallowed Spiritkeeper dies, create X 1/1 white Spirit creature tokens with flying, where X is the number of creature cards in your graveyard.| +Hanged Executioner|Crimson Vow Commander|89|R|{2}{W}|Creature - Spirit|1|1|Flying$When Hanged Executioner enters the battlefield, create a 1/1 white Spirit creature token with flying.${3}{W}, Exile Hanged Executioner: Exile target creature.| +Karmic Guide|Crimson Vow Commander|90|R|{3}{W}{W}|Creature - Angel Spirit|2|2|Flying, protection from black$Echo {3}{W}{W}$When Karmic Guide enters the battlefield, return target creature card from your graveyard to the battlefield.| +Kirtar's Wrath|Crimson Vow Commander|91|R|{4}{W}{W}|Sorcery|||Destroy all creatures. They can't be regenerated.$Threshold — If seven or more cards are in your graveyard, instead destroy all creatures, then create two 1/1 white Spirit creature tokens with flying. Creatures destroyed this way can't be regenerated.| +Knight of the White Orchid|Crimson Vow Commander|92|R|{W}{W}|Creature - Human Knight|2|2|First strike$When Knight of the White Orchid enters the battlefield, if an opponent controls more lands than you, you may search your library for a Plains card, put it onto the battlefield, then shuffle.| +Mentor of the Meek|Crimson Vow Commander|93|R|{2}{W}|Creature - Human Soldier|2|2|Whenever another creature with power 2 or less enters the battlefield under your control, you may pay {1}. If you do, draw a card.| +Mirror Entity|Crimson Vow Commander|94|R|{2}{W}|Creature - Shapeshifter|1|1|Changeling${X}: Until end of turn, creatures you control have base power and toughness X/X and gain all creature types.| +Oyobi, Who Split the Heavens|Crimson Vow Commander|95|R|{6}{W}|Legendary Creature - Spirit|3|6|Flying$Whenever you cast a Spirit or Arcane spell, create a 3/3 white Spirit creature token with flying.| +Promise of Bunrei|Crimson Vow Commander|96|R|{2}{W}|Enchantment|||When a creature you control dies, sacrifice Promise of Bunrei. If you do, create four 1/1 colorless Spirit creature tokens.| +Remorseful Cleric|Crimson Vow Commander|97|R|{1}{W}|Creature - Spirit Cleric|2|1|Flying$Sacrifice Remorseful Cleric: Exile all cards from target player's graveyard.| +Spectral Shepherd|Crimson Vow Commander|98|U|{2}{W}|Creature - Spirit|2|2|Flying${1}{U}: Return target Spirit you control to its owner's hand.| +Swords to Plowshares|Crimson Vow Commander|99|U|{W}|Instant|||Exile target creature. Its controller gains life equal to its power.| +Twilight Drover|Crimson Vow Commander|100|R|{2}{W}|Creature - Spirit|1|1|Whenever a creature token leaves the battlefield, put a +1/+1 counter on Twilight Drover.${2}{W}, Remove a +1/+1 counter from Twilight Drover: Create two 1/1 white Spirit creature tokens with flying.| +Windborn Muse|Crimson Vow Commander|101|R|{3}{W}|Creature - Spirit|2|3|Flying$Creatures can't attack you unless their controller pays {2} for each creature they control that's attacking you.| +Arcane Denial|Crimson Vow Commander|102|C|{1}{U}|Instant|||Counter target spell. Its controller may draw up to two cards at the beginning of the next turn's upkeep.$You draw a card at the beginning of the next turn's upkeep.| +Distant Melody|Crimson Vow Commander|103|C|{3}{U}|Sorcery|||Choose a creature type. Draw a card for each permanent you control of that type.| +Flood of Tears|Crimson Vow Commander|104|R|{4}{U}{U}|Sorcery|||Return all nonland permanents to their owners' hands. If you return four or more nontoken permanents you control this way, you may put a permanent card from your hand onto the battlefield.| +Ghostly Pilferer|Crimson Vow Commander|105|R|{1}{U}|Creature - Spirit Rogue|2|1|Whenever Ghostly Pilferer becomes untapped, you may pay {2}. If you do, draw a card.$Whenever an opponent casts a spell from anywhere other than their hand, draw a card.$Discard a card: Ghostly Pilferer can't be blocked this turn.| +Imprisoned in the Moon|Crimson Vow Commander|106|R|{2}{U}|Enchantment - Aura|||Enchant creature, land, or planeswalker$Enchanted permanent is a colorless land with "{T}: Add {C}" and loses all other card types and abilities.| +Kami of the Crescent Moon|Crimson Vow Commander|107|R|{U}{U}|Legendary Creature - Spirit|1|3|At the beginning of each player's draw step, that player draws an additional card.| +Midnight Clock|Crimson Vow Commander|108|R|{2}{U}|Artifact|||{T}: Add {U}.${2}{U}: Put an hour counter on Midnight Clock.$At the beginning of each upkeep, put an hour counter on Midnight Clock.$When the twelfth hour counter is put on Midnight Clock, shuffle your hand and graveyard into your library, then draw seven cards. Exile Midnight Clock.| +Nebelgast Herald|Crimson Vow Commander|109|U|{2}{U}|Creature - Spirit|2|1|Flash$Flying$Whenever Nebelgast Herald or another Spirit enters the battlefield under your control, tap target creature an opponent controls.| +Rattlechains|Crimson Vow Commander|110|R|{1}{U}|Creature - Spirit|2|1|Flash$Flying$When Rattlechains enters the battlefield, target Spirit gains hexproof until end of turn.$You may cast Spirit spells as though they had flash.| +Reconnaissance Mission|Crimson Vow Commander|111|U|{2}{U}{U}|Enchantment|||Whenever a creature you control deals combat damage to a player, you may draw a card.$Cycling {2}| +Shacklegeist|Crimson Vow Commander|112|R|{1}{U}|Creature - Spirit|2|2|Flying$Shacklegeist can block only creatures with flying.$Tap two untapped Spirits you control: Tap target creature you don't control.| +Sire of the Storm|Crimson Vow Commander|113|U|{4}{U}{U}|Creature - Spirit|3|3|Flying$Whenever you cast a Spirit or Arcane spell, you may draw a card.| +Spectral Sailor|Crimson Vow Commander|114|U|{U}|Creature - Spirit Pirate|1|1|Flash$Flying${3}{U}: Draw a card.| +Supreme Phantom|Crimson Vow Commander|115|R|{1}{U}|Creature - Spirit|1|3|Flying$Other Spirits you control get +1/+1.| +Verity Circle|Crimson Vow Commander|116|R|{2}{U}|Enchantment|||Whenever a creature an opponent controls becomes tapped, if it isn't being declared as an attacker, you may draw a card.${4}{U}: Tap target creature without flying.| +Ancient Craving|Crimson Vow Commander|117|U|{3}{B}|Sorcery|||You draw three cards and you lose 3 life.| +Anowon, the Ruin Sage|Crimson Vow Commander|118|R|{3}{B}{B}|Legendary Creature - Vampire Shaman|4|3|At the beginning of your upkeep, each player sacrifices a non-Vampire creature.| +Blood Artist|Crimson Vow Commander|119|U|{1}{B}|Creature - Vampire|0|1|Whenever Blood Artist or another creature dies, target player loses 1 life and you gain 1 life.| +Bloodline Necromancer|Crimson Vow Commander|120|U|{4}{B}|Creature - Vampire Wizard|3|2|Lifelink$When Bloodline Necromancer enters the battlefield, you may return target Vampire or Wizard creature card from your graveyard to the battlefield.| +Bloodlord of Vaasgoth|Crimson Vow Commander|121|M|{3}{B}{B}|Creature - Vampire Warrior|3|3|Bloodthirst 3$Flying$Whenever you cast a Vampire creature spell, it gains bloodthirst 3.| +Bloodtracker|Crimson Vow Commander|122|R|{3}{B}|Creature - Vampire Wizard|2|2|Flying${B}, Pay 2 life: Put a +1/+1 counter on Bloodtracker.$When Bloodtracker leaves the battlefield, draw a card for each +1/+1 counter on it.| +Butcher of Malakir|Crimson Vow Commander|123|R|{5}{B}{B}|Creature - Vampire Warrior|5|4|Flying$Whenever Butcher of Malakir or another creature you control dies, each opponent sacrifices a creature.| +Champion of Dusk|Crimson Vow Commander|124|R|{3}{B}{B}|Creature - Vampire Knight|4|4|When Champion of Dusk enters the battlefield, you draw X cards and you lose X life, where X is the number of Vampires you control.| +Cordial Vampire|Crimson Vow Commander|125|R|{B}{B}|Creature - Vampire|1|1|Whenever Cordial Vampire or another creature dies, put a +1/+1 counter on each Vampire you control.| +Damnable Pact|Crimson Vow Commander|126|R|{X}{B}{B}|Sorcery|||Target player draws X cards and loses X life.| +Dark Impostor|Crimson Vow Commander|127|R|{2}{B}|Creature - Vampire Assassin|2|2|{4}{B}{B}: Exile target creature and put a +1/+1 counter on Dark Impostor.$Dark Impostor has all activated abilities of all creature cards exiled with it.| +Falkenrath Noble|Crimson Vow Commander|128|U|{3}{B}|Creature - Vampire Noble|2|2|Flying$Whenever Falkenrath Noble or another creature dies, target player loses 1 life and you gain 1 life.| +Feed the Swarm|Crimson Vow Commander|129|C|{1}{B}|Sorcery|||Destroy target creature or enchantment an opponent controls. You lose life equal to that permanent's mana value.| +Indulgent Aristocrat|Crimson Vow Commander|130|U|{B}|Creature - Vampire Noble|1|1|Lifelink${2}, Sacrifice a creature: Put a +1/+1 counter on each Vampire you control.| +Malakir Bloodwitch|Crimson Vow Commander|131|R|{3}{B}{B}|Creature - Vampire Shaman|4|4|Flying, protection from white$When Malakir Bloodwitch enters the battlefield, each opponent loses life equal to the number of Vampires you control. You gain life equal to the life lost this way.| +Necropolis Regent|Crimson Vow Commander|132|M|{3}{B}{B}{B}|Creature - Vampire|6|5|Flying$Whenever a creature you control deals combat damage to a player, put that many +1/+1 counters on it.| +Night's Whisper|Crimson Vow Commander|133|C|{1}{B}|Sorcery|||You draw two cards and you lose 2 life.| +Nirkana Revenant|Crimson Vow Commander|134|M|{4}{B}{B}|Creature - Vampire Shade|4|4|Whenever you tap a Swamp for mana, add an additional {B}.${B}: Nirkana Revenant gets +1/+1 until end of turn.| +Patron of the Vein|Crimson Vow Commander|135|R|{4}{B}{B}|Creature - Vampire Shaman|4|4|Flying$When Patron of the Vein enters the battlefield, destroy target creature an opponent controls.$Whenever a creature an opponent controls dies, exile it and put a +1/+1 counter on each Vampire you control.| +Sanctum Seeker|Crimson Vow Commander|136|R|{2}{B}{B}|Creature - Vampire Knight|3|4|Whenever a Vampire you control attacks, each opponent loses 1 life and you gain 1 life.| +Stromkirk Condemned|Crimson Vow Commander|137|R|{B}{B}|Creature - Vampire Horror|2|2|Discard a card: Vampires you control get +1/+1 until end of turn. Activate only once each turn.| +Underworld Connections|Crimson Vow Commander|138|R|{1}{B}{B}|Enchantment - Aura|||Enchant land$Enchanted land has "{T}, Pay 1 life: Draw a card."| +Urge to Feed|Crimson Vow Commander|139|U|{B}{B}|Instant|||Target creature gets -3/-3 until end of turn. You may tap any number of untapped Vampire creatures you control. If you do, put a +1/+1 counter on each of those Vampires.| +Vampire Nighthawk|Crimson Vow Commander|140|U|{1}{B}{B}|Creature - Vampire Shaman|2|3|Flying, deathtouch, lifelink| +Anje's Ravager|Crimson Vow Commander|141|R|{2}{R}|Creature - Vampire Berserker|3|3|Anje's Ravager attacks each combat if able.$Whenever Anje's Ravager attacks, discard your hand, then draw three cards.$Madness {1}{R}| +Avacyn's Judgment|Crimson Vow Commander|142|R|{1}{R}|Sorcery|||Madness {X}{R}$Avacyn's Judgment deals 2 damage divided as you choose among any number of targets. If this spell's madness cost was paid, it deals X damage divided as you choose among those permanents and/or players instead.| +Blasphemous Act|Crimson Vow Commander|143|R|{8}{R}|Sorcery|||This spell costs {1} less to cast for each creature on the battlefield.$Blasphemous Act deals 13 damage to each creature.| +Bloodsworn Steward|Crimson Vow Commander|144|R|{2}{R}{R}|Creature - Vampire Knight|4|4|Flying$Commander creatures you control get +2/+2 and have haste.| +Crimson Honor Guard|Crimson Vow Commander|145|R|{3}{R}{R}|Creature - Vampire Knight|4|5|Trample$At the beginning of each player's end step, Crimson Honor Guard deals 4 damage to that player unless they control a commander.| +Falkenrath Gorger|Crimson Vow Commander|146|R|{R}|Creature - Vampire Berserker|2|1|Each Vampire creature card you own that isn't on the battlefield has madness. The madness cost is equal to its mana cost.| +Mob Rule|Crimson Vow Commander|147|R|{4}{R}{R}|Sorcery|||Choose one —$• Gain control of all creatures with power 4 or greater until end of turn. Untap those creatures. They gain haste until end of turn.$• Gain control of all creatures with power 3 or less until end of turn. Untap those creatures. They gain haste until end of turn.| +Molten Echoes|Crimson Vow Commander|148|R|{2}{R}{R}|Enchantment|||As Molten Echoes enters the battlefield, choose a creature type.$Whenever a nontoken creature of the chosen type enters the battlefield under your control, create a token that's a copy of that creature. That token gains haste. Exile it at the beginning of the next end step.| +Rakish Heir|Crimson Vow Commander|149|U|{2}{R}|Creature - Vampire|2|2|Whenever a Vampire you control deals combat damage to a player, put a +1/+1 counter on it.| +Stensia Masquerade|Crimson Vow Commander|150|U|{2}{R}|Enchantment|||Attacking creatures you control have first strike.$Whenever a Vampire you control deals combat damage to a player, put a +1/+1 counter on it.$Madness {2}{R}| +Stromkirk Occultist|Crimson Vow Commander|151|R|{2}{R}|Creature - Vampire Horror|3|2|Trample$Whenever Stromkirk Occultist deals combat damage to a player, exile the top card of your library. Until end of turn, you may play that card.$Madness {1}{R}| +Vandalblast|Crimson Vow Commander|152|U|{R}|Sorcery|||Destroy target artifact you don't control.$Overload {4}{R}| +Dovin, Grand Arbiter|Crimson Vow Commander|153|M|{1}{W}{U}|Legendary Planeswalker - Dovin|3|+1: Until end of turn, whenever a creature you control deals combat damage to a player, put a loyalty counter on Dovin, Grand Arbiter.$−1: Create a 1/1 colorless Thopter artifact creature token with flying. You gain 1 life.$−7: Look at the top ten cards of your library. Put three of them into your hand and the rest on the bottom of your library in a random order.| +Drogskol Captain|Crimson Vow Commander|154|U|{1}{W}{U}|Creature - Spirit Soldier|2|2|Flying$Other Spirit creatures you control get +1/+1 and have hexproof.| +Geist of Saint Traft|Crimson Vow Commander|155|M|{1}{W}{U}|Legendary Creature - Spirit Cleric|2|2|Hexproof$Whenever Geist of Saint Traft attacks, create a 4/4 white Angel creature token with flying that's tapped and attacking. Exile that token at end of combat.| +Rakdos Charm|Crimson Vow Commander|156|U|{B}{R}|Instant|||Choose one —$• Exile all cards from target player's graveyard.$• Destroy target artifact.$• Each creature deals 1 damage to its controller.| +Stromkirk Captain|Crimson Vow Commander|157|U|{1}{B}{R}|Creature - Vampire Soldier|2|2|First strike$Other Vampire creatures you control get +1/+1 and have first strike.| +Vampiric Dragon|Crimson Vow Commander|158|R|{6}{B}{R}|Creature - Vampire Dragon|5|5|Flying$Whenever a creature dealt damage by Vampiric Dragon this turn dies, put a +1/+1 counter on Vampiric Dragon.${1}{R}: Vampiric Dragon deals 1 damage to target creature.| +Arcane Signet|Crimson Vow Commander|159|C|{2}|Artifact|||{T}: Add one mana of any color in your commander's color identity.| +Azorius Locket|Crimson Vow Commander|160|C|{3}|Artifact|||{T}: Add {W} or {U}.${W/U}{W/U}{W/U}{W/U}, {T}, Sacrifice Azorius Locket: Draw two cards.| +Azorius Signet|Crimson Vow Commander|161|U|{2}|Artifact|||{1}, {T}: Add {W}{U}.| +Charcoal Diamond|Crimson Vow Commander|162|C|{2}|Artifact|||Charcoal Diamond enters the battlefield tapped.${T}: Add {B}.| +Commander's Sphere|Crimson Vow Commander|163|C|{3}|Artifact|||{T}: Add one mana of any color in your commander's color identity.$Sacrifice Commander's Sphere: Draw a card.| +Fire Diamond|Crimson Vow Commander|164|C|{2}|Artifact|||Fire Diamond enters the battlefield tapped.${T}: Add {R}.| +Marble Diamond|Crimson Vow Commander|165|C|{2}|Artifact|||Marble Diamond enters the battlefield tapped.${T}: Add {W}.| +Rakdos Signet|Crimson Vow Commander|166|U|{2}|Artifact|||{1}, {T}: Add {B}{R}.| +Sky Diamond|Crimson Vow Commander|167|C|{2}|Artifact|||Sky Diamond enters the battlefield tapped.${T}: Add {U}.| +Sol Ring|Crimson Vow Commander|168|U|{1}|Artifact|||{T}: Add {C}{C}.| +Swiftfoot Boots|Crimson Vow Commander|169|U|{2}|Artifact - Equipment|||Equipped creature has hexproof and haste.$Equip {1}| +Unstable Obelisk|Crimson Vow Commander|170|U|{3}|Artifact|||{T}: Add {C}.${7}, {T}, Sacrifice Unstable Obelisk: Destroy target permanent.| +Azorius Chancery|Crimson Vow Commander|171|U||Land|||Azorius Chancery enters the battlefield tapped.$When Azorius Chancery enters the battlefield, return a land you control to its owner's hand.${T}: Add {W}{U}.| +Command Tower|Crimson Vow Commander|172|C||Land|||{T}: Add one mana of any color in your commander's color identity.| +Exotic Orchard|Crimson Vow Commander|173|R||Land|||{T}: Add one mana of any color that a land an opponent controls could produce.| +Foreboding Ruins|Crimson Vow Commander|174|R||Land|||As Foreboding Ruins enters the battlefield, you may reveal a Swamp or Mountain card from your hand. If you don't, Foreboding Ruins enters the battlefield tapped.${T}: Add {B} or {R}.| +Moorland Haunt|Crimson Vow Commander|175|R||Land|||{T}: Add {C}.${W}{U}, {T}, Exile a creature card from your graveyard: Create a 1/1 white Spirit creature token with flying.| +Myriad Landscape|Crimson Vow Commander|176|U||Land|||Myriad Landscape enters the battlefield tapped.${T}: Add {C}.${2}, {T}, Sacrifice Myriad Landscape: Search your library for up to two basic land cards that share a land type, put them onto the battlefield tapped, then shuffle.| +Path of Ancestry|Crimson Vow Commander|177|C||Land|||Path of Ancestry enters the battlefield tapped.${T}: Add one mana of any color in your commander's color identity. When that mana is spent to cast a creature spell that shares a creature type with your commander, scry 1.| +Port Town|Crimson Vow Commander|178|R||Land|||As Port Town enters the battlefield, you may reveal a Plains or Island card from your hand. If you don't, Port Town enters the battlefield tapped.${T}: Add {W} or {U}.| +Prairie Stream|Crimson Vow Commander|179|R||Land - Plains Island|||({T}: Add {W} or {U}.)$Prairie Stream enters the battlefield tapped unless you control two or more basic lands.| +Rakdos Carnarium|Crimson Vow Commander|180|U||Land|||Rakdos Carnarium enters the battlefield tapped.$When Rakdos Carnarium enters the battlefield, return a land you control to its owner's hand.${T}: Add {B}{R}.| +Shadowblood Ridge|Crimson Vow Commander|181|R||Land|||{1}, {T}: Add {B}{R}.| +Skycloud Expanse|Crimson Vow Commander|182|R||Land|||{1}, {T}: Add {W}{U}.| +Smoldering Marsh|Crimson Vow Commander|183|R||Land - Swamp Mountain|||({T}: Add {B} or {R}.)$Smoldering Marsh enters the battlefield tapped unless you control two or more basic lands.| +Tainted Peak|Crimson Vow Commander|184|U||Land|||{T}: Add {C}.${T}: Add {B} or {R}. Activate only if you control a Swamp.| +Temple of Enlightenment|Crimson Vow Commander|185|R||Land|||Temple of Enlightenment enters the battlefield tapped.$When Temple of Enlightenment enters the battlefield, scry 1.${T}: Add {W} or {U}.| +Temple of Malice|Crimson Vow Commander|186|R||Land|||Temple of Malice enters the battlefield tapped.$When Temple of Malice enters the battlefield, scry 1.${T}: Add {B} or {R}.| +Temple of the False God|Crimson Vow Commander|187|U||Land|||{T}: Add {C}{C}. Activate only if you control five or more lands.| +Unclaimed Territory|Crimson Vow Commander|188|U||Land|||As Unclaimed Territory enters the battlefield, choose a creature type.${T}: Add {C}.${T}: Add one mana of any color. Spend this mana only to cast a creature spell of the chosen type.| +Assembled Ensemble|Unfinity|3|U|{4}{W}|Artifact Creature - Clown Robot Bard|*|6|Vigilance$Assembled Ensemble's power is equal to the number of Robots you control.$Whenever you cast a spell with an artifact creature in its art, create a 1/1 white Clown Robot artifact creature token.| +Saw in Half|Unfinity|88|R|{2}{B}|Instant|||Destroy target creature. If that creature dies this way, its controller creates two tokens that are copies of that creature, except their base power is half that creature's power their base toughness is half that creature's toughness. Round up each time.| +Killer Cosplay|Unfinity|143|M|{G}|Artifact - Equipment|||Whenever Killer Cosplay becomes attached to a creature, choose a creature card name with an identical mana cost. That creature becomes a copy of the card with the chosen name until Killer Cosplay becomes unattached from it.$Equip {3}| +The Space Family Goblinson|Unfinity|179|U|{2}{R}{G}|Legendary Creature - Goblin Guest|1|1|The Space Family Goblinson has trample as long as you've rolled three or more dice this turn.$Whenever you roll a die, put a +1/+1 counter on The Space Family Goblinson.| +Island|Unfinity|236|C||Basic Land - Island|||({T}: Add {U}.)| +Swamp|Unfinity|237|C||Basic Land - Swamp|||({T}: Add {B}.)| +Mountain|Unfinity|238|C||Basic Land - Mountain|||({T}: Add {R}.)| +Forest|Unfinity|239|C||Basic Land - Forest|||({T}: Add {G}.)| +Plains|Unfinity|240|C||Basic Land - Plains|||({T}: Add {W}.)| +Hallowed Fountain|Unfinity|277|R||Land - Plains Island|||({T}: Add {W} or {U}.)$As Hallowed Fountain enters the battlefield, you may pay 2 life. If you don't, it enters the battlefield tapped.| +Watery Grave|Unfinity|278|R||Land - Island Swamp|||({T}: Add {U} or {B}.)$As Watery Grave enters the battlefield, you may pay 2 life. If you don't, it enters the battlefield tapped.| +Blood Crypt|Unfinity|279|C||Land - Swamp Mountain|||({T}: Add {B} or {R}.)$As Blood Crypt enters the battlefield, you may pay 2 life. If you don't, it enters the battlefield tapped.| +Stomping Ground|Unfinity|280|R||Land - Mountain Forest|||({T}: Add {R} or {G}.)$As Stomping Ground enters the battlefield, you may pay 2 life. If you don't, it enters the battlefield tapped.| +Temple Garden|Unfinity|281|R||Land - Forest Plains|||({T}: Add {G} or {W}.)$As Temple Garden enters the battlefield, you may pay 2 life. If you don't, it enters the battlefield tapped.| +Godless Shrine|Unfinity|282|R||Land - Plains Swamp|||({T}: Add {W} or {B}.)$As Godless Shrine enters the battlefield, you may pay 2 life. If you don't, it enters the battlefield tapped.| +Steam Vents|Unfinity|283|R||Land - Island Mountain|||({T}: Add {U} or {R}.)$As Steam Vents enters the battlefield, you may pay 2 life. If you don't, it enters the battlefield tapped.| +Overgrown Tomb|Unfinity|284|R||Land - Swamp Forest|||({T}: Add {B} or {G}.)$As Overgrown Tomb enters the battlefield, you may pay 2 life. If you don't, it enters the battlefield tapped.| +Sacred Foundry|Unfinity|285|R||Land - Mountain Plains|||({T}: Add {R} or {W}.)$As Sacred Foundry enters the battlefield, you may pay 2 life. If you don't, it enters the battlefield tapped.| +Breeding Pool|Unfinity|286|R||Land - Forest Island|||({T}: Add {G} or {U}.)$As Breeding Pool enters the battlefield, you may pay 2 life. If you don't, it enters the battlefield tapped.| +Ancestral Katana|Kamigawa: Neon Dynasty|1|C|{1}{W}|Artifact - Equipment|||Whenever a Samurai or Warrior you control attacks alone, you may pay {1}. When you do, attach Ancestral Katana to it.$Equipped creature gets +2/+1.$Equip {3}| +Ao, the Dawn Sky|Kamigawa: Neon Dynasty|2|M|{3}{W}{W}|Legendary Creature - Dragon Spirit|5|4|Flying, vigilance$When Ao, the Dawn Sky dies, choose one —$• Look at the top seven cards of your library. Put any number of nonland permanent cards with total mana value 4 or less from among them onto the battlefield. Put the rest on the bottom of your library in a random order.$• Put two +1/+1 counters on each permanent you control that's a creature or Vehicle.| +Banishing Slash|Kamigawa: Neon Dynasty|3|U|{W}{W}|Sorcery|||Destroy up to one target artifact, enchantment, or tapped creature. Then if you control an artifact and an enchantment, create a 2/2 white Samurai creature token with vigilance.| +Befriending the Moths|Kamigawa: Neon Dynasty|4|C|{3}{W}|Enchantment - Saga|||(As this Saga enters and after your draw step, add a lore counter.)$I, II — Target creature you control gets +1/+1 and gains flying until end of turn.$III — Exile this Saga, then return it to the battlefield transformed under your control.| +Imperial Moth|Kamigawa: Neon Dynasty|4|C||Enchantment Creature - Insect|2|4|Flying| +Blade-Blizzard Kitsune|Kamigawa: Neon Dynasty|5|U|{2}{W}|Creature - Fox Ninja|2|2|Ninjutsu {3}{W}$Double strike| +Born to Drive|Kamigawa: Neon Dynasty|6|U|{2}{W}|Enchantment - Aura|||Enchant artifact or creature$As long as enchanted permanent is a creature, it gets +1/+1 for each creature and/or Vehicle you control.$Channel — {2}{W}, Discard Born to Drive: Create two 1/1 colorless Pilot creature tokens with "This creature crews Vehicles as though its power were 2 greater."| +Brilliant Restoration|Kamigawa: Neon Dynasty|7|R|{3}{W}{W}{W}{W}|Sorcery|||Return all artifact and enchantment cards from your graveyard to the battlefield.| +Cloudsteel Kirin|Kamigawa: Neon Dynasty|8|R|{2}{W}|Artifact Creature - Equipment Kirin|3|2|Flying$Equipped creature has flying and "You can't lose the game and your opponents can't win the game."$Reconfigure {5}| +Dragonfly Suit|Kamigawa: Neon Dynasty|9|C|{2}{W}|Artifact - Vehicle|3|2|Flying$Crew 1| +Eiganjo Exemplar|Kamigawa: Neon Dynasty|10|C|{1}{W}|Enchantment Creature - Human Samurai|2|1|Whenever a Samurai or Warrior you control attacks alone, it gets +1/+1 until end of turn.| +Era of Enlightenment|Kamigawa: Neon Dynasty|11|C|{1}{W}|Enchantment - Saga|||(As this Saga enters and after your draw step, add a lore counter.)$I — Scry 2.$II — You gain 2 life.$III — Exile this Saga, then return it to the battlefield transformed under your control.| +Hand of Enlightenment|Kamigawa: Neon Dynasty|11|C||Enchantment Creature - Human Monk|2|2|First strike| +The Fall of Lord Konda|Kamigawa: Neon Dynasty|12|U|{2}{W}|Enchantment - Saga|||(As this Saga enters and after your draw step, add a lore counter.)$I — Exile target creature an opponent controls with mana value 4 or greater.$II — Each player gains control of all permanents they own.$III — Exile this Saga, then return it to the battlefield transformed under your control.| +Fragment of Konda|Kamigawa: Neon Dynasty|12|U||Enchantment Creature - Human Noble|1|3|Defender$When Fragment of Konda dies, draw a card.| +Farewell|Kamigawa: Neon Dynasty|13|R|{4}{W}{W}|Sorcery|||Choose one or more —$• Exile all artifacts.$• Exile all creatures.$• Exile all enchantments.$• Exile all graveyards.| +Go-Shintai of Shared Purpose|Kamigawa: Neon Dynasty|14|U|{3}{W}|Legendary Enchantment Creature - Shrine|1|3|Vigilance$At the beginning of your end step, you may pay {1}. If you do, create a 1/1 colorless Spirit creature token for each Shrine you control.| +Golden-Tail Disciple|Kamigawa: Neon Dynasty|15|C|{2}{W}|Enchantment Creature - Fox Monk|2|3|Lifelink| +Hotshot Mechanic|Kamigawa: Neon Dynasty|16|U|{W}|Artifact Creature - Fox Pilot|2|1|Hotshot Mechanic crews Vehicles as though its power were 2 greater.| +Imperial Oath|Kamigawa: Neon Dynasty|17|C|{5}{W}|Sorcery|||Create three 2/2 white Samurai creature tokens with vigilance. Scry 3.| +Imperial Recovery Unit|Kamigawa: Neon Dynasty|18|U|{2}{W}|Artifact - Vehicle|3|4|Whenever Imperial Recovery Unit attacks, return target creature or Vehicle card with mana value 2 or less from your graveyard to your hand.$Crew 2| +Imperial Subduer|Kamigawa: Neon Dynasty|19|C|{2}{W}|Creature - Human Samurai|3|2|Whenever a Samurai or Warrior you control attacks alone, tap target creature you don't control.| +Intercessor's Arrest|Kamigawa: Neon Dynasty|20|C|{2}{W}|Enchantment - Aura|||Enchant permanent$Enchanted permanent can't attack, block, or crew Vehicles. Its activated abilities can't be activated unless they're mana abilities.| +Invoke Justice|Kamigawa: Neon Dynasty|21|R|{1}{W}{W}{W}{W}|Sorcery|||Return target permanent card from your graveyard to the battlefield, then distribute four +1/+1 counters among any number of creatures and/or Vehicles target player controls.| +Kitsune Ace|Kamigawa: Neon Dynasty|22|C|{1}{W}|Creature - Fox Pilot|2|2|Whenever a Vehicle you control attacks, choose one —$• That Vehicle gains first strike until end of turn.$• Untap Kitsune Ace.| +Kyodai, Soul of Kamigawa|Kamigawa: Neon Dynasty|23|R|{3}{W}|Legendary Creature - Dragon Spirit|3|3|Flash$Flying$When Kyodai, Soul of Kamigawa enters the battlefield, another target permanent gains indestructible for as long as you control Kyodai.${W}{U}{B}{R}{G}: Kyodai, Soul of Kamigawa gets +5/+5 until end of turn.| +Light the Way|Kamigawa: Neon Dynasty|24|C|{W}|Instant|||Choose one —$• Put a +1/+1 counter on target creature or Vehicle. Untap it.$• Return target permanent you control to its owner's hand.| +Light-Paws, Emperor's Voice|Kamigawa: Neon Dynasty|25|R|{1}{W}|Legendary Creature - Fox Advisor|2|2|Whenever an Aura enters the battlefield under your control, if you cast it, you may search your library for an Aura card with mana value less than or equal to that Aura and with a different name than each Aura you control, put that card onto the battlefield attached to Light-Paws, Emperor's Voice, then shuffle.| +Lion Sash|Kamigawa: Neon Dynasty|26|R|{1}{W}|Artifact Creature - Equipment Cat|1|1|{W}: Exile target card from a graveyard. If it was a permanent card, put a +1/+1 counter on Lion Sash.$Equipped creature gets +1/+1 for each +1/+1 counter on Lion Sash.$Reconfigure {2}| +Lucky Offering|Kamigawa: Neon Dynasty|27|C|{W}|Sorcery|||Destroy target artifact with mana value 3 or less. You gain 3 life.| +March of Otherworldly Light|Kamigawa: Neon Dynasty|28|R|{X}{W}|Instant|||As an additional cost to cast this spell, you may exile any number of white cards from your hand. This spell costs {2} less to cast for each card exiled this way.$Exile target artifact, creature, or enchantment with mana value X or less.| +Michiko's Reign of Truth|Kamigawa: Neon Dynasty|29|U|{1}{W}|Enchantment - Saga|||(As this Saga enters and after your draw step, add a lore counter.)$I, II — Target creature gets +1/+1 until end of turn for each artifact and/or enchantment you control.$III — Exile this Saga, then return it to the battlefield transformed under your control.| +Portrait of Michiko|Kamigawa: Neon Dynasty|29|U||Enchantment Creature - Human Noble|0|0|Portrait of Michiko gets +1/+1 for each artifact and/or enchantment you control.| +Mothrider Patrol|Kamigawa: Neon Dynasty|30|C|{W}|Creature - Fox Warrior|1|1|Flying${3}{W}, {T}: Tap target creature.| +Norika Yamazaki, the Poet|Kamigawa: Neon Dynasty|31|U|{2}{W}|Legendary Creature - Human Samurai|3|2|Vigilance$Whenever a Samurai or Warrior you control attacks alone, you may cast target enchantment card from your graveyard this turn.| +Regent's Authority|Kamigawa: Neon Dynasty|32|C|{W}|Instant|||Target creature gets +2/+2 until end of turn. If it's an enchantment creature or legendary creature, instead put a +1/+1 counter on it and it gets +1/+1 until end of turn.| +Repel the Vile|Kamigawa: Neon Dynasty|33|C|{3}{W}|Instant|||Choose one —$• Exile target creature with power 4 or greater.$• Exile target enchantment.| +The Restoration of Eiganjo|Kamigawa: Neon Dynasty|34|R|{2}{W}|Enchantment - Saga|||(As this Saga enters and after your draw step, add a lore counter.)$I — Search your library for a basic Plains card, reveal it, put it into your hand, then shuffle.$II — You may discard a card. When you do, return target permanent card with mana value 2 or less from your graveyard to the battlefield tapped.$III — Exile this Saga, then return it to the battlefield transformed under your control.| +Architect of Restoration|Kamigawa: Neon Dynasty|34|R||Enchantment Creature - Fox Monk|3|4|Vigilance$Whenever Architect of Restoration attacks or blocks, create a 1/1 colorless Spirit creature token.| +Selfless Samurai|Kamigawa: Neon Dynasty|35|U|{1}{W}|Creature - Fox Samurai|2|2|Whenever a Samurai or Warrior you control attacks alone, it gains lifelink until end of turn.$Sacrifice Selfless Samurai: Another target creature you control gains indestructible until end of turn.| +Seven-Tail Mentor|Kamigawa: Neon Dynasty|36|C|{3}{W}|Creature - Fox Samurai|2|3|When Seven-Tail Mentor enters the battlefield or dies, put a +1/+1 counter on target creature or Vehicle you control.| +Sky-Blessed Samurai|Kamigawa: Neon Dynasty|37|U|{6}{W}|Enchantment Creature - Human Samurai|4|4|This spell costs {1} less to cast for each enchantment you control.$Flying| +Spirited Companion|Kamigawa: Neon Dynasty|38|C|{1}{W}|Enchantment Creature - Dog|1|1|When Spirited Companion enters the battlefield, draw a card.| +Sunblade Samurai|Kamigawa: Neon Dynasty|39|C|{4}{W}|Enchantment Creature - Human Samurai|4|4|Vigilance$Channel — {2}, Discard Sunblade Samurai: Search your library for a basic Plains card, reveal it, put it into your hand, then shuffle. You gain 2 life.| +Touch the Spirit Realm|Kamigawa: Neon Dynasty|40|U|{2}{W}|Enchantment|||When Touch the Spirit Realm enters the battlefield, exile up to one target artifact or creature until Touch the Spirit Realm leaves the battlefield.$Channel — {1}{W}, Discard Touch the Spirit Realm: Exile target artifact or creature. Return it to the battlefield under its owner's control at the beginning of the next end step.| +Wanderer's Intervention|Kamigawa: Neon Dynasty|41|C|{1}{W}|Instant|||Wanderer's Intervention deals 4 damage to target attacking or blocking creature.| +The Wandering Emperor|Kamigawa: Neon Dynasty|42|M|{2}{W}{W}|Legendary Planeswalker|3|Flash$As long as The Wandering Emperor entered the battlefield this turn, you may activate her loyalty abilities any time you could cast an instant.$+1: Put a +1/+1 counter on up to one target creature. It gains first strike until end of turn.$−1: Create a 2/2 white Samurai creature token with vigilance.$−2: Exile target tapped creature. You gain 2 life.| +When We Were Young|Kamigawa: Neon Dynasty|43|U|{3}{W}|Instant|||Up to two target creatures each get +2/+2 until end of turn. If you control an artifact and an enchantment, those creatures also gain lifelink until end of turn.| +Acquisition Octopus|Kamigawa: Neon Dynasty|44|U|{2}{U}|Artifact Creature - Equipment Octopus|2|2|Whenever Acquisition Octopus or equipped creature deals combat damage to a player, draw a card.$Reconfigure {2}| +Anchor to Reality|Kamigawa: Neon Dynasty|45|U|{2}{U}{U}|Sorcery|||As an additional cost to cast this spell, sacrifice an artifact or creature.$Search your library for an Equipment or Vehicle card, put that card onto the battlefield, then shuffle. If it has mana value less than the sacrificed permanent's mana value, scry 2.| +Armguard Familiar|Kamigawa: Neon Dynasty|46|C|{1}{U}|Artifact Creature - Equipment Beast|2|1|Ward {2}$Equipped creature gets +2/+1 and has ward {2}.$Reconfigure {4}| +Awakened Awareness|Kamigawa: Neon Dynasty|47|U|{X}{U}{U}|Enchantment - Aura|||Enchant artifact or creature$When Awakened Awareness enters the battlefield, put X +1/+1 counters on enchanted permanent.$As long as enchanted permanent is a creature, it has base power and toughness 1/1.| +Behold the Unspeakable|Kamigawa: Neon Dynasty|48|U|{3}{U}{U}|Enchantment - Saga|||(As this Saga enters and after your draw step, add a lore counter.)$I — Creatures you don't control get -2/-0 until your next turn.$II — If you have one or fewer cards in hand, draw four cards. Otherwise, scry 2, then draw two cards.$III — Exile this Saga, then return it to the battlefield transformed under your control.| +Vision of the Unspeakable|Kamigawa: Neon Dynasty|48|U||Enchantment Creature - Spirit|0|0|Flying, trample$Vision of the Unspeakable gets +1/+1 for each card in your hand.| +Covert Technician|Kamigawa: Neon Dynasty|49|U|{2}{U}|Artifact Creature - Human Ninja|2|4|Ninjutsu {1}{U}$Whenever Covert Technician deals combat damage to a player, you may put an artifact card with mana value less than or equal to that damage from your hand onto the battlefield.| +Discover the Impossible|Kamigawa: Neon Dynasty|50|U|{2}{U}|Instant|||Look at the top five cards of your library. Exile one of them face down and put the rest on the bottom of your library in a random order. You may cast the exiled card without paying its mana cost if it's an instant spell with mana value 2 or less. If you don't, put that card into your hand.| +Disruption Protocol|Kamigawa: Neon Dynasty|51|C|{U}{U}|Instant|||As an additional cost to cast this spell, tap an untapped artifact you control or pay {1}.$Counter target spell.| +Essence Capture|Kamigawa: Neon Dynasty|52|U|{U}{U}|Instant|||Counter target creature spell. Put a +1/+1 counter on up to one target creature you control.| +Futurist Operative|Kamigawa: Neon Dynasty|53|U|{3}{U}|Creature - Human Ninja|3|4|As long as Futurist Operative is tapped, it's a Human Citizen with base power and toughness 1/1 and can't be blocked.${2}{U}: Untap Futurist Operative.| +Futurist Sentinel|Kamigawa: Neon Dynasty|54|C|{3}{U}|Artifact - Vehicle|6|6|Crew 3| +Go-Shintai of Lost Wisdom|Kamigawa: Neon Dynasty|55|U|{1}{U}|Legendary Enchantment Creature - Shrine|0|4|Flying$At the beginning of your end step, you may pay {1}. When you do, target player mills X cards, where X is the number of Shrines you control.| +Guardians of Oboro|Kamigawa: Neon Dynasty|56|C|{2}{U}|Creature - Moonfolk Samurai|3|4|Defender$Modified creatures you control can attack as though they didn't have defender.| +Inventive Iteration|Kamigawa: Neon Dynasty|57|R|{3}{U}|Enchantment - Saga|||(As this Saga enters and after your draw step, add a lore counter.)$I — Return up to one target creature or planeswalker to its owner's hand.$II — Return an artifact card from your graveyard to your hand. If you can't, draw a card.$III — Exile this Saga, then return it to the battlefield transformed under your control.| +Living Breakthrough|Kamigawa: Neon Dynasty|57|R||Enchantment Creature - Moonfolk|3|3|Flying$Whenever you cast a spell, your opponents can't cast spells with the same mana value as that spell until your next turn.| +Invoke the Winds|Kamigawa: Neon Dynasty|58|R|{1}{U}{U}{U}{U}|Sorcery|||Gain control of target artifact or creature. Untap it.| +Jin-Gitaxias, Progress Tyrant|Kamigawa: Neon Dynasty|59|M|{5}{U}{U}|Legendary Creature - Phyrexian Praetor|5|5|Whenever you cast an artifact, instant, or sorcery spell, copy that spell. You may choose new targets for the copy. This ability triggers only once each turn.$Whenever an opponent casts an artifact, instant, or sorcery spell, counter that spell. This ability triggers only once each turn.| +Kairi, the Swirling Sky|Kamigawa: Neon Dynasty|60|M|{4}{U}{U}|Legendary Creature - Dragon Spirit|6|6|Flying, ward {3}$When Kairi, the Swirling Sky dies, choose one —$• Return any number of target nonland permanents with total mana value 6 or less to their owners' hands.$• Mill six cards, then return up to two instant and/or sorcery cards from your graveyard to your hand.| +March of Swirling Mist|Kamigawa: Neon Dynasty|61|R|{X}{U}|Instant|||As an additional cost to cast this spell, you may exile any number of blue cards from your hand. This spell costs {2} less to cast for each card exiled this way.$Up to X target creatures phase out.| +Mindlink Mech|Kamigawa: Neon Dynasty|62|R|{2}{U}|Artifact - Vehicle|4|3|Flying$Whenever Mindlink Mech becomes crewed for the first time each turn, until end of turn, Mindlink Mech becomes a copy of target nonlegendary creature that crewed it this turn, except it's 4/3, it's a Vehicle artifact in addition to its other types, and it has flying.$Crew 1| +Mirrorshell Crab|Kamigawa: Neon Dynasty|63|C|{5}{U}{U}|Artifact Creature - Crab|5|7|Ward {3}$Channel — {2}{U}, Discard Mirrorshell Crab: Counter target spell or ability unless its controller pays {3}.| +Mnemonic Sphere|Kamigawa: Neon Dynasty|64|C|{1}{U}|Artifact|||{1}{U}, Sacrifice Mnemonic Sphere: Draw two cards.$Channel — {U}, Discard Mnemonic Sphere: Draw a card.| +Mobilizer Mech|Kamigawa: Neon Dynasty|65|U|{1}{U}|Artifact - Vehicle|3|4|Flying$Whenever Mobilizer Mech becomes crewed, up to one other target Vehicle you control becomes an artifact creature until end of turn.$Crew 3| +The Modern Age|Kamigawa: Neon Dynasty|66|C|{1}{U}|Enchantment - Saga|||(As this Saga enters and after your draw step, add a lore counter.)$I, II — Draw a card, then discard a card.$III — Exile this Saga, then return it to the battlefield transformed under your control.| +Vector Glider|Kamigawa: Neon Dynasty|66|C||Enchantment Creature - Spirit|2|3|Flying| +Moon-Circuit Hacker|Kamigawa: Neon Dynasty|67|C|{1}{U}|Enchantment Creature - Human Ninja|2|1|Ninjutsu {U}$Whenever Moon-Circuit Hacker deals combat damage to a player, you may draw a card. If you do, discard a card unless Moon-Circuit Hacker entered the battlefield this turn.| +Moonfolk Puzzlemaker|Kamigawa: Neon Dynasty|68|C|{2}{U}|Artifact Creature - Moonfolk Wizard|1|4|Flying$Whenever Moonfolk Puzzlemaker becomes tapped, scry 1.| +Moonsnare Prototype|Kamigawa: Neon Dynasty|69|C|{U}|Artifact|||{T}, Tap an untapped artifact or creature you control: Add {C}.$Channel — {4}{U}, Discard Moonsnare Prototype: The owner of target nonland permanent puts it on the top or bottom of their library.| +Moonsnare Specialist|Kamigawa: Neon Dynasty|70|C|{3}{U}|Creature - Human Ninja|2|2|Ninjutsu {2}{U}$When Moonsnare Specialist enters the battlefield, return up to one target creature to its owner's hand.| +Network Disruptor|Kamigawa: Neon Dynasty|71|C|{U}|Artifact Creature - Moonfolk Rogue|1|1|Flying$When Network Disruptor enters the battlefield, tap target permanent.| +Planar Incision|Kamigawa: Neon Dynasty|72|C|{1}{U}|Instant|||Exile target artifact or creature, then return it to the battlefield under its owner's control with a +1/+1 counter on it.| +Prosperous Thief|Kamigawa: Neon Dynasty|73|U|{2}{U}|Creature - Human Ninja|3|2|Ninjutsu {1}{U}$Whenever one or more Ninja or Rogue creatures you control deal combat damage to a player, create a Treasure token.| +The Reality Chip|Kamigawa: Neon Dynasty|74|R|{1}{U}|Legendary Artifact Creature - Equipment Jellyfish|0|4|You may look at the top card of your library any time.$As long as The Reality Chip is attached to a creature, you may play lands and cast spells from the top of your library.$Reconfigure {2}{U}| +Reality Heist|Kamigawa: Neon Dynasty|75|U|{5}{U}{U}|Instant|||This spell costs {1} less to cast for each artifact you control.$Look at the top seven cards of your library. You may reveal up to two artifact cards from among them and put them into your hand. Put the rest on the bottom of your library in a random order.| +Replication Specialist|Kamigawa: Neon Dynasty|76|U|{4}{U}|Creature - Moonfolk Artificer|3|4|Flying$Whenever a nontoken artifact enters the battlefield under your control, you may pay {1}{U}. If you do, create a token that's a copy of that artifact.| +Saiba Trespassers|Kamigawa: Neon Dynasty|77|C|{4}{U}|Artifact Creature - Moonfolk Rogue|3|5|Channel — {3}{U}, Discard Saiba Trespassers: Tap up to two target creatures you don't control. Those creatures don't untap during their controller's next untap step.| +Short Circuit|Kamigawa: Neon Dynasty|78|C|{1}{U}|Enchantment - Aura|||Flash$Enchant artifact or creature$As long as enchanted permanent is a creature, it gets -3/-0 and loses flying.| +Skyswimmer Koi|Kamigawa: Neon Dynasty|79|C|{3}{U}|Creature - Fish|3|3|Flying$Whenever an artifact enters the battlefield under your control, you may draw a card. If you do, discard a card.| +Spell Pierce|Kamigawa: Neon Dynasty|80|C|{U}|Instant|||Counter target noncreature spell unless its controller pays {2}.| +Suit Up|Kamigawa: Neon Dynasty|81|C|{2}{U}|Instant|||Until end of turn, target creature or Vehicle becomes an artifact creature with base power and toughness 4/5.$Draw a card.| +Tameshi, Reality Architect|Kamigawa: Neon Dynasty|82|R|{2}{U}|Legendary Creature - Moonfolk Wizard|2|3|Whenever one or more noncreature permanents are returned to hand, draw a card. This ability triggers only once each turn.${X}{W}, Return a land you control to its owner's hand: Return target artifact or enchantment card with mana value X or less from your graveyard to the battlefield. Activate only as a sorcery.| +Tamiyo's Compleation|Kamigawa: Neon Dynasty|83|C|{3}{U}|Enchantment - Aura|||Flash$Enchant artifact, creature, or planeswalker$When Tamiyo's Compleation enters the battlefield, tap enchanted permanent. If it's an Equipment, unattach it.$Enchanted permanent loses all abilities and doesn't untap during its controller's untap step.| +Tezzeret, Betrayer of Flesh|Kamigawa: Neon Dynasty|84|M|{2}{U}{U}|Legendary Planeswalker - Tezzeret|4|The first activated ability of an artifact you activate each turn costs {2} less to activate.$+1: Draw two cards. Then discard two cards unless you discard an artifact card.$−2: Target artifact becomes an artifact creature. If it isn't a Vehicle, it has base power and toughness 4/4.$−6: You get an emblem with "Whenever an artifact you control becomes tapped, draw a card."| +Thirst for Knowledge|Kamigawa: Neon Dynasty|85|U|{2}{U}|Instant|||Draw three cards. Then discard two cards unless you discard an artifact card.| +Thousand-Faced Shadow|Kamigawa: Neon Dynasty|86|R|{U}|Creature - Human Ninja|1|1|Ninjutsu {2}{U}{U}$Flying$When Thousand-Faced Shadow enters the battlefield from your hand, if it's attacking, create a token that's a copy of another target attacking creature. The token enters the battlefield tapped and attacking.| +Assassin's Ink|Kamigawa: Neon Dynasty|87|U|{2}{B}{B}|Instant|||This spell costs {1} less to cast if you control an artifact and {1} less to cast if you control an enchantment.$Destroy target creature or planeswalker.| +Biting-Palm Ninja|Kamigawa: Neon Dynasty|88|R|{2}{B}|Creature - Human Ninja|3|3|Ninjutsu {2}{B}$Biting-Palm Ninja enters the battlefield with a menace counter on it.$Whenever Biting-Palm Ninja deals combat damage to a player, you may remove a menace counter from it. When you do, that player reveals their hand and you choose a nonland card from it. Exile that card.| +Blade of the Oni|Kamigawa: Neon Dynasty|89|M|{1}{B}|Artifact Creature - Equipment Demon|3|1|Menace$Equipped creature has base power and toughness 5/5, has menace, and is a black Demon in addition to its other colors and types.$Reconfigure {2}{B}{B}| +Chainflail Centipede|Kamigawa: Neon Dynasty|90|C|{2}{B}|Artifact Creature - Equipment Insect|2|2|Whenever Chainflail Centipede or equipped creature attacks, it gets +2/+0 until end of turn.$Reconfigure {2}| +Clawing Torment|Kamigawa: Neon Dynasty|91|C|{B}|Enchantment - Aura|||Enchant artifact or creature$As long as enchanted permanent is a creature, it gets -1/-1 and can't block.$Enchanted permanent has "At the beginning of your upkeep, you lose 1 life."| +Debt to the Kami|Kamigawa: Neon Dynasty|92|C|{2}{B}|Instant|||Choose one —$• Target opponent exiles a creature they control.$• Target opponent exiles an enchantment they control.| +Dockside Chef|Kamigawa: Neon Dynasty|93|U|{B}|Enchantment Creature - Human Citizen|1|2|{1}{B}, Sacrifice an artifact or creature: Draw a card.| +Dokuchi Shadow-Walker|Kamigawa: Neon Dynasty|94|C|{4}{B}{B}|Creature - Ogre Ninja|5|5|Ninjutsu {3}{B}| +Dokuchi Silencer|Kamigawa: Neon Dynasty|95|U|{1}{B}|Creature - Human Ninja|2|1|Ninjutsu {1}{B}$Whenever Dokuchi Silencer deals combat damage to a player, you may discard a creature card. When you do, destroy target creature or planeswalker that player controls.| +Enormous Energy Blade|Kamigawa: Neon Dynasty|96|U|{2}{B}|Artifact - Equipment|||Equipped creature gets +4/+0.$Whenever Enormous Energy Blade becomes attached to a creature, tap that creature.$Equip {2}| +Go-Shintai of Hidden Cruelty|Kamigawa: Neon Dynasty|97|U|{3}{B}|Legendary Enchantment Creature - Shrine|2|2|Deathtouch$At the beginning of your end step, you may pay {1}. When you do, destroy target creature with toughness X or less, where X is the number of Shrines you control.| +Gravelighter|Kamigawa: Neon Dynasty|98|U|{2}{B}|Creature - Spirit|2|2|Flying$When Gravelighter enters the battlefield, draw a card if a creature died this turn. Otherwise, each player sacrifices a creature.| +Hidetsugu, Devouring Chaos|Kamigawa: Neon Dynasty|99|R|{3}{B}|Legendary Creature - Ogre Demon|4|4|{B}, Sacrifice a creature: Scry 2.${2}{R}, {T}: Exile the top card of your library. You may play that card this turn. When you exile a nonland card this way, Hidetsugu, Devouring Chaos deals damage equal to the exiled card's mana value to any target.| +Inkrise Infiltrator|Kamigawa: Neon Dynasty|100|C|{1}{B}|Creature - Human Ninja|1|2|Flying${3}{B}: Inkrise Infiltrator gets +2/+2 until end of turn.| +Invoke Despair|Kamigawa: Neon Dynasty|101|R|{1}{B}{B}{B}{B}|Sorcery|||Target opponent sacrifices a creature. If they can't, they lose 2 life and you draw a card. Then repeat this process for an enchantment and a planeswalker.| +Junji, the Midnight Sky|Kamigawa: Neon Dynasty|102|M|{3}{B}{B}|Legendary Creature - Dragon Spirit|5|5|Flying, menace$When Junji, the Midnight Sky dies, choose one —$• Each opponent discards two cards and loses 2 life.$• Put target non-Dragon creature card from a graveyard onto the battlefield under your control. You lose 2 life.| +Kaito's Pursuit|Kamigawa: Neon Dynasty|103|C|{2}{B}|Sorcery|||Target player discards two cards. Ninjas and Rogues you control gain menace until end of turn.| +Kami of Restless Shadows|Kamigawa: Neon Dynasty|104|C|{4}{B}|Creature - Spirit|3|3|When Kami of Restless Shadows enters the battlefield, choose one —$• Return up to one target Ninja or Rogue creature card from your graveyard to your hand.$• Put target creature card from your graveyard on top of your library.| +Kami of Terrible Secrets|Kamigawa: Neon Dynasty|105|C|{3}{B}|Creature - Spirit|3|4|When Kami of Terrible Secrets enters the battlefield, if you control an artifact and an enchantment, you draw a card and you gain 1 life.| +Leech Gauntlet|Kamigawa: Neon Dynasty|106|U|{1}{B}|Artifact Creature - Equipment Leech|2|2|Lifelink$Equipped creature has lifelink.$Reconfigure {4}| +Lethal Exploit|Kamigawa: Neon Dynasty|107|C|{1}{B}|Instant|||Target creature gets -2/-2 until end of turn. It gets an additional -1/-1 until end of turn for each modified creature you controlled as you cast this spell.| +Life of Toshiro Umezawa|Kamigawa: Neon Dynasty|108|U|{1}{B}|Enchantment - Saga|||(As this Saga enters and after your draw step, add a lore counter.)$I, II — Choose one —$• Target creature gets +2/+2 until end of turn.$• Target creature gets -1/-1 until end of turn.$• You gain 2 life.$III — Exile this Saga, then return it to the battlefield transformed under your control.| +Memory of Toshiro|Kamigawa: Neon Dynasty|108|U||Enchantment Creature - Human Samurai|2|3|{T}, Pay 1 life: Add {B}. Spend this mana only to cast an instant or sorcery spell.| +The Long Reach of Night|Kamigawa: Neon Dynasty|109|U|{3}{B}|Enchantment - Saga|||(As this Saga enters and after your draw step, add a lore counter.)$I, II — Each opponent sacrifices a creature unless they discard a card.$III — Exile this Saga, then return it to the battlefield transformed under your control.| +Animus of Night's Reach|Kamigawa: Neon Dynasty|109|U||Enchantment Creature - Spirit|0|4|Menace$Whenever Animus of Night's Reach attacks, it gets +X/+0 until end of turn, where X is the number of creature cards in defending player's graveyard.| +Malicious Malfunction|Kamigawa: Neon Dynasty|110|U|{1}{B}{B}|Sorcery|||All creatures get -2/-2 until end of turn. If a creature would die this turn, exile it instead.| +March of Wretched Sorrow|Kamigawa: Neon Dynasty|111|R|{X}{B}|Instant|||As an additional cost to cast this spell, you may exile any number of black cards from your hand. This spell costs {2} less to cast for each card exiled this way.$March of Wretched Sorrow deals X damage to target creature or planeswalker and you gain X life.| +Mukotai Ambusher|Kamigawa: Neon Dynasty|112|C|{3}{B}|Artifact Creature - Rat Ninja|3|2|Ninjutsu {1}{B}$Lifelink| +Mukotai Soulripper|Kamigawa: Neon Dynasty|113|R|{1}{B}|Artifact - Vehicle|4|3|Whenever Mukotai Soulripper attacks, you may sacrifice another artifact or creature. If you do, put a +1/+1 counter on Mukotai Soulripper and it gains menace until end of turn.$Crew 2| +Nashi, Moon Sage's Scion|Kamigawa: Neon Dynasty|114|M|{1}{B}{B}|Legendary Creature - Rat Ninja|3|2|Ninjutsu {3}{B}$Whenever Nashi, Moon Sage's Scion deals combat damage to a player, exile the top card of each player's library. Until end of turn, you may play one of those cards. If you cast a spell this way, pay life equal to its mana value rather than paying its mana cost.| +Nezumi Bladeblesser|Kamigawa: Neon Dynasty|115|C|{2}{B}|Creature - Rat Samurai|3|2|Nezumi Bladeblesser has deathtouch as long as you control an artifact.$Nezumi Bladeblesser has menace as long as you control an enchantment.| +Nezumi Prowler|Kamigawa: Neon Dynasty|116|U|{1}{B}|Artifact Creature - Rat Ninja|3|1|Ninjutsu {1}{B}$When Nezumi Prowler enters the battlefield, target creature you control gains deathtouch and lifelink until end of turn.| +Okiba Reckoner Raid|Kamigawa: Neon Dynasty|117|C|{B}|Enchantment - Saga|||(As this Saga enters and after your draw step, add a lore counter.)$I, II — Each opponent loses 1 life and you gain 1 life.$III — Exile this Saga, then return it to the battlefield transformed under your control.| +Nezumi Road Captain|Kamigawa: Neon Dynasty|117|C||Enchantment Creature - Rat Rogue|2|2|Menace$Vehicles you control have menace.| +Okiba Salvage|Kamigawa: Neon Dynasty|118|U|{4}{B}|Sorcery|||Return target creature or Vehicle card from your graveyard to the battlefield. Then put two +1/+1 counters on that permanent if you control an artifact and an enchantment.| +Reckoner Shakedown|Kamigawa: Neon Dynasty|119|C|{2}{B}|Sorcery|||Target opponent reveals their hand. You may choose a nonland card from it. If you do, that player discards that card. If you don't, put two +1/+1 counters on a creature or Vehicle you control.| +Reckoner's Bargain|Kamigawa: Neon Dynasty|120|C|{1}{B}|Instant|||As an additional cost to cast this spell, sacrifice an artifact or creature.$You gain life equal to the sacrificed permanent's mana value. Draw two cards.| +Return to Action|Kamigawa: Neon Dynasty|121|C|{1}{B}|Instant|||Until end of turn, target creature gets +1/+0 and gains lifelink and "When this creature dies, return it to the battlefield tapped under its owner's control."| +Soul Transfer|Kamigawa: Neon Dynasty|122|R|{1}{B}{B}|Sorcery|||Choose one. If you control an artifact and an enchantment as you cast this spell, you may choose both.$• Exile target creature or planeswalker.$• Return target creature or planeswalker card from your graveyard to your hand.| +Tatsunari, Toad Rider|Kamigawa: Neon Dynasty|123|R|{2}{B}|Legendary Creature - Human Ninja|3|3|Whenever you cast an enchantment spell, if you don't control a creature named Keimi, create Keimi, a legendary 3/3 black and green Frog creature token with "Whenever you cast an enchantment spell, each opponent loses 1 life and you gain 1 life."${1}{G/U}: Tatsunari, Toad Rider and target Frog you control can't be blocked this turn except by creatures with flying or reach.| +Tribute to Horobi|Kamigawa: Neon Dynasty|124|R|{1}{B}|Enchantment - Saga|||(As this Saga enters and after your draw step, add a lore counter.)$I, II — Each opponent creates a 1/1 black Rat Rogue creature token.$III — Exile this Saga, then return it to the battlefield transformed under your control.| +Echo of Death's Wail|Kamigawa: Neon Dynasty|124|R||Enchantment Creature - Spirit|3|3|Flying, haste$When Echo of Death's Wail enters the battlefield, gain control of all Rat tokens.$Whenever Echo of Death's Wail attacks, you may sacrifice another creature. If you do, draw a card.| +Twisted Embrace|Kamigawa: Neon Dynasty|125|C|{2}{B}{B}|Enchantment - Aura|||Enchant artifact or creature you control$When Twisted Embrace enters the battlefield, destroy target creature or planeswalker an opponent controls.$As long as enchanted permanent is a creature, it gets +1/+1.| +Undercity Scrounger|Kamigawa: Neon Dynasty|126|C|{2}{B}|Artifact Creature - Human Rogue|1|4|{T}: Create a Treasure token. Activate only if a creature died this turn.| +Unforgiving One|Kamigawa: Neon Dynasty|127|U|{2}{B}|Creature - Spirit|2|3|Menace$Whenever Unforgiving One attacks, return target creature card with mana value X or less from your graveyard to the battlefield, where X is the number of modified creatures you control.| +Virus Beetle|Kamigawa: Neon Dynasty|128|C|{1}{B}|Artifact Creature - Insect|1|1|When Virus Beetle enters the battlefield, each opponent discards a card.| +You Are Already Dead|Kamigawa: Neon Dynasty|129|C|{B}|Instant|||Destroy target creature that was dealt damage this turn.$Draw a card.| +Akki Ember-Keeper|Kamigawa: Neon Dynasty|130|C|{1}{R}|Enchantment Creature - Goblin Warrior|2|1|Whenever a nontoken modified creature you control dies, create a 1/1 colorless Spirit creature token.| +Akki Ronin|Kamigawa: Neon Dynasty|131|C|{1}{R}|Creature - Goblin Samurai|1|3|Whenever a Samurai or Warrior you control attacks alone, you may discard a card. If you do, draw a card.| +Akki War Paint|Kamigawa: Neon Dynasty|132|C|{R}|Enchantment - Aura|||Enchant artifact or creature$As long as enchanted permanent is a creature, it gets +2/+1.| +Ambitious Assault|Kamigawa: Neon Dynasty|133|C|{2}{R}|Instant|||Creatures you control get +2/+0 until end of turn. If you control a modified creature, draw a card.| +Atsushi, the Blazing Sky|Kamigawa: Neon Dynasty|134|M|{2}{R}{R}|Legendary Creature - Dragon Spirit|4|4|Flying, trample$When Atsushi, the Blazing Sky dies, choose one —$• Exile the top two cards of your library. Until the end of your next turn, you may play those cards.$• Create three Treasure tokens.| +Bronzeplate Boar|Kamigawa: Neon Dynasty|135|U|{2}{R}|Artifact Creature - Equipment Boar|3|2|Trample$Equipped creature gets +3/+2 and has trample.$Reconfigure {5}| +Crackling Emergence|Kamigawa: Neon Dynasty|136|C|{1}{R}|Enchantment - Aura|||Enchant land you control$Enchanted land is a 3/3 red Spirit creature with haste. It's still a land.$If enchanted land would be destroyed, instead sacrifice Crackling Emergence and that land gains indestructible until end of turn.| +Dragonspark Reactor|Kamigawa: Neon Dynasty|137|U|{1}{R}|Artifact|||Whenever Dragonspark Reactor or another artifact enters the battlefield under your control, put a charge counter on Dragonspark Reactor.${4}, Sacrifice Dragonspark Reactor: It deals damage equal to the number of charge counters on it to target player and that much damage to up to one target creature.| +Experimental Synthesizer|Kamigawa: Neon Dynasty|138|C|{R}|Artifact|||When Experimental Synthesizer enters or leaves the battlefield, exile the top card of your library. Until end of turn, you may play that card.${2}{R}, Sacrifice Experimental Synthesizer: Create a 2/2 white Samurai creature token with vigilance. Activate only as a sorcery.| +Explosive Entry|Kamigawa: Neon Dynasty|139|C|{1}{R}|Sorcery|||Destroy up to one target artifact. Put a +1/+1 counter on up to one target creature.| +Explosive Singularity|Kamigawa: Neon Dynasty|140|M|{8}{R}{R}|Sorcery|||As an additional cost to cast this spell, you may tap any number of untapped creatures you control. This spell costs {1} less to cast for each creature tapped this way.$Explosive Singularity deals 10 damage to any target.| +Fable of the Mirror-Breaker|Kamigawa: Neon Dynasty|141|R|{2}{R}|Enchantment - Saga|||(As this Saga enters and after your draw step, add a lore counter.)$I — Create a 2/2 red Goblin Shaman creature token with "Whenever this creature attacks, create a Treasure token."$II — You may discard up to two cards. If you do, draw that many cards.$III — Exile this Saga, then return it to the battlefield transformed under your control.| +Reflection of Kiki-Jiki|Kamigawa: Neon Dynasty|141|R||Enchantment Creature - Goblin Shaman|2|2|{1}, {T}: Create a token that's a copy of another target nonlegendary creature you control, except it has haste. Sacrifice it at the beginning of the next end step.| +Flame Discharge|Kamigawa: Neon Dynasty|142|U|{X}{R}|Instant|||Flame Discharge deals X damage to target creature or planeswalker. If you controlled a modified creature as you cast this spell, it deals X plus 2 damage instead.| +Gift of Wrath|Kamigawa: Neon Dynasty|143|C|{3}{R}|Enchantment - Aura|||Enchant artifact or creature$As long as enchanted permanent is a creature, it gets +2/+2 and has menace.$When Gift of Wrath leaves the battlefield, create a 2/2 red Spirit creature token with menace.| +Go-Shintai of Ancient Wars|Kamigawa: Neon Dynasty|144|U|{2}{R}|Legendary Enchantment Creature - Shrine|2|2|First strike$At the beginning of your end step, you may pay {1}. When you do, Go-Shintai of Ancient Wars deals X damage to target player or planeswalker, where X is the number of Shrines you control.| +Goro-Goro, Disciple of Ryusei|Kamigawa: Neon Dynasty|145|R|{1}{R}|Legendary Creature - Goblin Samurai|2|2|{R}: Creatures you control gain haste until end of turn.${3}{R}{R}: Create a 5/5 red Dragon Spirit creature token with flying. Activate only if you control an attacking modified creature.| +Heiko Yamazaki, the General|Kamigawa: Neon Dynasty|146|U|{3}{R}|Legendary Creature - Human Samurai|3|3|Trample$Whenever a Samurai or Warrior you control attacks alone, you may cast target artifact card from your graveyard this turn.| +Invoke Calamity|Kamigawa: Neon Dynasty|147|R|{1}{R}{R}{R}{R}|Instant|||You may cast up to two instant and/or sorcery spells with total mana value 6 or less from your graveyard and/or hand without paying their mana costs. If those spells would be put into your graveyard, exile them instead. Exile Invoke Calamity.| +Ironhoof Boar|Kamigawa: Neon Dynasty|148|C|{5}{R}|Artifact Creature - Boar|5|4|Trample, haste$Channel — {1}{R}, Discard Ironhoof Boar: Target creature gets +3/+1 and gains trample until end of turn.| +Kami of Industry|Kamigawa: Neon Dynasty|149|C|{4}{R}|Creature - Spirit|3|6|When Kami of Industry enters the battlefield, return target artifact card with mana value 3 or less from your graveyard to the battlefield. It gains haste. Sacrifice it at the beginning of the next end step.| +Kami's Flare|Kamigawa: Neon Dynasty|150|C|{1}{R}|Instant|||Kami's Flare deals 3 damage to target creature or planeswalker. Kami's Flare also deals 2 damage to that permanent's controller if you control a modified creature.| +Kindled Fury|Kamigawa: Neon Dynasty|151|C|{R}|Instant|||Target creature gets +1/+0 and gains first strike until end of turn.| +Kumano Faces Kakkazan|Kamigawa: Neon Dynasty|152|U|{R}|Enchantment - Saga|||(As this Saga enters and after your draw step, add a lore counter.)$I — Kumano Faces Kakkazan deals 1 damage to each opponent and each planeswalker they control.$II — When you cast your next creature spell this turn, that creature enters the battlefield with an additional +1/+1 counter on it.$III — Exile this Saga, then return it to the battlefield transformed under your control.| +Etching of Kumano|Kamigawa: Neon Dynasty|152|U||Enchantment Creature - Human Shaman|2|2|Haste$If a creature dealt damage this turn by a source you controlled would die, exile it instead.| +Lizard Blades|Kamigawa: Neon Dynasty|153|R|{1}{R}|Artifact Creature - Equipment Lizard|1|1|Double strike$Equipped creature has double strike.$Reconfigure {2}| +March of Reckless Joy|Kamigawa: Neon Dynasty|154|R|{X}{R}|Instant|||As an additional cost to cast this spell, you may exile any number of red cards from your hand. This spell costs {2} less to cast for each card exiled this way.$Exile the top X cards of your library. You may play up to two of those cards until the end of your next turn.| +Ogre-Head Helm|Kamigawa: Neon Dynasty|155|R|{1}{R}|Artifact Creature - Equipment Ogre|2|2|Equipped creature gets +2/+2.$Whenever Ogre-Head Helm or equipped creature deals combat damage to a player, you may sacrifice it. If you do, discard your hand, then draw three cards.$Reconfigure {3}| +Peerless Samurai|Kamigawa: Neon Dynasty|156|C|{2}{R}|Creature - Human Samurai|2|3|Menace$Whenever a Samurai or Warrior you control attacks alone, the next spell you cast this turn costs {1} less to cast.| +Rabbit Battery|Kamigawa: Neon Dynasty|157|U|{R}|Artifact Creature - Equipment Rabbit|1|1|Haste$Equipped creature gets +1/+1 and has haste.$Reconfigure {R}| +Reinforced Ronin|Kamigawa: Neon Dynasty|158|U|{R}|Artifact Creature - Human Samurai|2|2|Haste$At the beginning of your end step, return Reinforced Ronin to its owner's hand.$Channel — {1}{R}, Discard Reinforced Ronin: Draw a card.| +Scrap Welder|Kamigawa: Neon Dynasty|159|R|{2}{R}|Creature - Goblin Artificer|3|3|{T}, Sacrifice an artifact with mana value X: Return target artifact card with mana value less than X from your graveyard to the battlefield. It gains haste until end of turn.| +Scrapyard Steelbreaker|Kamigawa: Neon Dynasty|160|C|{3}{R}|Artifact Creature - Human Warrior|3|4|{1}, Sacrifice another artifact: Scrapyard Steelbreaker gets +2/+1 until end of turn.| +Seismic Wave|Kamigawa: Neon Dynasty|161|U|{2}{R}|Instant|||Seismic Wave deals 2 damage to any target and 1 damage to each nonartifact creature target opponent controls.| +The Shattered States Era|Kamigawa: Neon Dynasty|162|C|{4}{R}|Enchantment - Saga|||(As this Saga enters and after your draw step, add a lore counter.)$I — Gain control of target creature until end of turn. Untap it. It gains haste until end of turn.$II — Creatures you control get +1/+0 until end of turn.$III — Exile this Saga, then return it to the battlefield transformed under your control.| +Nameless Conqueror|Kamigawa: Neon Dynasty|162|C||Enchantment Creature - Human Samurai|3|3|Trample, haste| +Simian Sling|Kamigawa: Neon Dynasty|163|C|{R}|Artifact Creature - Equipment Monkey|1|1|Equipped creature gets +1/+1.$Whenever Simian Sling or equipped creature becomes blocked, it deals 1 damage to defending player.$Reconfigure {2}| +Sokenzan Smelter|Kamigawa: Neon Dynasty|164|U|{1}{R}|Creature - Goblin Artificer|2|2|At the beginning of combat on your turn, you may pay {1} and sacrifice an artifact. If you do, create a 3/1 red Construct artifact creature token with haste.| +Tempered in Solitude|Kamigawa: Neon Dynasty|165|U|{1}{R}|Enchantment|||Whenever a creature you control attacks alone, exile the top card of your library. You may play that card this turn.| +Thundering Raiju|Kamigawa: Neon Dynasty|166|R|{2}{R}{R}|Creature - Spirit|3|3|Haste$Whenever Thundering Raiju attacks, put a +1/+1 counter on target creature you control. Then Thundering Raiju deals X damage to each opponent, where X is the number of modified creatures you control other than Thundering Raiju.| +Towashi Songshaper|Kamigawa: Neon Dynasty|167|C|{1}{R}|Artifact Creature - Human Artificer|2|2|Whenever another artifact enters the battlefield under your control, Towashi Songshaper gets +1/+0 until end of turn.| +Twinshot Sniper|Kamigawa: Neon Dynasty|168|U|{3}{R}|Artifact Creature - Goblin Archer|2|3|Reach$When Twinshot Sniper enters the battlefield, it deals 2 damage to any target.$Channel — {1}{R}, Discard Twinshot Sniper: It deals 2 damage to any target.| +Unstoppable Ogre|Kamigawa: Neon Dynasty|169|C|{2}{R}|Artifact Creature - Ogre Warrior|4|1|When Unstoppable Ogre enters the battlefield, target creature can't block this turn.| +Upriser Renegade|Kamigawa: Neon Dynasty|170|U|{1}{R}|Creature - Human Samurai|1|3|Upriser Renegade gets +2/+0 for each other modified creature you control.| +Voltage Surge|Kamigawa: Neon Dynasty|171|C|{R}|Instant|||As an additional cost to cast this spell, you may sacrifice an artifact.$Voltage Surge deals 2 damage to target creature or planeswalker. If this spell's additional cost was paid, Voltage Surge deals 4 damage instead.| +Azusa's Many Journeys|Kamigawa: Neon Dynasty|172|U|{1}{G}|Enchantment - Saga|||(As this Saga enters and after your draw step, add a lore counter.)$I — You may play an additional land this turn.$II — You gain 3 life.$III — Exile this Saga, then return it to the battlefield transformed under your control.| +Likeness of the Seeker|Kamigawa: Neon Dynasty|172|U||Enchantment Creature - Human Monk|3|3|Whenever Likeness of the Seeker becomes blocked, untap up to three lands you control.| +Bamboo Grove Archer|Kamigawa: Neon Dynasty|173|C|{1}{G}|Enchantment Creature - Snake Archer|3|3|Defender, reach$Channel — {4}{G}, Discard Bamboo Grove Archer: Destroy target creature with flying.| +Bearer of Memory|Kamigawa: Neon Dynasty|174|C|{2}{G}|Enchantment Creature - Human Monk|3|2|{5}{G}: Put a +1/+1 counter on target enchantment creature. It gains trample until end of turn.| +Blossom Prancer|Kamigawa: Neon Dynasty|175|U|{3}{G}{G}|Creature - Spirit|4|4|Reach$When Blossom Prancer enters the battlefield, look at the top five cards of your library. You may reveal a creature or enchantment card from among them and put it into your hand. Put the rest on the bottom of your library in a random order. If you didn't put a card into your hand this way, you gain 4 life.| +Boon of Boseiju|Kamigawa: Neon Dynasty|176|U|{1}{G}|Instant|||Target creature gets +X/+X until end of turn, where X is the greatest mana value among permanents you control. Untap it.| +Boseiju Reaches Skyward|Kamigawa: Neon Dynasty|177|U|{3}{G}|Enchantment - Saga|||(As this Saga enters and after your draw step, add a lore counter.)$I — Search your library for up to two basic Forest cards, reveal them, put them into your hand, then shuffle.$II — Put up to one target land card from your graveyard on top of your library.$III — Exile this Saga, then return it to the battlefield transformed under your control.| +Branch of Boseiju|Kamigawa: Neon Dynasty|177|U||Enchantment Creature - Plant|0|0|Reach$Branch of Boseiju gets +1/+1 for each land you control.| +Careful Cultivation|Kamigawa: Neon Dynasty|178|C|{2}{G}|Enchantment - Aura|||Enchant artifact or creature$As long as enchanted permanent is a creature, it gets +1/+3 and has reach and "{T}: Add {G}{G}."$Channel — {1}{G}, Discard Careful Cultivation: Create a 1/1 green Human Monk creature token with "{T}: Add {G}."| +Coiling Stalker|Kamigawa: Neon Dynasty|179|C|{1}{G}|Creature - Snake Ninja|2|1|Ninjutsu {1}{G}$Whenever Coiling Stalker deals combat damage to a player, put a +1/+1 counter on target creature you control that doesn't have a +1/+1 counter on it.| +Commune with Spirits|Kamigawa: Neon Dynasty|180|C|{G}|Sorcery|||Look at the top four cards of your library. You may reveal an enchantment or land card from among them and put it into your hand. Put the rest on the bottom of your library in a random order.| +The Dragon-Kami Reborn|Kamigawa: Neon Dynasty|181|R|{2}{G}|Enchantment - Saga|||(As this Saga enters and after your draw step, add a lore counter.)$I, II — You gain 2 life. Look at the top three cards of your library. Exile one of them face down with a hatching counter on it, then put the rest on the bottom of your library in any order.$III — Exile this Saga, then return it to the battlefield transformed under your control.| +Dragon-Kami's Egg|Kamigawa: Neon Dynasty|181|R||Enchantment Creature - Egg|0|1|Whenever Dragon-Kami's Egg or a Dragon you control dies, you may cast a creature spell from among cards you own in exile with hatching counters on them without paying its mana cost.| +Fade into Antiquity|Kamigawa: Neon Dynasty|182|C|{2}{G}|Sorcery|||Exile target artifact or enchantment.| +Fang of Shigeki|Kamigawa: Neon Dynasty|183|C|{G}|Enchantment Creature - Snake Ninja|1|1|Deathtouch| +Favor of Jukai|Kamigawa: Neon Dynasty|184|C|{3}{G}|Enchantment - Aura|||Enchant artifact or creature$As long as enchanted permanent is a creature, it gets +3/+3 and has reach.$Channel — {1}{G}, Discard Favor of Jukai: Target creature gets +3/+3 and gains reach until end of turn.| +Generous Visitor|Kamigawa: Neon Dynasty|185|U|{G}|Creature - Spirit|1|1|Whenever you cast an enchantment spell, put a +1/+1 counter on target creature.| +Geothermal Kami|Kamigawa: Neon Dynasty|186|C|{3}{G}|Creature - Spirit|4|3|When Geothermal Kami enters the battlefield, you may return an enchantment you control to its owner's hand. If you do, you gain 3 life.| +Go-Shintai of Boundless Vigor|Kamigawa: Neon Dynasty|187|U|{1}{G}|Legendary Enchantment Creature - Shrine|1|1|Trample$At the beginning of your end step, you may pay {1}. When you do, put a +1/+1 counter on target Shrine for each Shrine you control.| +Grafted Growth|Kamigawa: Neon Dynasty|188|C|{2}{G}|Enchantment - Aura|||Enchant land$When Grafted Growth enters the battlefield, put a +1/+1 counter on target creature or Vehicle you control.$Enchanted land has "{T}: Add two mana of any one color."| +Greater Tanuki|Kamigawa: Neon Dynasty|189|C|{4}{G}{G}|Enchantment Creature - Dog|6|5|Trample$Channel — {2}{G}, Discard Greater Tanuki: Search your library for a basic land card, put it onto the battlefield tapped, then shuffle.| +Harmonious Emergence|Kamigawa: Neon Dynasty|190|C|{3}{G}|Enchantment - Aura|||Enchant land you control$Enchanted land is a 4/5 green Spirit creature with vigilance and haste. It's still a land.$If enchanted land would be destroyed, instead sacrifice Harmonious Emergence and that land gains indestructible until end of turn.| +Heir of the Ancient Fang|Kamigawa: Neon Dynasty|191|C|{2}{G}|Creature - Snake Samurai|2|3|Heir of the Ancient Fang enters the battlefield with a +1/+1 counter on it if you control a modified creature.| +Historian's Wisdom|Kamigawa: Neon Dynasty|192|U|{2}{G}|Enchantment - Aura|||Enchant artifact or creature$When Historian's Wisdom enters the battlefield, if enchanted permanent is a creature with the greatest power among creatures on the battlefield, draw a card.$As long as enchanted permanent is a creature, it gets +2/+1.| +Invoke the Ancients|Kamigawa: Neon Dynasty|193|R|{1}{G}{G}{G}{G}|Sorcery|||Create two 4/5 green Spirit creature tokens. For each of them, put your choice of a vigilance counter, a reach counter, or a trample counter on it.| +Jugan Defends the Temple|Kamigawa: Neon Dynasty|194|M|{2}{G}|Enchantment - Saga|||(As this Saga enters and after your draw step, add a lore counter.)$I — Create a 1/1 green Human Monk creature token with "{T}: Add {G}."$II — Put a +1/+1 counter on each of up to two target creatures.$III — Exile this Saga, then return it to the battlefield transformed under your control.| +Remnant of the Rising Star|Kamigawa: Neon Dynasty|194|M||Enchantment Creature - Dragon Spirit|2|2|Flying$Whenever another creature enters the battlefield under your control, you may pay {X}. When you do, put X +1/+1 counters on that creature.$As long as you control five or more modified creatures, Remnant of the Rising Star gets +5/+5 and has trample.| +Jukai Preserver|Kamigawa: Neon Dynasty|195|C|{3}{G}|Enchantment Creature - Human Druid|3|3|When Jukai Preserver enters the battlefield, put a +1/+1 counter on target creature you control.$Channel — {2}{G}, Discard Jukai Preserver: Put a +1/+1 counter on each of up to two target creatures you control.| +Jukai Trainee|Kamigawa: Neon Dynasty|196|C|{1}{G}|Creature - Human Samurai|2|2|Whenever Jukai Trainee blocks or becomes blocked, it gets +1/+1 until end of turn.| +Kami of Transience|Kamigawa: Neon Dynasty|197|R|{1}{G}|Creature - Spirit|2|2|Trample$Whenever you cast an enchantment spell, put a +1/+1 counter on Kami of Transience.$At the beginning of each end step, if an enchantment was put into your graveyard from the battlefield this turn, you may return Kami of Transience from your graveyard to your hand.| +Kappa Tech-Wrecker|Kamigawa: Neon Dynasty|198|U|{1}{G}|Creature - Turtle Ninja|1|3|Ninjutsu {1}{G}$Kappa Tech-Wrecker enters the battlefield with a deathtouch counter on it.$Whenever Kappa Tech-Wrecker deals combat damage to a player, you may remove a deathtouch counter from it. When you do, exile target artifact or enchantment that player controls.| +Kodama of the West Tree|Kamigawa: Neon Dynasty|199|M|{2}{G}|Legendary Creature - Spirit|3|3|Reach$Modified creatures you control have trample.$Whenever a modified creature you control deals combat damage to a player, search your library for a basic land card, put it onto the battlefield tapped, then shuffle.| +Kura, the Boundless Sky|Kamigawa: Neon Dynasty|200|M|{3}{G}{G}|Legendary Creature - Dragon Spirit|4|4|Flying, deathtouch$When Kura, the Boundless Sky dies, choose one —$• Search your library for up to three land cards, reveal them, put them into your hand, then shuffle.$• Create an X/X green Spirit creature token, where X is the number of lands you control.| +March of Burgeoning Life|Kamigawa: Neon Dynasty|201|R|{X}{G}|Instant|||As an additional cost to cast this spell, you may exile any number of green cards from your hand. This spell costs {2} less to cast for each card exiled this way.$Choose target creature with mana value less than X. Search your library for a creature card with the same name as that creature, put it onto the battlefield tapped, then shuffle.| +Master's Rebuke|Kamigawa: Neon Dynasty|202|C|{1}{G}|Instant|||Target creature you control deals damage equal to its power to target creature or planeswalker you don't control.| +Orochi Merge-Keeper|Kamigawa: Neon Dynasty|203|U|{1}{G}|Creature - Snake Druid|1|1|{T}: Add {G}.$As long as Orochi Merge-Keeper is modified, it has "{T}: Add {G}{G}."| +Roaring Earth|Kamigawa: Neon Dynasty|204|U|{1}{G}|Enchantment|||Whenever a land enters the battlefield under your control, put a +1/+1 counter on target creature or Vehicle you control.$Channel — {X}{G}{G}, Discard Roaring Earth: Put X +1/+1 counters on target land you control. It becomes a 0/0 green Spirit creature with haste. It's still a land.| +Season of Renewal|Kamigawa: Neon Dynasty|205|C|{2}{G}|Instant|||Choose one or both —$• Return target creature card from your graveyard to your hand.$• Return target enchantment card from your graveyard to your hand.| +Shigeki, Jukai Visionary|Kamigawa: Neon Dynasty|206|R|{1}{G}|Legendary Enchantment Creature - Snake Druid|1|3|{1}{G}, {T}, Return Shigeki, Jukai Visionary to its owner's hand: Reveal the top four cards of your library. You may put a land card from among them onto the battlefield tapped. Put the rest into your graveyard.$Channel — {X}{X}{G}{G}, Discard Shigeki: Return X target nonlegendary cards from your graveyard to your hand.| +Spinning Wheel Kick|Kamigawa: Neon Dynasty|207|U|{X}{X}{G}{G}|Sorcery|||Target creature you control deals damage equal to its power to each of X target creatures and/or planeswalkers.| +Spring-Leaf Avenger|Kamigawa: Neon Dynasty|208|R|{3}{G}{G}|Creature - Insect Ninja|6|5|Ninjutsu {3}{G}$Whenever Spring-Leaf Avenger deals combat damage to a player, return target permanent card from your graveyard to your hand.| +Storyweave|Kamigawa: Neon Dynasty|209|U|{2}{G}|Instant|||Choose one —$• Put two +1/+1 counters on target creature you control.$• Put two lore counters on target Saga you control. The next time one or more enchantment creatures enter the battlefield under your control this turn, each enters with two additional +1/+1 counters on it.| +Tales of Master Seshiro|Kamigawa: Neon Dynasty|210|C|{4}{G}|Enchantment - Saga|||(As this Saga enters and after your draw step, add a lore counter.)$I, II — Put a +1/+1 counter on target creature or Vehicle you control. It gains vigilance until end of turn.$III — Exile this Saga, then return it to the battlefield transformed under your control.| +Seshiro's Living Legacy|Kamigawa: Neon Dynasty|210|C||Enchantment Creature - Snake Warrior|5|5|Vigilance, haste| +Tamiyo's Safekeeping|Kamigawa: Neon Dynasty|211|C|{G}|Instant|||Target permanent you control gains hexproof and indestructible until end of turn. You gain 2 life.| +Teachings of the Kirin|Kamigawa: Neon Dynasty|212|R|{1}{G}|Enchantment - Saga|||(As this Saga enters and after your draw step, add a lore counter.)$I — Mill three cards. Create a 1/1 colorless Spirit creature token.$II — Put a +1/+1 counter on target creature you control.$III — Exile this Saga, then return it to the battlefield transformed under your control.| +Kirin-Touched Orochi|Kamigawa: Neon Dynasty|212|R||Enchantment Creature - Snake Monk|1|1|Whenever Kirin-Touched Orochi attacks, choose one —$• Exile target creature card from a graveyard. When you do, create a 1/1 colorless Spirit creature token.$• Exile target noncreature card from a graveyard. When you do, put a +1/+1 counter on target creature you control.| +Weaver of Harmony|Kamigawa: Neon Dynasty|213|R|{1}{G}|Enchantment Creature - Snake Druid|2|2|Other enchantment creatures you control get +1/+1.${G}, {T}: Copy target activated or triggered ability you control from an enchantment source. You may choose new targets for the copy.| +Webspinner Cuff|Kamigawa: Neon Dynasty|214|U|{2}{G}|Artifact Creature - Equipment Spider|1|4|Reach$Equipped creature gets +1/+4 and has reach.$Reconfigure {4}| +Asari Captain|Kamigawa: Neon Dynasty|215|U|{3}{R}{W}|Creature - Human Samurai|4|3|Haste$Whenever a Samurai or Warrior you control attacks alone, it gets +1/+0 until end of turn for each Samurai or Warrior you control.| +Colossal Skyturtle|Kamigawa: Neon Dynasty|216|U|{4}{G}{G}{U}|Enchantment Creature - Turtle|6|5|Flying, ward {2}$Channel — {2}{G}, Discard Colossal Skyturtle: Return target card from your graveyard to your hand.$Channel — {1}{U}, Discard Colossal Skyturtle: Return target creature to its owner's hand.| +Eiganjo Uprising|Kamigawa: Neon Dynasty|217|R|{X}{R}{W}|Sorcery|||Create X 2/2 white Samurai creature tokens with vigilance. They gain menace and haste until end of turn.$Each opponent creates X minus one 2/2 white Samurai creature tokens with vigilance.| +Enthusiastic Mechanaut|Kamigawa: Neon Dynasty|218|U|{U}{R}|Artifact Creature - Goblin Artificer|2|2|Flying$Artifact spells you cast cost {1} less to cast.| +Gloomshrieker|Kamigawa: Neon Dynasty|219|U|{1}{B}{G}|Enchantment Creature - Cat Beast|2|1|Menace$When Gloomshrieker enters the battlefield, return target permanent card from your graveyard to your hand.$If Gloomshrieker would die, exile it instead.| +Greasefang, Okiba Boss|Kamigawa: Neon Dynasty|220|R|{1}{W}{B}|Legendary Creature - Rat Pilot|4|3|At the beginning of combat on your turn, return target Vehicle card from your graveyard to the battlefield. It gains haste. Return it to its owner's hand at the beginning of your next end step.| +Hidetsugu Consumes All|Kamigawa: Neon Dynasty|221|M|{1}{B}{R}|Enchantment - Saga|||(As this Saga enters and after your draw step, add a lore counter.)$I — Destroy each nonland permanent with mana value 1 or less.$II — Exile all graveyards.$III — Exile this Saga, then return it to the battlefield transformed under your control.| +Vessel of the All-Consuming|Kamigawa: Neon Dynasty|221|M||Enchantment Creature - Ogre Shaman|3|3|Trample$Whenever Vessel of the All-Consuming deals damage, put a +1/+1 counter on it.$Whenever Vessel of the All-Consuming deals damage to a player, if it has dealt 10 or more damage to that player this turn, they lose the game.| +Hinata, Dawn-Crowned|Kamigawa: Neon Dynasty|222|R|{1}{U}{R}{W}|Legendary Creature - Kirin Spirit|4|4|Flying, trample$Spells you cast cost {1} less to cast for each target.$Spells your opponents cast cost {1} more to cast for each target.| +Invigorating Hot Spring|Kamigawa: Neon Dynasty|223|U|{1}{R}{G}|Enchantment|||Invigorating Hot Spring enters the battlefield with four +1/+1 counters on it.$Modified creatures you control have haste.$Remove a +1/+1 counter from Invigorating Hot Spring: Put a +1/+1 counter on target creature you control. Activate only as a sorcery and only once each turn.| +Isshin, Two Heavens as One|Kamigawa: Neon Dynasty|224|R|{R}{W}{B}|Legendary Creature - Human Samurai|3|4|If a creature attacking causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time.| +Jukai Naturalist|Kamigawa: Neon Dynasty|225|U|{G}{W}|Enchantment Creature - Human Monk|2|2|Lifelink$Enchantment spells you cast cost {1} less to cast.| +Kaito Shizuki|Kamigawa: Neon Dynasty|226|M|{1}{U}{B}|Legendary Planeswalker - Kaito|3|At the beginning of your end step, if Kaito Shizuki entered the battlefield this turn, he phases out.$+1: Draw a card. Then discard a card unless you attacked this turn.$−2: Create a 1/1 blue Ninja creature token with "This creature can't be blocked."$−7: You get an emblem with "Whenever a creature you control deals combat damage to a player, search your library for a blue or black creature card, put it onto the battlefield, then shuffle."| +The Kami War|Kamigawa: Neon Dynasty|227|M|{1}{W}{U}{B}{R}{G}|Enchantment - Saga|||(As this Saga enters and after your draw step, add a lore counter.)$I — Exile target nonland permanent an opponent controls.$II — Return up to one other target nonland permanent to its owner's hand. Then each opponent discards a card.$III — Exile this Saga, then return it to the battlefield transformed under your control.| +O-Kagachi Made Manifest|Kamigawa: Neon Dynasty|227|M||Enchantment Creature - Dragon Spirit|6|6|O-Kagachi Made Manifest is all colors.$Flying, trample$Whenever O-Kagachi Made Manifest attacks, defending player chooses a nonland card in your graveyard. Return that card to your hand. O-Kagachi Made Manifest gets +X/+0 until end of turn, where X is the mana value of that card.| +Kotose, the Silent Spider|Kamigawa: Neon Dynasty|228|R|{3}{U}{B}|Legendary Creature - Human Ninja|4|4|When Kotose, the Silent Spider enters the battlefield, exile target card other than a basic land card from an opponent's graveyard. Search that player's graveyard, hand, and library for any number of cards with the same name as that card and exile them. Then that player shuffles. For as long as you control Kotose, you may play one of the exiled cards, and you may spend mana as though it were mana of any color to cast it.| +Naomi, Pillar of Order|Kamigawa: Neon Dynasty|229|U|{3}{W}{B}|Legendary Creature - Human Advisor|4|4|Whenever Naomi, Pillar of Order enters the battlefield or attacks, if you control an artifact and an enchantment, create a 2/2 white Samurai creature token with vigilance.| +Oni-Cult Anvil|Kamigawa: Neon Dynasty|230|U|{B}{R}|Artifact|||Whenever one or more artifacts you control leave the battlefield during your turn, create a 1/1 colorless Construct artifact creature token. This ability triggers only once each turn.${T}, Sacrifice an artifact: Oni-Cult Anvil deals 1 damage to each opponent. You gain 1 life.| +Prodigy's Prototype|Kamigawa: Neon Dynasty|231|U|{1}{W}{U}|Artifact - Vehicle|3|4|Whenever one or more Vehicles you control attack, create a 1/1 colorless Pilot creature token with "This creature crews Vehicles as though its power were 2 greater."$Crew 2| +Raiyuu, Storm's Edge|Kamigawa: Neon Dynasty|232|R|{2}{R}{W}|Legendary Creature - Human Samurai|3|3|First strike$Whenever a Samurai or Warrior you control attacks alone, untap it. If it's the first combat phase of the turn, there is an additional combat phase after this phase.| +Risona, Asari Commander|Kamigawa: Neon Dynasty|233|R|{1}{R}{W}|Legendary Creature - Human Samurai|3|3|Haste$Whenever Risona, Asari Commander deals combat damage to a player, if it doesn't have an indestructible counter on it, put an indestructible counter on it.$Whenever combat damage is dealt to you, remove an indestructible counter from Risona.| +Satoru Umezawa|Kamigawa: Neon Dynasty|234|R|{1}{U}{B}|Legendary Creature - Human Ninja|2|4|Whenever you activate a ninjutsu ability, look at the top three cards of your library. Put one of them into your hand and the rest on the bottom of your library in any order. This ability triggers only once each turn.$Each creature card in your hand has ninjutsu {2}{U}{B}.| +Satsuki, the Living Lore|Kamigawa: Neon Dynasty|235|R|{G}{W}|Legendary Creature - Human Druid|1|3|{T}: Put a lore counter on each Saga you control. Activate only as a sorcery.$When Satsuki, the Living Lore dies, choose up to one —$• Return target Saga or enchantment creature you control to its owner's hand.$• Return target Saga card from your graveyard to your hand.| +Silver-Fur Master|Kamigawa: Neon Dynasty|236|U|{U}{B}|Creature - Rat Ninja|2|2|Ninjutsu {U}{B}$Ninjutsu abilities you activate cost {1} less to activate.$Other Ninja and Rogue creatures you control get +1/+1.| +Spirit-Sister's Call|Kamigawa: Neon Dynasty|237|M|{3}{W}{B}|Enchantment|||At the beginning of your end step, choose target permanent card in your graveyard. You may sacrifice a permanent that shares a card type with the chosen card. If you do, return the chosen card from your graveyard to the battlefield and it gains "If this permanent would leave the battlefield, exile it instead of putting it anywhere else."| +Tamiyo, Compleated Sage|Kamigawa: Neon Dynasty|238|M|{2}{G}{G/U/P}{U}|Legendary Planeswalker - Tamiyo|5|Compleated$+1: Tap up to one target artifact or creature. It doesn't untap during its controller's next untap step.$−X: Exile target nonland permanent card with mana value X from your graveyard. Create a token that's a copy of that card.$−7: Create Tamiyo's Notebook, a legendary colorless artifact token with "Spells you cast cost {2} less to cast" and "{T}: Draw a card."| +Automated Artificer|Kamigawa: Neon Dynasty|239|C|{2}|Artifact Creature - Artificer|1|3|{T}: Add {C}. Spend this mana only to activate an ability or cast an artifact spell.| +Bronze Cudgels|Kamigawa: Neon Dynasty|240|U|{1}|Artifact - Equipment|||{2}: Until end of turn, equipped creature gets +X/+0, where X is the number of times this ability has resolved this turn.$Equip {1}| +Brute Suit|Kamigawa: Neon Dynasty|241|C|{3}|Artifact - Vehicle|4|3|Vigilance$Crew 1| +Circuit Mender|Kamigawa: Neon Dynasty|242|U|{3}|Artifact Creature - Insect|2|3|When Circuit Mender enters the battlefield, you gain 2 life.$When Circuit Mender leaves the battlefield, draw a card.| +Containment Construct|Kamigawa: Neon Dynasty|243|U|{2}|Artifact Creature - Construct|2|1|Whenever you discard a card, you may exile that card from your graveyard. If you do, you may play that card this turn.| +Dramatist's Puppet|Kamigawa: Neon Dynasty|244|C|{4}|Artifact Creature - Construct|2|4|When Dramatist's Puppet enters the battlefield, for each kind of counter on target permanent, put another counter of that kind on it or remove one from it.| +Eater of Virtue|Kamigawa: Neon Dynasty|245|R|{1}|Legendary Artifact - Equipment|||Whenever equipped creature dies, exile it.$Equipped creature gets +2/+0.$As long as a card exiled with Eater of Virtue has flying, equipped creature has flying. The same is true for first strike, double strike, deathtouch, haste, hexproof, indestructible, lifelink, menace, protection, reach, trample, and vigilance.$Equip {1}| +Ecologist's Terrarium|Kamigawa: Neon Dynasty|246|C|{2}|Artifact|||When Ecologist's Terrarium enters the battlefield, you may search your library for a basic land card, reveal it, put it into your hand, then shuffle.${2}, {T}, Sacrifice Ecologist's Terrarium: Put a +1/+1 counter on target creature. Activate only as a sorcery.| +High-Speed Hoverbike|Kamigawa: Neon Dynasty|247|U|{2}|Artifact - Vehicle|2|2|Flash$Flying$When High-Speed Hoverbike enters the battlefield, tap up to one target creature.$Crew 1| +Iron Apprentice|Kamigawa: Neon Dynasty|248|C|{1}|Artifact Creature - Construct|0|0|Iron Apprentice enters the battlefield with a +1/+1 counter on it.$When Iron Apprentice dies, if it had counters on it, put those counters on target creature you control.| +Mechtitan Core|Kamigawa: Neon Dynasty|249|R|{2}|Artifact - Vehicle|2|4|{5}, Exile Mechtitan Core and four other artifact creatures and/or Vehicles you control: Create Mechtitan, a legendary 10/10 Construct artifact creature token with flying, vigilance, trample, lifelink, and haste that's all colors. When that token leaves the battlefield, return all cards exiled with Mechtitan Core except Mechtitan Core to the battlefield tapped under their owners' control.$Crew 2| +Mirror Box|Kamigawa: Neon Dynasty|250|R|{3}|Artifact|||The "legend rule" doesn't apply to permanents you control.$Each legendary creature you control gets +1/+1.$Each nontoken creature you control gets +1/+1 for each other creature you control with the same name as that creature.| +Network Terminal|Kamigawa: Neon Dynasty|251|C|{3}|Artifact|||{T}: Add one mana of any color.${1}, {T}, Tap another untapped artifact you control: Draw a card, then discard a card.| +Ninja's Kunai|Kamigawa: Neon Dynasty|252|C|{1}|Artifact - Equipment|||Equipped creature has "{1}, {T}, Sacrifice Ninja's Kunai: Ninja's Kunai deals 3 damage to any target."$Equip {1}| +Papercraft Decoy|Kamigawa: Neon Dynasty|253|C|{2}|Artifact Creature - Frog|2|1|When Papercraft Decoy leaves the battlefield, you may pay {2}. If you do, draw a card.| +Patchwork Automaton|Kamigawa: Neon Dynasty|254|U|{2}|Artifact Creature - Construct|1|1|Ward {2}$Whenever you cast an artifact spell, put a +1/+1 counter on Patchwork Automaton.| +Reckoner Bankbuster|Kamigawa: Neon Dynasty|255|R|{2}|Artifact - Vehicle|4|4|Reckoner Bankbuster enters the battlefield with three charge counters on it.${2}, {T}, Remove a charge counter from Reckoner Bankbuster: Draw a card. Then if there are no charge counters on Reckoner Bankbuster, create a Treasure token and a 1/1 colorless Pilot creature token with "This creature crews Vehicles as though its power were 2 greater."$Crew 3| +Reito Sentinel|Kamigawa: Neon Dynasty|256|U|{3}|Artifact Creature - Construct|3|3|Defender$When Reito Sentinel enters the battlefield, target player mills three cards.${3}: Put target card from a graveyard on the bottom of its owner's library.| +Runaway Trash-Bot|Kamigawa: Neon Dynasty|257|U|{3}|Artifact Creature - Construct|0|4|Trample$Runaway Trash-Bot gets +1/+0 for each artifact and/or enchantment card in your graveyard.| +Searchlight Companion|Kamigawa: Neon Dynasty|258|C|{3}|Artifact Creature - Drone|1|1|Flying$When Searchlight Companion enters the battlefield, create a 1/1 colorless Spirit creature token.| +Shrine Steward|Kamigawa: Neon Dynasty|259|C|{5}|Artifact Creature - Construct|3|2|When Shrine Steward enters the battlefield, you may search your library for an Aura or Shrine card, reveal it, put it into your hand, then shuffle.| +Surgehacker Mech|Kamigawa: Neon Dynasty|260|R|{4}|Artifact - Vehicle|5|5|Menace$When Surgehacker Mech enters the battlefield, it deals damage equal to twice the number of Vehicles you control to target creature or planeswalker an opponent controls.$Crew 4| +Thundersteel Colossus|Kamigawa: Neon Dynasty|261|C|{7}|Artifact - Vehicle|7|7|Trample, haste$Crew 2| +Towashi Guide-Bot|Kamigawa: Neon Dynasty|262|U|{4}|Artifact Creature - Construct|2|1|When Towashi Guide-Bot enters the battlefield, put a +1/+1 counter on target creature you control.${4}, {T}: Draw a card. This ability costs {1} less to activate for each modified creature you control.| +Walking Skyscraper|Kamigawa: Neon Dynasty|263|U|{8}|Artifact Creature - Construct|8|8|This spell costs {1} less to cast for each modified creature you control.$Trample$Walking Skyscraper has hexproof as long as it's untapped.| +Bloodfell Caves|Kamigawa: Neon Dynasty|264|C||Land|||Bloodfell Caves enters the battlefield tapped.$When Bloodfell Caves enters the battlefield, you gain 1 life.${T}: Add {B} or {R}.| +Blossoming Sands|Kamigawa: Neon Dynasty|265|C||Land|||Blossoming Sands enters the battlefield tapped.$When Blossoming Sands enters the battlefield, you gain 1 life.${T}: Add {G} or {W}.| +Boseiju, Who Endures|Kamigawa: Neon Dynasty|266|R||Legendary Land|||{T}: Add {G}.$Channel — {1}{G}, Discard Boseiju, Who Endures: Destroy target artifact, enchantment, or nonbasic land an opponent controls. That player may search their library for a land card with a basic land type, put it onto the battlefield, then shuffle. This ability costs {1} less to activate for each legendary creature you control.| +Dismal Backwater|Kamigawa: Neon Dynasty|267|C||Land|||Dismal Backwater enters the battlefield tapped.$When Dismal Backwater enters the battlefield, you gain 1 life.${T}: Add {U} or {B}.| +Eiganjo, Seat of the Empire|Kamigawa: Neon Dynasty|268|R||Legendary Land|||{T}: Add {W}.$Channel — {2}{W}, Discard Eiganjo, Seat of the Empire: It deals 4 damage to target attacking or blocking creature. This ability costs {1} less to activate for each legendary creature you control.| +Jungle Hollow|Kamigawa: Neon Dynasty|269|C||Land|||Jungle Hollow enters the battlefield tapped.$When Jungle Hollow enters the battlefield, you gain 1 life.${T}: Add {B} or {G}.| +Mech Hangar|Kamigawa: Neon Dynasty|270|U||Land|||{T}: Add {C}.${T}: Add one mana of any color. Spend this mana only to cast a Pilot or Vehicle spell.${3}, {T}: Target Vehicle becomes an artifact creature until end of turn.| +Otawara, Soaring City|Kamigawa: Neon Dynasty|271|R||Legendary Land|||{T}: Add {U}.$Channel — {3}{U}, Discard Otawara, Soaring City: Return target artifact, creature, enchantment, or planeswalker to its owner's hand. This ability costs {1} less to activate for each legendary creature you control.| +Roadside Reliquary|Kamigawa: Neon Dynasty|272|U||Land|||{T}: Add {C}.${2}, {T}, Sacrifice Roadside Reliquary: Draw a card if you control an artifact. Draw a card if you control an enchantment.| +Rugged Highlands|Kamigawa: Neon Dynasty|273|C||Land|||Rugged Highlands enters the battlefield tapped.$When Rugged Highlands enters the battlefield, you gain 1 life.${T}: Add {R} or {G}.| +Scoured Barrens|Kamigawa: Neon Dynasty|274|C||Land|||Scoured Barrens enters the battlefield tapped.$When Scoured Barrens enters the battlefield, you gain 1 life.${T}: Add {W} or {B}.| +Secluded Courtyard|Kamigawa: Neon Dynasty|275|U||Land|||As Secluded Courtyard enters the battlefield, choose a creature type.${T}: Add {C}.${T}: Add one mana of any color. Spend this mana only to cast a creature spell of the chosen type or activate an ability of a creature or creature card of the chosen type.| +Sokenzan, Crucible of Defiance|Kamigawa: Neon Dynasty|276|R||Legendary Land|||{T}: Add {R}.$Channel — {3}{R}, Discard Sokenzan, Crucible of Defiance: Create two 1/1 colorless Spirit creature tokens. They gain haste until end of turn. This ability costs {1} less to activate for each legendary creature you control.| +Swiftwater Cliffs|Kamigawa: Neon Dynasty|277|C||Land|||Swiftwater Cliffs enters the battlefield tapped.$When Swiftwater Cliffs enters the battlefield, you gain 1 life.${T}: Add {U} or {R}.| +Takenuma, Abandoned Mire|Kamigawa: Neon Dynasty|278|R||Legendary Land|||{T}: Add {B}.$Channel — {3}{B}, Discard Takenuma, Abandoned Mire: Mill three cards, then return a creature or planeswalker card from your graveyard to your hand. This ability costs {1} less to activate for each legendary creature you control.| +Thornwood Falls|Kamigawa: Neon Dynasty|279|C||Land|||Thornwood Falls enters the battlefield tapped.$When Thornwood Falls enters the battlefield, you gain 1 life.${T}: Add {G} or {U}.| +Tranquil Cove|Kamigawa: Neon Dynasty|280|C||Land|||Tranquil Cove enters the battlefield tapped.$When Tranquil Cove enters the battlefield, you gain 1 life.${T}: Add {W} or {U}.| +Uncharted Haven|Kamigawa: Neon Dynasty|281|C||Land|||Uncharted Haven enters the battlefield tapped.$As Uncharted Haven enters the battlefield, choose a color.${T}: Add one mana of the chosen color.| +Wind-Scarred Crag|Kamigawa: Neon Dynasty|282|C||Land|||Wind-Scarred Crag enters the battlefield tapped.$When Wind-Scarred Crag enters the battlefield, you gain 1 life.${T}: Add {R} or {W}.| +Plains|Kamigawa: Neon Dynasty|283|C||Basic Land - Plains|||({T}: Add {W}.)| +Island|Kamigawa: Neon Dynasty|285|C||Basic Land - Island|||({T}: Add {U}.)| +Swamp|Kamigawa: Neon Dynasty|287|C||Basic Land - Swamp|||({T}: Add {B}.)| +Mountain|Kamigawa: Neon Dynasty|289|C||Basic Land - Mountain|||({T}: Add {R}.)| +Forest|Kamigawa: Neon Dynasty|291|C||Basic Land - Forest|||({T}: Add {G}.)| +Chishiro, the Shattered Blade|Neon Dynasty Commander|1|M|{2}{R}{G}|Legendary Creature - Snake Samurai|4|4|Whenever an Aura or Equipment enters the battlefield under your control, create a 2/2 red Spirit creature token with menace.$At the beginning of your end step, put a +1/+1 counter on each modified creature you control.| +Kotori, Pilot Prodigy|Neon Dynasty Commander|2|M|{1}{W}{U}|Legendary Creature - Moonfolk Pilot|2|4|Vehicles you control have crew 2.$At the beginning of combat on your turn, target artifact creature you control gains lifelink and vigilance until end of turn.| +Kaima, the Fractured Calm|Neon Dynasty Commander|3|M|{2}{R}{G}|Legendary Creature - Spirit|3|3|At the beginning of your end step, goad each creature your opponents control that's enchanted by an Aura you control. Put a +1/+1 counter on Kaima, the Fractured Calm for each creature goaded this way.| +Shorikai, Genesis Engine|Neon Dynasty Commander|4|M|{2}{W}{U}|Legendary Artifact - Vehicle|8|8|{1}, {T}: Draw two cards, then discard a card. Create a 1/1 colorless Pilot creature token with "This creature crews Vehicles as though its power were 2 greater."$Crew 8$Shorikai, Genesis Engine can be your commander.| +Aerial Surveyor|Neon Dynasty Commander|5|R|{2}{W}|Artifact - Vehicle|3|4|Flying$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.$Crew 2| +Drumbellower|Neon Dynasty Commander|6|R|{2}{W}|Creature - Spirit|2|1|Flying$Untap all creatures you control during each other player's untap step.| +Ironsoul Enforcer|Neon Dynasty Commander|7|R|{4}{W}|Artifact Creature - Human Samurai|4|4|Whenever Ironsoul Enforcer or a commander you control attacks alone, return target artifact card from your graveyard to the battlefield.| +Organic Extinction|Neon Dynasty Commander|8|R|{8}{W}{W}|Sorcery|||Improvise$Destroy all nonartifact creatures.| +Release to Memory|Neon Dynasty Commander|9|R|{3}{W}|Instant|||Exile target opponent's graveyard. For each creature card exiled this way, create a 1/1 colorless Spirit creature token.| +Swift Reconfiguration|Neon Dynasty Commander|10|R|{W}|Enchantment - Aura|||Flash$Enchant creature or Vehicle$Enchanted permanent is a Vehicle artifact with crew 5 and it loses all other card types.| +Access Denied|Neon Dynasty Commander|11|R|{3}{U}{U}|Instant|||Counter target spell. Create X 1/1 colorless Thopter artifact creature tokens with flying, where X is that spell's mana value.| +Cyberdrive Awakener|Neon Dynasty Commander|12|R|{5}{U}|Artifact Creature - Construct|4|4|Flying$Other artifact creatures you control have flying.$When Cyberdrive Awakener enters the battlefield, until end of turn, each noncreature artifact you control becomes an artifact creature with base power and toughness 4/4.| +Imposter Mech|Neon Dynasty Commander|13|R|{1}{U}|Artifact - Vehicle|3|1|You may have Imposter Mech enter the battlefield as a copy of a creature an opponent controls, except its a Vehicle artifact with crew 3 and it loses all other card types.$Crew 3| +Kappa Cannoneer|Neon Dynasty Commander|14|R|{5}{U}|Artifact Creature - Turtle Warrior|4|4|Improvise$Ward {4}$Whenever an artifact enters the battlefield under your control, put a +1/+1 counter on Kappa Cannoneer and it can't be blocked this turn.| +Katsumasa, the Animator|Neon Dynasty Commander|15|R|{2}{U}{U}|Legendary Creature - Moonfolk Artificer|3|3|Flying${2}{U}: Until end of turn, target noncreature artifact you control becomes an artifact creature and gains flying. If it's not a Vehicle, it has base power and toughness 1/1 until end of turn.$At the beginning of your upkeep, put a +1/+1 counter on each of up to three target noncreature artifacts.| +Research Thief|Neon Dynasty Commander|16|R|{4}{U}|Artifact Creature - Moonfolk Wizard|3|3|Flash$Flying$Whenever an artifact creature you control deals combat damage to a player, draw a card.| +Universal Surveillance|Neon Dynasty Commander|17|R|{X}{U}{U}{U}|Sorcery|||Improvise$Draw X cards.| +Akki Battle Squad|Neon Dynasty Commander|18|R|{5}{R}|Creature - Goblin Samurai|6|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.| +Collision of Realms|Neon Dynasty Commander|19|R|{6}{R}|Sorcery|||Each player shuffles all creatures they own into their library. Each player who shuffled a nontoken creature into their library this way reveals cards from the top of their library until they reveal a creature card, then puts that card onto the battlefield and the rest on the bottom of their library in a random order.| +Kami of Celebration|Neon Dynasty Commander|20|R|{4}{R}|Creature - Spirit|3|3|Whenever a modified creature you control attacks, exile the top card of your library. You may play that card this turn.$Whenever you cast a spell from exile, put a +1/+1 counter on target creature you control.| +Komainu Battle Armor|Neon Dynasty Commander|21|R|{2}{R}|Artifact Creature - Equipment Dog|2|2|Menace$Equipped creature gets +2/+2 and has menace.$Whenever Komainu Battle Armor or equipped creature deals combat damage to a player, goad each creature that player controls.$Reconfigure {4}| +Smoke Spirits' Aid|Neon Dynasty Commander|22|R|{X}{R}|Sorcery|||For each of up to X target creatures, create a red Aura enchantment token named Smoke Blessing attached to that creature. Those tokens have enchant creature and "When enchanted creature dies, it deals 1 damage to its controller and you create a Treasure token."| +Unquenchable Fury|Neon Dynasty Commander|23|R|{2}{R}|Enchantment - Aura|||Enchant creature$Enchanted creature has "Whenever this creature attacks, it deals X damage to defending player, where X is the number of cards in their hand."$When Unquenchable Fury is put into your graveyard from the battlfield, return it to your hand.| +Ascendant Acolyte|Neon Dynasty Commander|24|R|{4}{G}|Creature - Human Monk|1|1|Ascendant Acolyte enters the battlefield with a +1/+1 counter on it for each +1/+1 counter among other creatures you control.$At the beginning of your upkeep, double the number of +1/+1 counters on Ascendant Acolyte.| +Concord with the Kami|Neon Dynasty Commander|25|R|{3}{G}|Enchantment|||At the beginning of your end step, choose one or more —$• Put a +1/+1 counter on target creature with a counter on it.$• Draw a card if you control an enchanted creature.$• Create a 1/1 colorless Spirit creature token if you control an equipped creature.| +Kosei, Penitent Warlord|Neon Dynasty Commander|26|R|{1}{G}{G}|Legendary Creature - Ogre Samurai|0|5|As long as Kosei, Penitent Warlord is enchanted, equipped, and has a counter on it, Kosei has "Whenever Kosei, Penitent Warlord deals combat damage to an opponent, you draw that many cards and Kosei deals that much damage to each opponent."| +One with the Kami|Neon Dynasty Commander|27|R|{3}{G}|Enchantment - Aura|||Flash$Enchant creature you control$Whenever enchanted creature or another modified creature you control dies, create X 1/1 colorless Spirit creature tokens, where X is that creature's power.| +Rampant Rejuvenator|Neon Dynasty Commander|28|R|{3}{G}|Creature - Plant Hydra|0|0|Rampant Rejuvenator enters the battlefield with two +1/+1 counters on it.$When Rampant Rejuvenator dies, search your library for up to X basic land cards, where X is Rampant Rejuvenator's power, put them onto the battlefield, then shuffle.| +Silkguard|Neon Dynasty Commander|29|R|{X}{G}|Instant|||Put a +1/+1 counter on each of up to X target creatures you control.$Auras, Equipment, and modified creatures you control gain hexproof until end of turn.| +Tanuki Transplanter|Neon Dynasty Commander|30|R|{3}{G}|Artifact Creature - Equipment Dog|2|4|Whenever Tanuki Transplanter or equipped creature attacks, add an amount of {G} equal to its power. Until end of turn, you don't lose this mana as steps and phases end.$Reconfigure {3}| +Myojin of Blooming Dawn|Neon Dynasty Commander|31|R|{5}{W}{W}{W}|Legendary Creature - Spirit|4|6|Myojin of Blooming Dawn enters the battlefield with an indestructible counter on it if you cast it from your hand.$Remove an indestructible counter from Myojin of Blooming Dawn: Create a 1/1 colorless Spirit creature token for each permanent you control.| +Yoshimaru, Ever Faithful|Neon Dynasty Commander|32|M|{W}|Legendary Creature - Dog|1|1|Whenever another legendary permanent enters the battlefield under your control, put a +1/+1 counter on Yoshimaru, Ever Faithful.$Partner| +Myojin of Cryptic Dreams|Neon Dynasty Commander|33|R|{5}{U}{U}{U}|Legendary Creature - Spirit|3|3|Myojin of Cryptic Dreams enters the battlefield with an indestructible counter on it if you cast it from your hand.$Remove an indestructible counter from Myojin of Cryptic Dreams: Copy target permanent spell you control three times.| +Myojin of Grim Betrayal|Neon Dynasty Commander|34|R|{5}{B}{B}{B}|Legendary Creature - Spirit|5|2|Myojin of Grim Betrayal enters the battlefield with an indestructible counter on it if you cast it from your hand.$Remove an indestructible counter from Myojin of Grim Betrayal: Put onto the battlefield under your control all creature cards in all graveyards that were put there from anywhere this turn.| +Ruthless Technomancer|Neon Dynasty Commander|35|R|{3}{B}|Creature - Human Wizard|2|4|When Ruthless Technomancer enters the battlefield, you may sacrifice another creature you control. If you do, create a number of Treasure tokens equal to that creature's power.${2}{B}, Sacrifice X artifacts: Return target creature card with power X or less from your graveyard to the battlefield. X can't be 0.| +Myojin of Roaring Blades|Neon Dynasty Commander|36|R|{5}{R}{R}{R}|Legendary Creature - Spirit|7|4|Myojin of Roaring Blades enters the battlefield with an indestructible counter on it if you cast it from your hand.$Remove an indestructible counter from Myojin of Roaring Blades: It deals 7 damage to each of up to three targets.| +Go-Shintai of Life's Origin|Neon Dynasty Commander|37|M|{3}{G}|Legendary Enchantment Creature - Shrine|3|4|{W}{U}{B}{R}{G}, {T}: Return target enchantment card from your graveyard to the battlefield.$Whenever Go-Shintai of Life's Origin or another nontoken Shrine enters the battlefield under your control, create a 1/1 colorless Shrine enchantment creature token.| +Myojin of Towering Might|Neon Dynasty Commander|38|R|{5}{G}{G}{G}|Legendary Creature - Spirit|8|8|Myojin of Towering Might enters the battlefield with an indestructible counter on it if you cast it from your hand.$Remove an indestructible counter from Myojin of Towering Might: Distribute eight +1/+1 counters among any number of target creatures you control. They gain trample until end of turn.| +Angel of Eternal Dawn|Alchemy: Innistrad|1|R|{2}{W}|Creature - Angel|2|4|Flying$When Angel of Eternal Dawn enters the battlefield, it becomes day.$It can't become night.$Your opponents can't cast spells with mana value greater than the number of turns they have begun.| +Angel of Unity|Alchemy: Innistrad|2|U|{1}{W}|Creature - Angel Cleric|1|3|Flying, lifelink$Whenever Angel of Unity enters the battlefield or you cast a party spell, choose a party creature card in your hand. It perpetually gets +1/+1.| +Captain Eberhart|Alchemy: Innistrad|3|M|{1}{W}|Legendary Creature - Human Soldier|1|1|Double strike$Spells cast from among cards you drew this turn cost {1} less to cast.$Spells cast from among cards your opponents drew this turn cost {1} more to cast.| +Divine Purge|Alchemy: Innistrad|4|R|{1}{W}{W}|Sorcery|||Exile all artifacts and creatures with mana value 3 or less. They perpetually gain "This spell costs {2} more to cast" and "This permanent enters the battlefield tapped." For as long as each of them remain exiled, its owner may play it.| +Ethereal Escort|Alchemy: Innistrad|5|M|{2}{W}|Creature - Spirit|3|3|Lifelink$Whenever Ethereal Escort enters the battlefield or attacks, choose a card in your hand. It perpetually gains lifelink.| +Expedition Supplier|Alchemy: Innistrad|6|R|{2}{W}|Creature - Human Warrior|2|2|Whenever Expedition Supplier or another Human enters the battlefield under your control, conjure a card named Utility Knife onto the battlefield. This ability triggers only once each turn.| +Faithful Disciple|Alchemy: Innistrad|7|U|{1}{W}|Creature - Human Cleric|2|2|Vigilance$When Faithful Disciple dies, draft a card from Faithful Disciple's spellbook.| +Inquisitor Captain|Alchemy: Innistrad|8|R|{3}{W}|Creature - Human Cleric|3|3|Vigilance$When Inquisitor Captain enters the battlefield, if you cast it and there are twenty or more creature cards with mana value 3 or less among cards in your graveyard, hand, and library, seek two creature cards with mana value 3 or less. Put one of them onto the battlefield and shuffle the other into your library.| +Sigardian Evangel|Alchemy: Innistrad|9|R|{1}{W}|Creature - Human Cleric|3|1|When Sigardian Evangel enters the battlefield, conjure a card named Sigardian Evangel into your hand. Discard that card at the beginning of the next end step.$When Sigardian Evangel enters the battlefield, tap target permanent you don't control.| +Slayer's Bounty|Alchemy: Innistrad|10|R|{W}|Legendary Artifact - Clue|||When Slayer's Bounty enters the battlefield, look at the creature cards in target opponent's hand.$Whenever you sacrifice Slayer's Bounty or another Clue, draft a card from Slayer's Bounty's spellbook.${2}, Sacrifice Slayer's Bounty: Draw a card.| +Suntail Squadron|Alchemy: Innistrad|11|R|{2}{W}{W}|Instant|||Conjure a card named Suntail Hawk into your hand. If you have fewer than 7 cards in hand, repeat this process.| +Absorb Energy|Alchemy: Innistrad|12|R|{1}{U}{U}|Instant|||Counter target spell. Cards in your hand that share a card type with that spell perpetually gain "This spell costs {1} less to cast."| +Unexpected Conversion|Alchemy: Innistrad|13|R|{2}{U}|Sorcery|||Draw two cards. Then you may exile an instant or sorcery card from your hand. If you do, search your hand and library for any number of cards with the same name, exile them, then shuffle. Seek an instant or sorcery card for each card exiled from your hand this way.| +Clone Crafter|Alchemy: Innistrad|14|R|{1}{U}|Creature - Human Wizard|1|2|When Clone Crafter enters the battlefield, conjure a duplicate of a random creature card from your opponent's library into your hand. It perpetually gains "You may spend mana as though it were mana of any color to cast this spell."| +Discover the Formula|Alchemy: Innistrad|15|R|{4}{U}{U}|Instant|||Seek three nonland cards, then nonland cards in your hand perpetually gain "This spell costs {1} less to cast."| +Geist of Regret|Alchemy: Innistrad|16|M|{4}{U}|Creature - Spirit|4|5|Flying$When Geist of Regret enters the battlefield, put a random instant card from your library into your graveyard. Then put a random sorcery card from your library into your graveyard.$Whenever you cast an instant or sorcery spell from your graveyard, copy that spell. You may choose new targets for the copy.| +Geistchanneler|Alchemy: Innistrad|17|R|{1}{U}|Creature - Human Wizard|1|3|When Geistchanneler enters the battlefield, choose an instant or sorcery card in your hand with mana value 3 or greater. It perpetually gains "This spell costs {2} less to cast."| +Kindred Denial|Alchemy: Innistrad|18|U|{2}{U}{U}|Instant|||Counter target spell. Seek a card with the same mana value as that spell.| +Obsessive Collector|Alchemy: Innistrad|19|R|{3}{U}|Creature - Spirit|4|3|Flying$Ward {2}$Whenever Obsessive Collector deals combat damage to a player, seek a card with mana value equal to the number of cards in your hand.| +Oglor, Devoted Assistant|Alchemy: Innistrad|20|M|{1}{U}|Legendary Creature - Homunculus|1|1|At the beginning of your upkeep, look at the top two cards of your library, then put one of them into your graveyard.$Whenever a creature card is put into your graveyard from your library or hand, it perpetually gains "When this card leaves your graveyard, create a tapped 2/2 black Zombie creature token."| +Rimewall Protector|Alchemy: Innistrad|21|U|{1}{U}{U}|Creature - Giant Wizard|3|4|Ward {1}$When Rimewall Protector enters the battlefield, each other Giant or Wizard you control and each Giant or Wizard card in your hand perpetually gains ward {1}.| +Sinister Reflections|Alchemy: Innistrad|22|R|{1}{U}|Instant|||Conjure a duplicate of each of up to two target nontoken creatures you control into your hand.| +Tireless Angler|Alchemy: Innistrad|23|R|{2}{U}|Creature - Human Rogue|1|4|Whenever an Island or Swamp enters the battlefield under your control, draft a card from Tireless Angler's spellbook.| +Cursebound Witch|Alchemy: Innistrad|24|U|{B}|Creature - Human Warlock|1|2|When Cursebound Witch dies, draft a card from Cursebound Witch's spellbook.| +Assemble from Parts|Alchemy: Innistrad|25|R|{B}|Instant|||Target creature card in your graveyard perpetually gains "{1}{B}{B}, Exile this card from your graveyard: Shuffle it into your library. Create a token that's a copy of it, except it's a 4/4 black Zombie in addition to its other types. Activate only as a sorcery."| +Break Expectations|Alchemy: Innistrad|26|U|{B}|Sorcery|||Target player reveals all cards with mana value 2 or greater in their hand. You choose a card from among those cards. Exile that card. If a card was exiled this way, that player drafts a card from Break Expectations's spellbook and reveals it.| +Citystalker Connoisseur|Alchemy: Innistrad|27|R|{3}{B}|Creature - Vampire|3|3|Deathtouch$When Citystalker Connoisseur enters the battlefield, target opponent discards a card with the greatest mana value among cards in their hand. Create a Blood token.| +Gutmorn, Pactbound Servant|Alchemy: Innistrad|28|M|{2}{B}|Legendary Creature - Demon|1|3|Flying, deathtouch$Whenever Gutmorn, Pactbound Servant enters the battlefield, each player discards a nonland card.$Whenever a player discards a card during your turn, they choose another player. That player conjures a duplicate of that card into their hand. It perpetually gains "You may spend mana as though it were mana of any color to cast this spell."| +Patient Zero|Alchemy: Innistrad|29|R|{1}{B}|Creature - Zombie|2|2|Lifelink$Damage isn't removed from creatures your opponents control during cleanup steps.| +Predatory Sludge|Alchemy: Innistrad|30|R|{2}{B}|Creature - Ooze|3|3|Menace$As Predatory Sludge enters the battlefield, choose a permanent you don't control. When the chosen permanent is put into a graveyard from the battlefield, conjure a card named Predatory Sludge into your hand.| +Puppet Raiser|Alchemy: Innistrad|31|M|{3}{B}|Creature - Zombie Wizard|3|4|At the beginning of your end step, exile up to one target creature card from your graveyard. If you do, seek a creature card with mana value equal to that mana value of that card plus one. That card perpetually gains menace.| +Sanguine Brushstroke|Alchemy: Innistrad|32|R|{1}{B}{B}|Enchantment|||When Sanguine Brushstroke enters the battlefield, create a Blood token and conjure a card named Blood Artist onto the battlefield.$Whenever you sacrifice a Blood token, each opponent loses 1 life.| +Sap Vitality|Alchemy: Innistrad|33|R|{B}{B}|Instant|||Sap Vitality deals 3 damage to target creature or planeswalker. Choose a creature card in your hand. It perpetually gets +3/+0.| +Veteran Ghoulcaller|Alchemy: Innistrad|34|R|{1}{B}|Creature - Human Rogue|2|1|Menace$Whenever a card in your graveyard is put into your hand, conjure a duplicate of that card into your hand.| +Arms Scavenger|Alchemy: Innistrad|35|R|{1}{R}|Creature - Human Warrior|2|2|At the beginning of your upkeep, draft a card from Arms Scavenger's spellbook, then exile it. Until end of turn, you may play that card.| +Bloodrage Alpha|Alchemy: Innistrad|36|R|{3}{R}|Creature - Wolf|4|4|When Bloodrage Alpha enters the battlefield, choose one —$• Another target Wolf or Werewolf you control fights target creature you don't control.$• When you cast your next Wolf or Werewolf spell, it gains "When this creature enters the battlefield, it fights up to one target creature you don't control."| +Brittle Blast|Alchemy: Innistrad|37|U|{2}{R}|Instant|||Creatures and planeswalkers your opponents control perpetually gain "If this permanent would die, exile it instead." Brittle Blast deals 5 damage to target creature or planeswalker.| +Conductive Current|Alchemy: Innistrad|38|R|{R}{R}{R}|Sorcery|||Conductive Current deals 3 damage to each creature. Choose an instant or sorcery card in your hand. It perpetually gains "If this spell would deal noncombat damage to a permanent or player, it deals that much damage plus 2 instead."| +Electrostatic Blast|Alchemy: Innistrad|39|R|{1}{R}|Instant|||Electrostatic Blast deals 2 damage to any target. When you cast your next instant or sorcery spell, exile the top three cards of your library. You may play one of those cards until end of turn.| +Fearsome Whelp|Alchemy: Innistrad|40|U|{1}{R}|Creature - Dragon|1|1|Flying$At the beginning of your upkeep, each Dragon card in your hand perpetually gains "This spell costs {1} less to cast."| +Frenzied Geistblaster|Alchemy: Innistrad|41|R|{1}{R}|Creature - Human Wizard|2|2|Prowess$When Frenzied Geistblaster enters the battlefield, if there are twenty or more instant and/or sorcery cards among cards in your graveyard, hand, and library, you may discard a card. If you do, seek an instant or sorcery card.| +Rahilda, Wanted Cutthroat|Alchemy: Innistrad|42|M|{1}{R}|Legendary Creature - Human Werewolf|2|2|First strike$When Rahilda, Wanted Cutthroat deals combat damage to a player, exile a nonland card from their library at random. During any turn you attacked with a Wolf or Werewolf, you may cast that card and you may spend mana as though it were mana of any color to cast that spell.$Daybound| +Rahilda, Feral Outlaw|Alchemy: Innistrad|42|M||Legendary Creature - Human Werewolf|2|2|Double strike$When Rahilda, Feral Outlaw deals combat damage to a player, exile a nonland card from their library at random. During any turn you attacked with a Wolf or Werewolf, you may cast that card and you may spend mana as though it were mana of any color to cast that spell.$Nightbound| +Tibalt, Wicked Tormentor|Alchemy: Innistrad|43|M|{3}{R}{R}|Legendary Planeswalker - Tibalt|3|+1: Add {R}{R}. Draft a card from Tibalt, Wicked Tormenter's spellbook, then exile it. Until end of turn, you may cast that card.$+1: Tibalt, Wicked Tormenter deals 4 damage to target creature or planeswalker unless its controller has Tibalt deal 4 damage to them. If they do, you may discard a card. If you do, draw a card.$−X: Create X 1/1 red Devil creature tokens with "When this creature dies, it deals 1 damage to any target."| +Toralf's Disciple|Alchemy: Innistrad|44|R|{2}{R}|Creature - Human Warrior|3|3|Haste$Whenever Toralf's Disciple attacks, conjure four cards named Lightning Bolt into your library, then shuffle.| +Town-Razer Tyrant|Alchemy: Innistrad|45|R|{2}{R}{R}|Creature - Dragon|4|4|Flying$When Town-Razer Tyrant enters the battlefield, target nonbasic land you don't control loses all abilities except mana abilities and gains "At the beginning of your end step, this permanent deals 2 damage to you unless you sacrifice it."| +Antique Collector|Alchemy: Innistrad|46|R|{1}{G}|Creature - Human Rogue|2|2|Antique Collector can't be blocked by creatures with power 2 or less.$When Antique Collector enters the battlefield, creatures you control perpetually gain "When this creature dies, you may shuffle it into its owner's library if its in your graveyard. If you do, investigate."| +Garruk, Wrath of the Wilds|Alchemy: Innistrad|47|M|{2}{G}{G}|Legendary Planeswalker - Garruk|3|+1: Choose a creature card in your hand. It perpetually gets +1/+1 and perpetually gains "This spell costs {1} less to cast."$−1: Draft a card from Garruk, Wrath of the Wilds's spellbook and put it onto the battlefield.$−5: Until end of turn, creatures you control get +3/+3 and gain trample.| +Geistpack Alpha|Alchemy: Innistrad|48|R|{3}{G}|Creature - Wolf|5|4|Trample$When Geistpack Alpha dies, seek a permanent card with mana value equal to the number of lands you control.| +Grizzled Huntmaster|Alchemy: Innistrad|49|R|{1}{G}{G}|Creature - Human Warrior|4|3|When Grizzled Huntmaster enters the battlefield, you may exile a creature card from your hand. If you do, search your hand and library for any number of cards with the same name, exile them, then shuffle. Choose a creature card you own from outside the game. Conjure a duplicate of that card into your hand for each card exiled from your hand this way.| +Hinterland Chef|Alchemy: Innistrad|50|R|{2}{G}|Creature - Human Scout|3|3|When Hinterland Chef enters the battlefield, draft a card from Hinterland Chef's spellbook. It perpetually becomes a Food artifact in addition to its other types and perpetually gains "{2}, {T}, Sacrifice this creature: You gain 3 life."| +Hollowhenge Wrangler|Alchemy: Innistrad|51|R|{3}{G}{G}|Creature - Elemental|6|6|When Hollowhenge Wrangler enters the battlefield, seek a land card.$Discard a land card: Conjure a card named Hollowhenge Beast into your hand. You may also activate this ability while Hollowhenge Wrangler is in your graveyard.| +Ishkanah, Broodmother|Alchemy: Innistrad|52|M|{3}{G}|Legendary Creature - Spider|3|5|Reach$Other Spiders you control get +1/+2.${1}{B/G}, Exile two cards from your graveyard: Draft a card from Ishkanah, Broodmother's spellbook.| +Lupine Harbingers|Alchemy: Innistrad|53|R|{3}{G}|Creature - Wolf|4|4|Trample, haste$Lupine Harbingers enters the battlefield with X +1/+1 counters on it, where X is the number of turns you've begun since it was foretold.$Foretell {4}{G}{G}| +Ravenous Pursuit|Alchemy: Innistrad|54|R|{1}{G}|Sorcery|||Target creature you control deals damage equal to its power to target creature you don't control. Choose a creature card in your hand. It perpetually gets +X/+X, where X is the amount of excess damage dealt this way.| +Settle the Wilds|Alchemy: Innistrad|55|U|{1}{G}{G}|Sorcery|||Seek a basic land card and put it onto the battlefield tapped. Then seek a permanent card with mana value equal to the number of lands you control.| +Tenacious Pup|Alchemy: Innistrad|56|U|{G}|Creature - Wolf|1|2|When Tenacious Pup enters the battlefield, you gain 1 life. When you cast your next creature spell, that creature enters the battlefield with an additional +1/+1 counter, trample counter, and vigilance counter on it.| +Begin Anew|Alchemy: Innistrad|57|R|{G}{G}{W}{W}|Sorcery|||Destroy all creatures. Creature cards in your hand perpetually get +1/+1.| +Gitrog, Horror of Zhava|Alchemy: Innistrad|58|R|{2}{B}{G}|Legendary Creature - Frog Horror|6|6|Menace$At the beginning of each combat, if Gitrog, Horror of Zhava is untapped, any opponent may sacrifice a nontoken creature. If they do, tap Gitrog, Horror of Zhava, then seek a land card and put it onto the battlefield tapped.$Whenever a land enters the battlefield under your control, it perpetually gains "{B}{G}, {T}, Sacrifice this land: Draw a card."| +Key to the Archive|Alchemy: Innistrad|59|R|{4}|Artifact|||Key to the Archive enters the battlefield tapped.$When Key to the Archive enters the battlefield, draft a card from Key to the Archive's spellbook, then discard a card.${T}: Add two mana in any combination of colors.| +Soulstealer Axe|Alchemy: Innistrad|60|U|{1}|Artifact - Equipment|||Equipped creature has trample.$Whenever equipped creature deals combat damage to a player, seek a card with mana value equal to that damage.$Equip {2}| +Wickerwing Effigy|Alchemy: Innistrad|61|R|{3}|Artifact Creature - Scarecrow|1|4|Defender$You may look at the top card of your library any time.$You may cast creature spells from the top of your library.$Whenever you cast a creature spell from your library, it becomes a black Bird in addition to its other colors and types, has flying, and has base power and toughness 1/1.| +Ominous Traveler|Alchemy: Innistrad|62|R|{2}|Creature - Human|1|1|When Ominous Traveler enters the battlefield, draft a card from Ominous Traveler's spellbook. That card perpetually gains "You may spend mana as though it were mana of any color to cast this spell" and "When you cast this spell, return a card named Ominous Traveler you control to its owner's hand."| +Forsaken Crossroads|Alchemy: Innistrad|63|R||Land|||Forsaken Crossroads enters the battlefield tapped.$As Forsaken Crossroads enters the battlefield, choose a color.$When Forsaken Crossroads enters the battlefield, scry 1. If you weren't the starting player, you may untap Forsaken Crossroads instead.${T}: Add one mana of the chosen color.| +Brokers Ascendancy|Streets of New Capenna|170|R|{G}{U}{W}|Enchantment|||At the beginning of your end step, put a +1/+1 counter on each creature you control and a loyalty counter on each planeswalker you control.| +Jetmir's Garden|Streets of New Capenna|250|R||Land - Mountain Forest Plains|||({T}: Add {R}, {G}, or {W}.)$Jetmir's Garden enters the battlefield tapped.$Cycling {3}| +Raffine's Tower|Streets of New Capenna|254|R||Land - Plains Island Swamp|||({T}: Add {W}, {U}, or {B}.)$Raffine's Tower enters the battlefield tapped.$Cycling {3}| +Spara's Headquarters|Streets of New Capenna|257|R||Land - Forest Plains Island|||({T}: Add {G}, {W}, or {U}.)$Spara's Headquarters enters the battlefield tapped.$Cycling {3}| +Xander's Lounge|Streets of New Capenna|260|R||Land - Island Swamp Mountain|||({T}: Add {U}, {B}, or {R}.)$Xander's Lounge enters the battlefield tapped.$Cycling {3}| +Ziatora's Proving Ground|Streets of New Capenna|261|R||Land - Swamp Mountain Forest|||({T}: Add {B}, {R}, or {G}.)$Ziatora's Proving Ground enters the battlefield tapped.$Cycling {3}| +Plains|Streets of New Capenna|272|C||Basic Land - Plains|||({T}: Add {W}.)| +Island|Streets of New Capenna|274|C||Basic Land - Island|||({T}: Add {U}.)| +Swamp|Streets of New Capenna|276|C||Basic Land - Swamp|||({T}: Add {B}.)| +Mountain|Streets of New Capenna|278|C||Basic Land - Mountain|||({T}: Add {R}.)| +Forest|Streets of New Capenna|280|C||Basic Land - Forest|||({T}: Add {G}.)| diff --git a/Utils/mtg-sets-data.txt b/Utils/mtg-sets-data.txt index af54b9c9282..334f6795ae0 100644 --- a/Utils/mtg-sets-data.txt +++ b/Utils/mtg-sets-data.txt @@ -8,6 +8,7 @@ Classic Sixth Edition|6ED| Seventh Edition|7ED| Eighth Edition|8ED| Ninth Edition|9ED| +Alchemy: Innistrad|Y22| Adventures in the Forgotten Realms|AFR| Forgotten Realms Commander|AFC| Aether Revolt|AER| @@ -117,6 +118,8 @@ Invasion|INV| Innistrad|ISD| Innistrad: Midnight Hunt|MID| Midnight Hunt Commander|MIC| +Innistrad: Crimson Vow|VOW| +Crimson Vow Commander|VOC| Journey into Nyx|JOU| Judge Promo|JR| Judgment|JUD| @@ -125,6 +128,8 @@ Jumpstart: Historic Horizons|J21| Kaladesh|KLD| Kaldheim|KHM| Kaldheim Commander|KHC| +Kamigawa: Neon Dynasty|NEO| +Neon Dynasty Commander|NEC| Khans of Tarkir|KTK| Limited Edition Alpha|LEA| Limited Edition Beta|LEB| @@ -195,6 +200,7 @@ Shadowmoor|SHM| Shadows over Innistrad|SOI| Saviors of Kamigawa|SOK| Scars of Mirrodin|SOM| +Streets of New Capenna|SNC| Stronghold|STH| Super Series|SUS| Theros|THS| @@ -210,6 +216,7 @@ Ugin's Fate|UGIN| Unglued|UGL| Urza's Legacy|ULG| Unhinged|UNH| +Unfinity|UNF| Urza's Saga|USG| Unstable|UST| HASCON Promo 2017|H17| diff --git a/pom.xml b/pom.xml index fd23b52482b..aca8ad9c5c1 100644 --- a/pom.xml +++ b/pom.xml @@ -184,7 +184,7 @@ com.j256.ormlite ormlite-jdbc - 5.6 + 5.7